🎉 Add Pico theme API versioning and add pico-theme.yml
- Add pico-theme.yml with a theme's API version, theme-specific default Twig config, registering theme-specific custom meta headers and defaults for Pico's `theme_config` config - Add new `onThemeLoading(&$theme)` and `onThemeLoaded($theme, $themeApiVersion, &$themeConfig)` events - Enable Twig autoescaping by default
This commit is contained in:
parent
c1113a780c
commit
b27b4f388a
@ -17,8 +17,9 @@ themes_url: ~ # Pico will try to guess the URL to the them
|
|||||||
theme_config: # Additional theme-specific config
|
theme_config: # Additional theme-specific config
|
||||||
widescreen: false # Default theme: Use more horicontal space (i.e. make the site container wider)
|
widescreen: false # Default theme: Use more horicontal space (i.e. make the site container wider)
|
||||||
twig_config: # Twig template engine config
|
twig_config: # Twig template engine config
|
||||||
autoescape: false # Let Twig escape variables by default
|
autoescape: html # Let Twig escape variables by default
|
||||||
strict_variables: false # If set to true, Twig will bail out when unset variables are being used
|
strict_variables: false # If set to true, Twig will bail out when unset variables are being used
|
||||||
|
charset: utf-8 # The charset used by Twig templates
|
||||||
debug: ~ # Enable Twig's debug mode
|
debug: ~ # Enable Twig's debug mode
|
||||||
cache: false # Enable Twig template caching by specifying a path to a writable directory
|
cache: false # Enable Twig template caching by specifying a path to a writable directory
|
||||||
auto_reload: ~ # Recompile Twig templates whenever the source code changes
|
auto_reload: ~ # Recompile Twig templates whenever the source code changes
|
||||||
|
173
lib/Pico.php
173
lib/Pico.php
@ -168,6 +168,29 @@ class Pico
|
|||||||
*/
|
*/
|
||||||
protected $config;
|
protected $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theme in use
|
||||||
|
*
|
||||||
|
* @see Pico::getTheme()
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $theme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API version of the current theme
|
||||||
|
*
|
||||||
|
* @see Pico::getThemeApiVersion()
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $themeApiVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional meta headers of the current theme
|
||||||
|
*
|
||||||
|
* @var array<string,string>|null
|
||||||
|
*/
|
||||||
|
protected $themeMetaHeaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of the URL describing the requested contents
|
* Part of the URL describing the requested contents
|
||||||
*
|
*
|
||||||
@ -411,6 +434,16 @@ class Pico
|
|||||||
throw new RuntimeException('Invalid content directory "' . $this->getConfig('content_dir') . '"');
|
throw new RuntimeException('Invalid content directory "' . $this->getConfig('content_dir') . '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load theme
|
||||||
|
$this->theme = $this->config['theme'];
|
||||||
|
$this->triggerEvent('onThemeLoading', array(&$this->theme));
|
||||||
|
|
||||||
|
$this->loadTheme();
|
||||||
|
$this->triggerEvent(
|
||||||
|
'onThemeLoaded',
|
||||||
|
array($this->theme, $this->themeApiVersion, &$this->config['theme_config'])
|
||||||
|
);
|
||||||
|
|
||||||
// evaluate request url
|
// evaluate request url
|
||||||
$this->evaluateRequestUrl();
|
$this->evaluateRequestUrl();
|
||||||
$this->triggerEvent('onRequestUrl', array(&$this->requestUrl));
|
$this->triggerEvent('onRequestUrl', array(&$this->requestUrl));
|
||||||
@ -903,6 +936,8 @@ class Pico
|
|||||||
'debug' => null,
|
'debug' => null,
|
||||||
'timezone' => null,
|
'timezone' => null,
|
||||||
'theme' => 'default',
|
'theme' => 'default',
|
||||||
|
'theme_config' => null,
|
||||||
|
'theme_meta' => null,
|
||||||
'themes_url' => null,
|
'themes_url' => null,
|
||||||
'twig_config' => null,
|
'twig_config' => null,
|
||||||
'date_format' => '%D %T',
|
'date_format' => '%D %T',
|
||||||
@ -950,27 +985,6 @@ class Pico
|
|||||||
$this->config['themes_url'] = $this->getAbsoluteUrl($this->config['themes_url']);
|
$this->config['themes_url'] = $this->getAbsoluteUrl($this->config['themes_url']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaultTwigConfig = array(
|
|
||||||
'autoescape' => false,
|
|
||||||
'strict_variables' => false,
|
|
||||||
'debug' => null,
|
|
||||||
'cache' => false,
|
|
||||||
'auto_reload' => null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!is_array($this->config['twig_config'])) {
|
|
||||||
$this->config['twig_config'] = $defaultTwigConfig;
|
|
||||||
} else {
|
|
||||||
$this->config['twig_config'] += $defaultTwigConfig;
|
|
||||||
|
|
||||||
if ($this->config['twig_config']['cache']) {
|
|
||||||
$this->config['twig_config']['cache'] = $this->getAbsolutePath($this->config['twig_config']['cache']);
|
|
||||||
}
|
|
||||||
if ($this->config['twig_config']['debug'] === null) {
|
|
||||||
$this->config['twig_config']['debug'] = $this->isDebugModeEnabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->config['content_dir']) {
|
if (!$this->config['content_dir']) {
|
||||||
// try to guess the content directory
|
// try to guess the content directory
|
||||||
if (is_file($this->getRootDir() . 'content/index' . $this->config['content_ext'])) {
|
if (is_file($this->getRootDir() . 'content/index' . $this->config['content_ext'])) {
|
||||||
@ -1056,6 +1070,113 @@ class Pico
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a theme's config file (pico-theme.yml)
|
||||||
|
*
|
||||||
|
* @see Pico::getTheme()
|
||||||
|
* @see Pico::getThemeApiVersion()
|
||||||
|
*/
|
||||||
|
protected function loadTheme()
|
||||||
|
{
|
||||||
|
$themeConfig = array();
|
||||||
|
|
||||||
|
// load theme config from pico-theme.yml
|
||||||
|
$themeConfigFile = $this->getThemesDir() . $this->getTheme() . '/pico-theme.yml';
|
||||||
|
if (is_file($themeConfigFile)) {
|
||||||
|
$yamlParser = $this->getYamlParser();
|
||||||
|
$loadConfigClosure = function ($configFile) use ($yamlParser) {
|
||||||
|
$yaml = file_get_contents($configFile);
|
||||||
|
$config = $yamlParser->parse($yaml);
|
||||||
|
return is_array($config) ? $config : array();
|
||||||
|
};
|
||||||
|
|
||||||
|
$themeConfig = $loadConfigClosure($themeConfigFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$themeConfig += array(
|
||||||
|
'api_version' => null,
|
||||||
|
'meta' => array(),
|
||||||
|
'twig_config' => array()
|
||||||
|
);
|
||||||
|
|
||||||
|
// theme API version
|
||||||
|
if (preg_match('/^[0-9]+$/', $themeConfig['api_version'])) {
|
||||||
|
$this->themeApiVersion = (int) $themeConfig['api_version'];
|
||||||
|
} else {
|
||||||
|
$this->themeApiVersion = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($themeConfig['api_version']);
|
||||||
|
|
||||||
|
// twig config
|
||||||
|
$themeTwigConfig = array('autoescape' => 'html', 'strict_variables' => false, 'charset' => 'utf-8');
|
||||||
|
foreach ($themeTwigConfig as $key => $_) {
|
||||||
|
if (isset($themeConfig['twig_config'][$key])) {
|
||||||
|
$themeTwigConfig[$key] = $themeConfig['twig_config'][$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($themeConfig['twig_config']);
|
||||||
|
|
||||||
|
$defaultTwigConfig = array('debug' => null, 'cache' => false, 'auto_reload' => null);
|
||||||
|
$this->config['twig_config'] = array_merge($defaultTwigConfig, $themeTwigConfig, $this->config['twig_config']);
|
||||||
|
|
||||||
|
if ($this->config['twig_config']['autoescape'] === true) {
|
||||||
|
$this->config['twig_config']['autoescape'] = 'html';
|
||||||
|
}
|
||||||
|
if ($this->config['twig_config']['cache']) {
|
||||||
|
$this->config['twig_config']['cache'] = $this->getAbsolutePath($this->config['twig_config']['cache']);
|
||||||
|
}
|
||||||
|
if ($this->config['twig_config']['debug'] === null) {
|
||||||
|
$this->config['twig_config']['debug'] = $this->isDebugModeEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
// meta headers
|
||||||
|
$this->themeMetaHeaders = is_array($themeConfig['meta']) ? $themeConfig['meta'] : array();
|
||||||
|
unset($themeConfig['meta']);
|
||||||
|
|
||||||
|
// theme config
|
||||||
|
if (!is_array($this->config['theme_config'])) {
|
||||||
|
$this->config['theme_config'] = $themeConfig;
|
||||||
|
} else {
|
||||||
|
$this->config['theme_config'] += $themeConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for theme compatibility
|
||||||
|
if (!isset($this->plugins['PicoDeprecated']) && ($this->themeApiVersion < static::API_VERSION)) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
'Current theme "' . $this->theme . '" uses API version ' . $this->themeApiVersion . ', but Pico '
|
||||||
|
. 'provides API version ' . static::API_VERSION . ' and PicoDeprecated isn\'t loaded'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the current theme
|
||||||
|
*
|
||||||
|
* @see Pico::loadTheme()
|
||||||
|
* @see Pico::getThemeApiVersion()
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTheme()
|
||||||
|
{
|
||||||
|
return $this->theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the API version of the current theme
|
||||||
|
*
|
||||||
|
* @see Pico::loadTheme()
|
||||||
|
* @see Pico::getTheme()
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getThemeApiVersion()
|
||||||
|
{
|
||||||
|
return $this->themeApiVersion;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates the requested URL
|
* Evaluates the requested URL
|
||||||
*
|
*
|
||||||
@ -1305,6 +1426,10 @@ class Pico
|
|||||||
'Hidden' => 'hidden'
|
'Hidden' => 'hidden'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($this->themeMetaHeaders) {
|
||||||
|
$this->metaHeaders += $this->themeMetaHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
$this->triggerEvent('onMetaHeaders', array(&$this->metaHeaders));
|
$this->triggerEvent('onMetaHeaders', array(&$this->metaHeaders));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1501,7 +1626,7 @@ class Pico
|
|||||||
$variables['%assets_url%'] = rtrim($this->getConfig('assets_url'), '/');
|
$variables['%assets_url%'] = rtrim($this->getConfig('assets_url'), '/');
|
||||||
|
|
||||||
// replace %theme_url%
|
// replace %theme_url%
|
||||||
$variables['%theme_url%'] = $this->getConfig('themes_url') . $this->getConfig('theme');
|
$variables['%theme_url%'] = $this->getConfig('themes_url') . $this->getTheme();
|
||||||
|
|
||||||
// replace %meta.*%
|
// replace %meta.*%
|
||||||
if ($meta) {
|
if ($meta) {
|
||||||
@ -1964,7 +2089,7 @@ class Pico
|
|||||||
if ($this->twig === null) {
|
if ($this->twig === null) {
|
||||||
$twigConfig = $this->getConfig('twig_config');
|
$twigConfig = $this->getConfig('twig_config');
|
||||||
|
|
||||||
$twigLoader = new Twig_Loader_Filesystem($this->getThemesDir() . $this->getConfig('theme'));
|
$twigLoader = new Twig_Loader_Filesystem($this->getThemesDir() . $this->getTheme());
|
||||||
$this->twig = new Twig_Environment($twigLoader, $twigConfig);
|
$this->twig = new Twig_Environment($twigLoader, $twigConfig);
|
||||||
$this->twig->addExtension(new PicoTwigExtension($this));
|
$this->twig->addExtension(new PicoTwigExtension($this));
|
||||||
|
|
||||||
@ -2011,7 +2136,7 @@ class Pico
|
|||||||
'plugins_url' => rtrim($this->getConfig('plugins_url'), '/'),
|
'plugins_url' => rtrim($this->getConfig('plugins_url'), '/'),
|
||||||
'themes_url' => rtrim($this->getConfig('themes_url'), '/'),
|
'themes_url' => rtrim($this->getConfig('themes_url'), '/'),
|
||||||
'assets_url' => rtrim($this->getConfig('assets_url'), '/'),
|
'assets_url' => rtrim($this->getConfig('assets_url'), '/'),
|
||||||
'theme_url' => $this->getConfig('themes_url') . $this->getConfig('theme'),
|
'theme_url' => $this->getConfig('themes_url') . $this->getTheme(),
|
||||||
'site_title' => $this->getConfig('site_title'),
|
'site_title' => $this->getConfig('site_title'),
|
||||||
'meta' => $this->meta,
|
'meta' => $this->meta,
|
||||||
'content' => $this->content,
|
'content' => $this->content,
|
||||||
|
@ -113,6 +113,35 @@ class DummyPlugin extends AbstractPicoPlugin
|
|||||||
// your code
|
// your code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered before Pico loads its theme
|
||||||
|
*
|
||||||
|
* @see Pico::loadTheme()
|
||||||
|
* @see DummyPlugin::onThemeLoaded()
|
||||||
|
*
|
||||||
|
* @param string $theme name of current theme
|
||||||
|
*/
|
||||||
|
public function onThemeLoading(&$theme)
|
||||||
|
{
|
||||||
|
// your code
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered after Pico loaded its theme
|
||||||
|
*
|
||||||
|
* @see DummyPlugin::onThemeLoading()
|
||||||
|
* @see Pico::getTheme()
|
||||||
|
* @see Pico::getThemeApiVersion()
|
||||||
|
*
|
||||||
|
* @param string $theme name of current theme
|
||||||
|
* @param int $themeApiVersion API version of the theme
|
||||||
|
* @param array $themeConfig config array of the theme
|
||||||
|
*/
|
||||||
|
public function onThemeLoaded($theme, $themeApiVersion, array &$themeConfig)
|
||||||
|
{
|
||||||
|
// your code
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered after Pico has evaluated the request URL
|
* Triggered after Pico has evaluated the request URL
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user