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