00-PicoDeprecated.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <?php
  2. /**
  3. * Serve features of Pico deprecated since v1.0
  4. *
  5. * This plugin exists for backward compatibility and is disabled by default.
  6. * It gets automatically enabled when a plugin which doesn't implement
  7. * {@link PicoPluginInterface} is loaded. This plugin triggers deprecated
  8. * events and automatically enables {@link PicoParsePagesContent} and
  9. * {@link PicoExcerpt}. These plugins heavily impact Pico's performance! You
  10. * can disable this plugin by calling {@link PicoDeprecated::setEnabled()}.
  11. *
  12. * The following deprecated events are triggered by this plugin:
  13. *
  14. * | Event | ... triggers the deprecated event |
  15. * | ------------------- | --------------------------------------------------------- |
  16. * | onPluginsLoaded | plugins_loaded() |
  17. * | onConfigLoaded | config_loaded($config) |
  18. * | onRequestUrl | request_url($url) |
  19. * | onContentLoading | before_load_content($file) |
  20. * | onContentLoaded | after_load_content($file, $rawContent) |
  21. * | on404ContentLoading | before_404_load_content($file) |
  22. * | on404ContentLoaded | after_404_load_content($file, $rawContent) |
  23. * | onMetaHeaders | before_read_file_meta($headers) |
  24. * | onMetaParsed | file_meta($meta) |
  25. * | onContentParsing | before_parse_content($rawContent) |
  26. * | onContentParsed | after_parse_content($content) |
  27. * | onContentParsed | content_parsed($content) |
  28. * | onSinglePageLoaded | get_page_data($pages, $meta) |
  29. * | onPagesLoaded | get_pages($pages, $currentPage, $previousPage, $nextPage) |
  30. * | onTwigRegistration | before_twig_register() |
  31. * | onPageRendering | before_render($twigVariables, $twig, $templateName) |
  32. * | onPageRendered | after_render($output) |
  33. *
  34. * Since Pico 1.0 the config is stored in {@path "config/config.php"}. This
  35. * plugin tries to read {@path "config.php"} in Pico's root dir and overwrites
  36. * all settings previously specified in {@path "config/config.php"}.
  37. *
  38. * @author Daniel Rudolf
  39. * @link http://picocms.org
  40. * @license http://opensource.org/licenses/MIT The MIT License
  41. * @version 1.0
  42. */
  43. class PicoDeprecated extends AbstractPicoPlugin
  44. {
  45. /**
  46. * This plugin is disabled by default
  47. *
  48. * @see AbstractPicoPlugin::$enabled
  49. */
  50. protected $enabled = false;
  51. /**
  52. * The requested file
  53. *
  54. * @see PicoDeprecated::getRequestFile()
  55. * @var string|null
  56. */
  57. protected $requestFile;
  58. /**
  59. * Enables this plugin on demand and triggers the deprecated event
  60. * plugins_loaded()
  61. *
  62. * @see DummyPlugin::onPluginsLoaded()
  63. */
  64. public function onPluginsLoaded(array &$plugins)
  65. {
  66. if (!empty($plugins)) {
  67. foreach ($plugins as $plugin) {
  68. if (!($plugin instanceof PicoPluginInterface)) {
  69. // the plugin doesn't implement PicoPluginInterface; it uses deprecated events
  70. // enable PicoDeprecated if it hasn't be explicitly enabled/disabled yet
  71. if (!$this->isStatusChanged()) {
  72. $this->setEnabled(true, true, true);
  73. }
  74. break;
  75. }
  76. }
  77. } else {
  78. // no plugins were found, so it actually isn't necessary to call deprecated events
  79. // anyway, this plugin also ensures compatibility apart from events used by old plugins,
  80. // so enable PicoDeprecated if it hasn't be explicitly enabled/disabled yet
  81. if (!$this->isStatusChanged()) {
  82. $this->setEnabled(true, true, true);
  83. }
  84. }
  85. if ($this->isEnabled()) {
  86. $this->triggerEvent('plugins_loaded');
  87. }
  88. }
  89. /**
  90. * Triggers the deprecated event config_loaded($config)
  91. *
  92. * This method also defines deprecated constants, reads the `config.php`
  93. * in Pico's root dir, enables the plugins {@link PicoParsePagesContent}
  94. * and {@link PicoExcerpt} and makes `$config` globally accessible (the
  95. * latter was removed with Pico 0.9 and was added again as deprecated
  96. * feature with Pico 1.0)
  97. *
  98. * @see PicoDeprecated::defineConstants()
  99. * @see PicoDeprecated::loadRootDirConfig()
  100. * @see PicoDeprecated::enablePlugins()
  101. * @see DummyPlugin::onConfigLoaded()
  102. * @param array &$config array of config variables
  103. * @return void
  104. */
  105. public function onConfigLoaded(array &$config)
  106. {
  107. $this->defineConstants();
  108. $this->loadRootDirConfig($config);
  109. $this->enablePlugins();
  110. $GLOBALS['config'] = &$config;
  111. $this->triggerEvent('config_loaded', array(&$config));
  112. }
  113. /**
  114. * Defines deprecated constants
  115. *
  116. * `ROOT_DIR`, `LIB_DIR`, `PLUGINS_DIR`, `THEMES_DIR` and `CONTENT_EXT`
  117. * are deprecated since v1.0, `CONTENT_DIR` existed just in v0.9,
  118. * `CONFIG_DIR` just for a short time between v0.9 and v1.0 and
  119. * `CACHE_DIR` was dropped with v1.0 without a replacement.
  120. *
  121. * @see PicoDeprecated::onConfigLoaded()
  122. * @return void
  123. */
  124. protected function defineConstants()
  125. {
  126. if (!defined('ROOT_DIR')) {
  127. define('ROOT_DIR', $this->getRootDir());
  128. }
  129. if (!defined('CONFIG_DIR')) {
  130. define('CONFIG_DIR', $this->getConfigDir());
  131. }
  132. if (!defined('LIB_DIR')) {
  133. $picoReflector = new ReflectionClass('Pico');
  134. define('LIB_DIR', dirname($picoReflector->getFileName()) . '/');
  135. }
  136. if (!defined('PLUGINS_DIR')) {
  137. define('PLUGINS_DIR', $this->getPluginsDir());
  138. }
  139. if (!defined('THEMES_DIR')) {
  140. define('THEMES_DIR', $this->getThemesDir());
  141. }
  142. if (!defined('CONTENT_DIR')) {
  143. define('CONTENT_DIR', $this->getConfig('content_dir'));
  144. }
  145. if (!defined('CONTENT_EXT')) {
  146. define('CONTENT_EXT', $this->getConfig('content_ext'));
  147. }
  148. }
  149. /**
  150. * Read config.php in Pico's root dir
  151. *
  152. * @see PicoDeprecated::onConfigLoaded()
  153. * @see Pico::loadConfig()
  154. * @param array &$realConfig array of config variables
  155. * @return void
  156. */
  157. protected function loadRootDirConfig(array &$realConfig)
  158. {
  159. if (file_exists($this->getRootDir() . 'config.php')) {
  160. $config = null;
  161. // scope isolated require()
  162. $includeClosure = function ($configFile) use (&$config) {
  163. require($configFile);
  164. };
  165. if (PHP_VERSION_ID >= 50400) {
  166. $includeClosure = $includeClosure->bindTo(null);
  167. }
  168. // config.php in Pico::$rootDir is deprecated
  169. // use config.php in Pico::$configDir instead
  170. $includeClosure($this->getRootDir() . 'config.php');
  171. if (is_array($config)) {
  172. if (isset($config['base_url'])) {
  173. $config['base_url'] = rtrim($config['base_url'], '/') . '/';
  174. }
  175. if (isset($config['content_dir'])) {
  176. $config['content_dir'] = rtrim($config['content_dir'], '/\\') . '/';
  177. }
  178. $realConfig = $config + $realConfig;
  179. }
  180. }
  181. }
  182. /**
  183. * Enables the plugins PicoParsePagesContent and PicoExcerpt
  184. *
  185. * @see PicoParsePagesContent
  186. * @see PicoExcerpt
  187. * @return void
  188. */
  189. protected function enablePlugins()
  190. {
  191. // enable PicoParsePagesContent and PicoExcerpt
  192. // we can't enable them during onPluginsLoaded because we can't know
  193. // if the user disabled us (PicoDeprecated) manually in the config
  194. $plugins = $this->getPlugins();
  195. if (isset($plugins['PicoParsePagesContent'])) {
  196. // parse all pages content if this plugin hasn't
  197. // be explicitly enabled/disabled yet
  198. if (!$plugins['PicoParsePagesContent']->isStatusChanged()) {
  199. $plugins['PicoParsePagesContent']->setEnabled(true, true, true);
  200. }
  201. }
  202. if (isset($plugins['PicoExcerpt'])) {
  203. // enable excerpt plugin if it hasn't be explicitly enabled/disabled yet
  204. if (!$plugins['PicoExcerpt']->isStatusChanged()) {
  205. $plugins['PicoExcerpt']->setEnabled(true, true, true);
  206. }
  207. }
  208. }
  209. /**
  210. * Triggers the deprecated event request_url($url)
  211. *
  212. * @see DummyPlugin::onRequestUrl()
  213. */
  214. public function onRequestUrl(&$url)
  215. {
  216. $this->triggerEvent('request_url', array(&$url));
  217. }
  218. /**
  219. * Sets PicoDeprecated::$requestFile to trigger the deprecated
  220. * events after_load_content() and after_404_load_content()
  221. *
  222. * @see PicoDeprecated::onContentLoaded()
  223. * @see PicoDeprecated::on404ContentLoaded()
  224. * @see DummyPlugin::onRequestFile()
  225. */
  226. public function onRequestFile(&$file)
  227. {
  228. $this->requestFile = &$file;
  229. }
  230. /**
  231. * Triggers the deprecated before_load_content($file)
  232. *
  233. * @see DummyPlugin::onContentLoading()
  234. */
  235. public function onContentLoading(&$file)
  236. {
  237. $this->triggerEvent('before_load_content', array(&$file));
  238. }
  239. /**
  240. * Triggers the deprecated event after_load_content($file, $rawContent)
  241. *
  242. * @see DummyPlugin::onContentLoaded()
  243. */
  244. public function onContentLoaded(&$rawContent)
  245. {
  246. $this->triggerEvent('after_load_content', array(&$this->requestFile, &$rawContent));
  247. }
  248. /**
  249. * Triggers the deprecated before_404_load_content($file)
  250. *
  251. * @see DummyPlugin::on404ContentLoading()
  252. */
  253. public function on404ContentLoading(&$file)
  254. {
  255. $this->triggerEvent('before_404_load_content', array(&$file));
  256. }
  257. /**
  258. * Triggers the deprecated event after_404_load_content($file, $rawContent)
  259. *
  260. * @see DummyPlugin::on404ContentLoaded()
  261. */
  262. public function on404ContentLoaded(&$rawContent)
  263. {
  264. $this->triggerEvent('after_404_load_content', array(&$this->requestFile, &$rawContent));
  265. }
  266. /**
  267. * Triggers the deprecated event before_read_file_meta($headers)
  268. *
  269. * @see DummyPlugin::onMetaHeaders()
  270. */
  271. public function onMetaHeaders(array &$headers)
  272. {
  273. $this->triggerEvent('before_read_file_meta', array(&$headers));
  274. }
  275. /**
  276. * Triggers the deprecated event file_meta($meta)
  277. *
  278. * @see DummyPlugin::onMetaParsed()
  279. */
  280. public function onMetaParsed(array &$meta)
  281. {
  282. $this->triggerEvent('file_meta', array(&$meta));
  283. }
  284. /**
  285. * Triggers the deprecated event before_parse_content($rawContent)
  286. *
  287. * @see DummyPlugin::onContentParsing()
  288. */
  289. public function onContentParsing(&$rawContent)
  290. {
  291. $this->triggerEvent('before_parse_content', array(&$rawContent));
  292. }
  293. /**
  294. * Triggers the deprecated events after_parse_content($content) and
  295. * content_parsed($content)
  296. *
  297. * @see DummyPlugin::onContentParsed()
  298. */
  299. public function onContentParsed(&$content)
  300. {
  301. $this->triggerEvent('after_parse_content', array(&$content));
  302. // deprecated since v0.8
  303. $this->triggerEvent('content_parsed', array(&$content));
  304. }
  305. /**
  306. * Triggers the deprecated event get_page_data($pages, $meta)
  307. *
  308. * @see DummyPlugin::onSinglePageLoaded()
  309. */
  310. public function onSinglePageLoaded(array &$pageData)
  311. {
  312. $this->triggerEvent('get_page_data', array(&$pageData, $pageData['meta']));
  313. }
  314. /**
  315. * Triggers the deprecated event
  316. * get_pages($pages, $currentPage, $previousPage, $nextPage)
  317. *
  318. * Please note that the `get_pages()` event gets `$pages` passed without a
  319. * array index. The index is rebuild later using either the `id` array key
  320. * or is derived from the `url` array key. Duplicates are prevented by
  321. * adding `~dup` when necessary.
  322. *
  323. * @see DummyPlugin::onPagesLoaded()
  324. */
  325. public function onPagesLoaded(
  326. array &$pages,
  327. array &$currentPage = null,
  328. array &$previousPage = null,
  329. array &$nextPage = null
  330. ) {
  331. // remove keys of pages array
  332. $plainPages = array();
  333. foreach ($pages as &$pageData) {
  334. $plainPages[] = &$pageData;
  335. }
  336. unset($pageData);
  337. $this->triggerEvent('get_pages', array(&$plainPages, &$currentPage, &$previousPage, &$nextPage));
  338. // re-index pages array
  339. $pages = array();
  340. foreach ($plainPages as &$pageData) {
  341. if (!isset($pageData['id'])) {
  342. $urlPrefixLength = strlen($this->getBaseUrl()) + intval(!$this->isUrlRewritingEnabled());
  343. $pageData['id'] = substr($pageData['url'], $urlPrefixLength);
  344. }
  345. // prevent duplicates
  346. $id = $pageData['id'];
  347. for ($i = 1; isset($pages[$id]); $i++) {
  348. $id = $pageData['id'] . '~dup' . $i;
  349. }
  350. $pages[$id] = &$pageData;
  351. }
  352. }
  353. /**
  354. * Triggers the deprecated event before_twig_register()
  355. *
  356. * @see DummyPlugin::onTwigRegistration()
  357. */
  358. public function onTwigRegistration()
  359. {
  360. $this->triggerEvent('before_twig_register');
  361. }
  362. /**
  363. * Triggers the deprecated event before_render($twigVariables, $twig, $templateName)
  364. *
  365. * Please note that the `before_render()` event gets `$templateName` passed
  366. * without its file extension. The file extension is later added again.
  367. *
  368. * @see DummyPlugin::onPageRendering()
  369. */
  370. public function onPageRendering(Twig_Environment &$twig, array &$twigVariables, &$templateName)
  371. {
  372. // template name contains file extension since Pico 1.0
  373. $fileExtension = '';
  374. if (($fileExtensionPos = strrpos($templateName, '.')) !== false) {
  375. $fileExtension = substr($templateName, $fileExtensionPos);
  376. $templateName = substr($templateName, 0, $fileExtensionPos);
  377. }
  378. $this->triggerEvent('before_render', array(&$twigVariables, &$twig, &$templateName));
  379. // add original file extension
  380. $templateName = $templateName . $fileExtension;
  381. }
  382. /**
  383. * Triggers the deprecated event after_render($output)
  384. *
  385. * @see DummyPlugin::onPageRendered()
  386. */
  387. public function onPageRendered(&$output)
  388. {
  389. $this->triggerEvent('after_render', array(&$output));
  390. }
  391. /**
  392. * Triggers a deprecated event on all plugins
  393. *
  394. * Deprecated events are also triggered on plugins which implement
  395. * {@link PicoPluginInterface}. Please note that the methods are called
  396. * directly and not through {@link PicoPluginInterface::handleEvent()}.
  397. *
  398. * @param string $eventName event to trigger
  399. * @param array $params parameters to pass
  400. * @return void
  401. */
  402. protected function triggerEvent($eventName, array $params = array())
  403. {
  404. foreach ($this->getPlugins() as $plugin) {
  405. if (method_exists($plugin, $eventName)) {
  406. call_user_func_array(array($plugin, $eventName), $params);
  407. }
  408. }
  409. }
  410. }