1c677cb9fSwikidesign<?php 2c677cb9fSwikidesign/** 3c677cb9fSwikidesign * Include Plugin: Display a wiki page within another wiki page 4c677cb9fSwikidesign * 5c677cb9fSwikidesign * Action plugin component, for cache validity determination 6c677cb9fSwikidesign * 7c677cb9fSwikidesign * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 8c677cb9fSwikidesign * @author Christopher Smith <chris@jalakai.co.uk> 9c677cb9fSwikidesign */ 10c677cb9fSwikidesignif(!defined('DOKU_INC')) die(); // no Dokuwiki, no go 11c677cb9fSwikidesign 12c677cb9fSwikidesignif(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 13c677cb9fSwikidesignrequire_once(DOKU_PLUGIN.'action.php'); 14c677cb9fSwikidesign 15c677cb9fSwikidesign/** 16c677cb9fSwikidesign * All DokuWiki plugins to extend the parser/rendering mechanism 17c677cb9fSwikidesign * need to inherit from this class 18c677cb9fSwikidesign */ 19c677cb9fSwikidesignclass action_plugin_include extends DokuWiki_Action_Plugin { 20c677cb9fSwikidesign 21c677cb9fSwikidesign var $supportedModes = array('xhtml'); 22c677cb9fSwikidesign 23c677cb9fSwikidesign /** 24c677cb9fSwikidesign * return some info 25c677cb9fSwikidesign */ 26c677cb9fSwikidesign function getInfo() { 27c677cb9fSwikidesign return array( 28dbdadbd9SGina Haeussge 'author' => 'Gina Häußge, Michael Klier, Christopher Smith', 29dbdadbd9SGina Haeussge 'email' => 'dokuwiki@chimeric.de', 304052f233SGina Haeussge 'date' => @file_get_contents(DOKU_PLUGIN . 'blog/VERSION'), 31c677cb9fSwikidesign 'name' => 'Include Plugin', 32*c44026dcSMichael Hamann 'desc' => 'Improved cache handling for included pages and redirect-handling', 33c677cb9fSwikidesign 'url' => 'http://wiki.splitbrain.org/plugin:include', 34c677cb9fSwikidesign ); 35c677cb9fSwikidesign } 36c677cb9fSwikidesign 37c677cb9fSwikidesign /** 38c677cb9fSwikidesign * plugin should use this method to register its handlers with the dokuwiki's event controller 39c677cb9fSwikidesign */ 40c677cb9fSwikidesign function register(&$controller) { 41c677cb9fSwikidesign $controller->register_hook('PARSER_CACHE_USE','BEFORE', $this, '_cache_prepare'); 42c677cb9fSwikidesign# $controller->register_hook('PARSER_CACHE_USE','AFTER', $this, '_cache_result'); // debugging only 43*c44026dcSMichael Hamann $controller->register_hook('HTML_EDITFORM_OUTPUT', 'BEFORE', $this, 'handle_form'); 44*c44026dcSMichael Hamann $controller->register_hook('HTML_CONFLICTFORM_OUTPUT', 'BEFORE', $this, 'handle_form'); 45*c44026dcSMichael Hamann $controller->register_hook('HTML_DRAFTFORM_OUTPUT', 'BEFORE', $this, 'handle_form'); 46*c44026dcSMichael Hamann $controller->register_hook('ACTION_SHOW_REDIRECT', 'BEFORE', $this, 'handle_redirect'); 47*c44026dcSMichael Hamann } 48*c44026dcSMichael Hamann 49*c44026dcSMichael Hamann /** 50*c44026dcSMichael Hamann * add a hidden input to the form to preserve the redirect_id 51*c44026dcSMichael Hamann */ 52*c44026dcSMichael Hamann function handle_form(&$event, $param) { 53*c44026dcSMichael Hamann if (array_key_exists('redirect_id', $_REQUEST)) { 54*c44026dcSMichael Hamann $event->data->addHidden('redirect_id', cleanID($_REQUEST['redirect_id'])); 55*c44026dcSMichael Hamann } 56*c44026dcSMichael Hamann } 57*c44026dcSMichael Hamann 58*c44026dcSMichael Hamann /** 59*c44026dcSMichael Hamann * modify the data for the redirect when there is a redirect_id set 60*c44026dcSMichael Hamann */ 61*c44026dcSMichael Hamann function handle_redirect(&$event, $param) { 62*c44026dcSMichael Hamann if (array_key_exists('redirect_id', $_REQUEST)) { 63*c44026dcSMichael Hamann $event->data['id'] = cleanID($_REQUEST['redirect_id']); 64*c44026dcSMichael Hamann $event->data['title'] = ''; 65*c44026dcSMichael Hamann } 66c677cb9fSwikidesign } 67c677cb9fSwikidesign 68c677cb9fSwikidesign /** 69c677cb9fSwikidesign * prepare the cache object for default _useCache action 70c677cb9fSwikidesign */ 71c677cb9fSwikidesign function _cache_prepare(&$event, $param) { 72c677cb9fSwikidesign $cache =& $event->data; 73c677cb9fSwikidesign 74c677cb9fSwikidesign // we're only interested in wiki pages and supported render modes 75c677cb9fSwikidesign if (!isset($cache->page)) return; 76c677cb9fSwikidesign if (!isset($cache->mode) || !in_array($cache->mode, $this->supportedModes)) return; 77c677cb9fSwikidesign 78c677cb9fSwikidesign $key = ''; 79c677cb9fSwikidesign $depends = array(); 80c677cb9fSwikidesign $expire = $this->_inclusion_check($cache->page, $key, $depends); 81c677cb9fSwikidesign 82c677cb9fSwikidesign# global $debug; 83c677cb9fSwikidesign# $debug[] = compact('key','expire','depends','cache'); 84c677cb9fSwikidesign 85c677cb9fSwikidesign // empty $key implies no includes, so nothing to do 86c677cb9fSwikidesign if (empty($key)) return; 87c677cb9fSwikidesign 88c677cb9fSwikidesign // mark the cache as being modified by the include plugin 89c677cb9fSwikidesign $cache->include = true; 90c677cb9fSwikidesign 91c677cb9fSwikidesign // set new cache key & cache name - now also dependent on included page ids and their ACL_READ status 92c677cb9fSwikidesign $cache->key .= $key; 93c677cb9fSwikidesign $cache->cache = getCacheName($cache->key, $cache->ext); 94c677cb9fSwikidesign 95c677cb9fSwikidesign // inclusion check was able to determine the cache must be invalid 96c677cb9fSwikidesign if ($expire) { 97c677cb9fSwikidesign $event->preventDefault(); 98c677cb9fSwikidesign $event->stopPropagation(); 99c677cb9fSwikidesign $event->result = false; 100c677cb9fSwikidesign return; 101c677cb9fSwikidesign } 102c677cb9fSwikidesign 103c677cb9fSwikidesign // update depends['files'] array to include all included files 104c677cb9fSwikidesign $cache->depends['files'] = !empty($cache->depends['files']) ? array_merge($cache->depends['files'], $depends) : $depends; 105c677cb9fSwikidesign } 106c677cb9fSwikidesign 107c677cb9fSwikidesign /** 108c677cb9fSwikidesign * carry out included page checks: 109c677cb9fSwikidesign * - to establish proper cache name, its dependent on the read status of included pages 110c677cb9fSwikidesign * - to establish file dependencies, the included raw wiki pages 111c677cb9fSwikidesign * 112c677cb9fSwikidesign * @param string $id wiki page name 113c677cb9fSwikidesign * @param string $key (reference) cache key 114c677cb9fSwikidesign * @param array $depends array of include file dependencies 115c677cb9fSwikidesign * 116c677cb9fSwikidesign * @return bool expire the cache 117c677cb9fSwikidesign */ 118c677cb9fSwikidesign function _inclusion_check($id, &$key, &$depends) { 119c677cb9fSwikidesign $hasPart = p_get_metadata($id, 'relation haspart'); 120c677cb9fSwikidesign if (empty($hasPart)) return false; 121c677cb9fSwikidesign 122c677cb9fSwikidesign $expire = false; 123c677cb9fSwikidesign foreach ($hasPart as $page => $exists) { 124c677cb9fSwikidesign // ensure its a wiki page 125c677cb9fSwikidesign if (strpos($page,'/') || cleanID($page) != $page) continue; 126c677cb9fSwikidesign 127c01ce462SGina Haeussge // recursive includes aren't allowed and there is no need to do the same page twice 128c677cb9fSwikidesign $file = wikiFN($page); 129c01ce462SGina Haeussge if (in_array($file, $depends)) continue; 130c677cb9fSwikidesign 131c677cb9fSwikidesign // file existence state is different from state recorded in metadata 132c677cb9fSwikidesign if (@file_exists($file) != $exists) { 133c677cb9fSwikidesign 134c677cb9fSwikidesign if (($acl = $this->_acl_read_check($page)) != 'NONE') { $expire = true; } 135c677cb9fSwikidesign 136c677cb9fSwikidesign } else if ($exists) { 137c677cb9fSwikidesign 138c677cb9fSwikidesign // carry out an inclusion check on the included page, that will update $key & $depends 139c677cb9fSwikidesign if ($this->_inclusion_check($page, $key, $depends)) { $expire = true; } 140c677cb9fSwikidesign if (($acl = $this->_acl_read_check($page)) != 'NONE') { $depends[] = $file; } 141c677cb9fSwikidesign 142c677cb9fSwikidesign } else { 143c677cb9fSwikidesign $acl = 'NONE'; 144c677cb9fSwikidesign } 145c677cb9fSwikidesign 146c677cb9fSwikidesign // add this page and acl status to the key 147c677cb9fSwikidesign $key .= '#'.$page.'|'.$acl; 148c677cb9fSwikidesign } 149c677cb9fSwikidesign 150c677cb9fSwikidesign return $expire; 151c677cb9fSwikidesign } 152c677cb9fSwikidesign 153c677cb9fSwikidesign function _acl_read_check($id) { 154c677cb9fSwikidesign return (AUTH_READ <= auth_quickaclcheck($id)) ? 'READ' : 'NONE'; 155c677cb9fSwikidesign } 156c677cb9fSwikidesign 157c677cb9fSwikidesign function _cache_result(&$event, $param) { 158c677cb9fSwikidesign $cache =& $event->data; 159c677cb9fSwikidesign if (empty($cache->include)) return; 160c677cb9fSwikidesign 161c677cb9fSwikidesign# global $debug; 162c677cb9fSwikidesign# $debug['cache_result'][] = $event->result ? 'true' : 'false'; 163c677cb9fSwikidesign } 164c677cb9fSwikidesign 165c677cb9fSwikidesign} 166df4e907bSMichael Klier//vim:ts=4:sw=4:et:enc=utf-8: 167