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