Pico/plugins/00-PicoDeprecated.php
Daniel Rudolf c0d30d20f8 Follow-up to c72ea0e
Don't use Pico::getAbsolutePath() for $config['content_dir'], just make sure the trailing slash exists. The config.php in Picos root dir should be interpreted exactly like in Pico 0.9 (the option didn't exist in Pico 0.8), thus the path isn't necessarily relative to Picos root dir
2015-11-14 16:50:32 +01:00

439 lines
15 KiB
PHP

<?php
/**
* Serve features of Pico deprecated since v1.0
*
* This plugin exists for backward compatibility and is disabled by default.
* It gets automatically enabled when a plugin which doesn't implement
* {@link PicoPluginInterface} is loaded. This plugin triggers deprecated
* events and automatically enables {@link PicoParsePagesContent} and
* {@link PicoExcerpt}. These plugins heavily impact Pico's performance! You
* can disable this plugin by calling {@link PicoDeprecated::setEnabled()}.
*
* The following deprecated events are triggered by this plugin:
* <pre>
* +---------------------+-----------------------------------------------------------+
* | Event | ... triggers the deprecated event |
* +---------------------+-----------------------------------------------------------+
* | onPluginsLoaded | plugins_loaded() |
* | onConfigLoaded | config_loaded($config) |
* | onRequestUrl | request_url($url) |
* | onContentLoading | before_load_content($file) |
* | onContentLoaded | after_load_content($file, $rawContent) |
* | on404ContentLoading | before_404_load_content($file) |
* | on404ContentLoaded | after_404_load_content($file, $rawContent) |
* | onMetaHeaders | before_read_file_meta($headers) |
* | onMetaParsed | file_meta($meta) |
* | onContentParsing | before_parse_content($rawContent) |
* | onContentParsed | after_parse_content($content) |
* | onContentParsed | content_parsed($content) |
* | onSinglePageLoaded | get_page_data($pages, $meta) |
* | onPagesLoaded | get_pages($pages, $currentPage, $previousPage, $nextPage) |
* | onTwigRegistration | before_twig_register() |
* | onPageRendering | before_render($twigVariables, $twig, $templateName) |
* | onPageRendered | after_render($output) |
* +---------------------+-----------------------------------------------------------+
* </pre>
*
* Since Pico 1.0 the config is stored in {@path "config/config.php"}. This
* plugin tries to read {@path "config.php"} in Pico's root dir and overwrites
* all settings previously specified in {@path "config/config.php"}.
*
* @author Daniel Rudolf
* @link http://picocms.org
* @license http://opensource.org/licenses/MIT
* @version 1.0
*/
class PicoDeprecated extends AbstractPicoPlugin
{
/**
* This plugin is disabled by default
*
* @see AbstractPicoPlugin::$enabled
*/
protected $enabled = false;
/**
* The requested file
*
* @see PicoDeprecated::getRequestFile()
* @var string|null
*/
protected $requestFile;
/**
* Enables this plugin on demand and triggers the deprecated event
* plugins_loaded()
*
* @see DummyPlugin::onPluginsLoaded()
*/
public function onPluginsLoaded(&$plugins)
{
if (!empty($plugins)) {
foreach ($plugins as $plugin) {
if (!is_a($plugin, 'PicoPluginInterface')) {
// the plugin doesn't implement PicoPluginInterface; it uses deprecated events
// enable PicoDeprecated if it hasn't be explicitly enabled/disabled yet
if (!$this->isStatusChanged()) {
$this->setEnabled(true, true, true);
}
break;
}
}
} else {
// no plugins were found, so it actually isn't necessary to call deprecated events
// anyway, this plugin also ensures compatibility apart from events used by old plugins,
// so enable PicoDeprecated if it hasn't be explicitly enabled/disabled yet
if (!$this->isStatusChanged()) {
$this->setEnabled(true, true, true);
}
}
if ($this->isEnabled()) {
$this->triggerEvent('plugins_loaded');
}
}
/**
* Triggers the deprecated event config_loaded($config)
*
* This method also defines deprecated constants, reads the `config.php`
* in Pico's root dir, enables the plugins {@link PicoParsePagesContent}
* and {@link PicoExcerpt} and makes `$config` globally accessible (the
* latter was removed with Pico 0.9 and was added again as deprecated
* feature with Pico 1.0)
*
* @see PicoDeprecated::defineConstants()
* @see PicoDeprecated::loadRootDirConfig()
* @see PicoDeprecated::enablePlugins()
* @see DummyPlugin::onConfigLoaded()
* @param mixed[] &$realConfig array of config variables
* @return void
*/
public function onConfigLoaded(&$realConfig)
{
global $config;
$this->defineConstants();
$this->loadRootDirConfig($realConfig);
$this->enablePlugins();
$config = &$realConfig;
$this->triggerEvent('config_loaded', array(&$realConfig));
}
/**
* Defines deprecated constants
*
* `ROOT_DIR`, `LIB_DIR`, `PLUGINS_DIR`, `THEMES_DIR` and `CONTENT_EXT`
* are deprecated since v1.0, `CONTENT_DIR` existed just in v0.9,
* `CONFIG_DIR` just for a short time between v0.9 and v1.0 and
* `CACHE_DIR` was dropped with v1.0 without a replacement.
*
* @see PicoDeprecated::onConfigLoaded()
* @return void
*/
protected function defineConstants()
{
if (!defined('ROOT_DIR')) {
define('ROOT_DIR', $this->getRootDir());
}
if (!defined('CONFIG_DIR')) {
define('CONFIG_DIR', $this->getConfigDir());
}
if (!defined('LIB_DIR')) {
$picoReflector = new ReflectionClass('Pico');
define('LIB_DIR', dirname($picoReflector->getFileName() . '/'));
}
if (!defined('PLUGINS_DIR')) {
define('PLUGINS_DIR', $this->getPluginsDir());
}
if (!defined('THEMES_DIR')) {
define('THEMES_DIR', $this->getThemesDir());
}
if (!defined('CONTENT_DIR')) {
define('CONTENT_DIR', $this->getConfig('content_dir'));
}
if (!defined('CONTENT_EXT')) {
define('CONTENT_EXT', $this->getConfig('content_ext'));
}
}
/**
* Read config.php in Pico's root dir
*
* @see PicoDeprecated::onConfigLoaded()
* @see Pico::loadConfig()
* @param mixed[] &$realConfig array of config variables
* @return void
*/
protected function loadRootDirConfig(&$realConfig)
{
if (file_exists($this->getRootDir() . 'config.php')) {
// config.php in Pico::$rootDir is deprecated
// use config.php in Pico::$configDir instead
$config = null;
require($this->getRootDir() . 'config.php');
if (is_array($config)) {
if (isset($config['base_url'])) {
$config['base_url'] = rtrim($config['base_url'], '/') . '/';
}
if (isset($config['content_dir'])) {
$config['content_dir'] = rtrim($config['content_dir'], '/') . '/';
}
$realConfig = $config + $realConfig;
}
}
}
/**
* Enables the plugins PicoParsePagesContent and PicoExcerpt
*
* @see PicoParsePagesContent
* @see PicoExcerpt
* @return void
*/
protected function enablePlugins()
{
// enable PicoParsePagesContent and PicoExcerpt
// we can't enable them during onPluginsLoaded because we can't know
// if the user disabled us (PicoDeprecated) manually in the config
$plugins = $this->getPlugins();
if (isset($plugins['PicoParsePagesContent'])) {
// parse all pages content if this plugin hasn't
// be explicitly enabled/disabled yet
if (!$plugins['PicoParsePagesContent']->isStatusChanged()) {
$plugins['PicoParsePagesContent']->setEnabled(true, true, true);
}
}
if (isset($plugins['PicoExcerpt'])) {
// enable excerpt plugin if it hasn't be explicitly enabled/disabled yet
if (!$plugins['PicoExcerpt']->isStatusChanged()) {
$plugins['PicoExcerpt']->setEnabled(true, true, true);
}
}
}
/**
* Triggers the deprecated event request_url($url)
*
* @see DummyPlugin::onRequestUrl()
*/
public function onRequestUrl(&$url)
{
$this->triggerEvent('request_url', array(&$url));
}
/**
* Sets PicoDeprecated::$requestFile to trigger the deprecated
* events after_load_content() and after_404_load_content()
*
* @see PicoDeprecated::onContentLoaded()
* @see PicoDeprecated::on404ContentLoaded()
* @see DummyPlugin::onRequestFile()
*/
public function onRequestFile(&$file)
{
$this->requestFile = &$file;
}
/**
* Triggers the deprecated before_load_content($file)
*
* @see DummyPlugin::onContentLoading()
*/
public function onContentLoading(&$file)
{
$this->triggerEvent('before_load_content', array(&$file));
}
/**
* Triggers the deprecated event after_load_content($file, $rawContent)
*
* @see DummyPlugin::onContentLoaded()
*/
public function onContentLoaded(&$rawContent)
{
$this->triggerEvent('after_load_content', array(&$this->requestFile, &$rawContent));
}
/**
* Triggers the deprecated before_404_load_content($file)
*
* @see DummyPlugin::on404ContentLoading()
*/
public function on404ContentLoading(&$file)
{
$this->triggerEvent('before_404_load_content', array(&$file));
}
/**
* Triggers the deprecated event after_404_load_content($file, $rawContent)
*
* @see DummyPlugin::on404ContentLoaded()
*/
public function on404ContentLoaded(&$rawContent)
{
$this->triggerEvent('after_404_load_content', array(&$this->requestFile, &$rawContent));
}
/**
* Triggers the deprecated event before_read_file_meta($headers)
*
* @see DummyPlugin::onMetaHeaders()
*/
public function onMetaHeaders(&$headers)
{
$this->triggerEvent('before_read_file_meta', array(&$headers));
}
/**
* Triggers the deprecated event file_meta($meta)
*
* @see DummyPlugin::onMetaParsed()
*/
public function onMetaParsed(&$meta)
{
$this->triggerEvent('file_meta', array(&$meta));
}
/**
* Triggers the deprecated event before_parse_content($rawContent)
*
* @see DummyPlugin::onContentParsing()
*/
public function onContentParsing(&$rawContent)
{
$this->triggerEvent('before_parse_content', array(&$rawContent));
}
/**
* Triggers the deprecated events after_parse_content($content) and
* content_parsed($content)
*
* @see DummyPlugin::onContentParsed()
*/
public function onContentParsed(&$content)
{
$this->triggerEvent('after_parse_content', array(&$content));
// deprecated since v0.8
$this->triggerEvent('content_parsed', array(&$content));
}
/**
* Triggers the deprecated event get_page_data($pages, $meta)
*
* @see DummyPlugin::onSinglePageLoaded()
*/
public function onSinglePageLoaded(&$pageData)
{
$this->triggerEvent('get_page_data', array(&$pageData, $pageData['meta']));
}
/**
* Triggers the deprecated event
* get_pages($pages, $currentPage, $previousPage, $nextPage)
*
* Please note that the `get_pages()` event gets `$pages` passed without a
* array index. The index is rebuild later using either the `id` array key
* or is derived from the `url` array key. Duplicates are prevented by
* adding `~dup` when necessary.
*
* @see DummyPlugin::onPagesLoaded()
*/
public function onPagesLoaded(&$pages, &$currentPage, &$previousPage, &$nextPage)
{
// remove keys of pages array
$plainPages = array();
foreach ($pages as &$pageData) {
$plainPages[] = &$pageData;
}
unset($pageData);
$this->triggerEvent('get_pages', array(&$plainPages, &$currentPage, &$previousPage, &$nextPage));
// re-index pages array
$pages = array();
foreach ($plainPages as &$pageData) {
if (!isset($pageData['id'])) {
$urlPrefixLength = strlen($this->getBaseUrl()) + intval(!$this->isUrlRewritingEnabled());
$pageData['id'] = substr($pageData['url'], $urlPrefixLength);
}
// prevent duplicates
$id = $pageData['id'];
for ($i = 1; isset($pages[$id]); $i++) {
$id = $pageData['id'] . '~dup' . $i;
}
$pages[$id] = &$pageData;
}
}
/**
* Triggers the deprecated event before_twig_register()
*
* @see DummyPlugin::onTwigRegistration()
*/
public function onTwigRegistration()
{
$this->triggerEvent('before_twig_register');
}
/**
* Triggers the deprecated event before_render($twigVariables, $twig, $templateName)
*
* Please note that the `before_render()` event gets `$templateName` passed
* without its file extension. The file extension is later added again.
*
* @see DummyPlugin::onPageRendering()
*/
public function onPageRendering(&$twig, &$twigVariables, &$templateName)
{
// template name contains file extension since Pico 1.0
$fileExtension = '';
if (($fileExtensionPos = strrpos($templateName, '.')) !== false) {
$fileExtension = substr($templateName, $fileExtensionPos);
$templateName = substr($templateName, 0, $fileExtensionPos);
}
$this->triggerEvent('before_render', array(&$twigVariables, &$twig, &$templateName));
// add original file extension
$templateName = $templateName . $fileExtension;
}
/**
* Triggers the deprecated event after_render($output)
*
* @see DummyPlugin::onPageRendered()
*/
public function onPageRendered(&$output)
{
$this->triggerEvent('after_render', array(&$output));
}
/**
* Triggers a deprecated event on all plugins
*
* Deprecated events are also triggered on plugins which implement
* {@link PicoPluginInterface}. Please note that the methods are called
* directly and not through {@link PicoPluginInterface::handleEvent()}.
*
* @param string $eventName event to trigger
* @param array $params parameters to pass
* @return void
*/
protected function triggerEvent($eventName, array $params = array())
{
foreach ($this->getPlugins() as $plugin) {
if (method_exists($plugin, $eventName)) {
call_user_func_array(array($plugin, $eventName), $params);
}
}
}
}