1<?php 2/** 3 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 4 * @author Esther Brunner <wikidesign@gmail.com> 5 * @author Christopher Smith <chris@jalakai.co.uk> 6 * @author Gina Häußge, Michael Klier <dokuwiki@chimeric.de> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) die(); 11 12if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 13if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 15 16class helper_plugin_include extends DokuWiki_Plugin { // DokuWiki_Helper_Plugin 17 18 var $includes = array(); 19 var $toplevel_id = NULL; 20 var $defaults = array(); 21 22 /** 23 * Constructor loads default config settings once 24 */ 25 function helper_plugin_include() { 26 $this->defaults['firstsec'] = $this->getConf('firstseconly'); 27 $this->defaults['editbtn'] = $this->getConf('showeditbtn'); 28 $this->defaults['taglogos'] = $this->getConf('showtaglogos'); 29 $this->defaults['footer'] = $this->getConf('showfooter'); 30 $this->defaults['redirect'] = $this->getConf('doredirect'); 31 $this->defaults['date'] = $this->getConf('showdate'); 32 $this->defaults['user'] = $this->getConf('showuser'); 33 $this->defaults['comments'] = $this->getConf('showcomments'); 34 $this->defaults['linkbacks'] = $this->getConf('linkbacks'); 35 $this->defaults['tags'] = $this->getConf('tags'); 36 $this->defaults['link'] = $this->getConf('showlink'); 37 } 38 39 function getInfo() { 40 return array( 41 'author' => 'Gina Häußge, Michael Klier, Esther Brunner', 42 'email' => 'dokuwiki@chimeric.de', 43 'date' => @file_get_contents(DOKU_PLUGIN . 'blog/VERSION'), 44 'name' => 'Include Plugin (helper class)', 45 'desc' => 'Functions to include another page in a wiki page', 46 'url' => 'http://wiki.splitbrain.org/plugin:include', 47 ); 48 } 49 50 /** 51 * Available methods for other plugins 52 */ 53 function getMethods() { 54 $result = array(); 55 $result[] = array( 56 'name' => 'get_flags', 57 'desc' => 'overrides standard values for showfooter and firstseconly settings', 58 'params' => array('flags' => 'array'), 59 ); 60 return $result; 61 } 62 63 /** 64 * Overrides standard values for showfooter and firstseconly settings 65 */ 66 function get_flags($setflags) { 67 // load defaults 68 $flags = array(); 69 $flags = $this->defaults; 70 foreach ($setflags as $flag) { 71 switch ($flag) { 72 case 'footer': 73 $flags['footer'] = 1; 74 break; 75 case 'nofooter': 76 $flags['footer'] = 0; 77 break; 78 case 'firstseconly': 79 case 'firstsectiononly': 80 $flags['firstsec'] = 1; 81 break; 82 case 'fullpage': 83 $flags['firstsec'] = 0; 84 break; 85 case 'noheader': 86 $flags['noheader'] = 1; 87 break; 88 case 'editbtn': 89 case 'editbutton': 90 $flags['editbtn'] = 1; 91 break; 92 case 'noeditbtn': 93 case 'noeditbutton': 94 $flags['editbtn'] = 0; 95 break; 96 case 'permalink': 97 $flags['permalink'] = 1; 98 break; 99 case 'nopermalink': 100 $flags['permalink'] = 0; 101 break; 102 case 'redirect': 103 $flags['redirect'] = 1; 104 break; 105 case 'noredirect': 106 $flags['redirect'] = 0; 107 break; 108 } 109 } 110 return $flags; 111 } 112 113 /** 114 * Parses the instructions list 115 * 116 * called by the action plugin component, this function is called 117 * recursively for the p_cached_instructions call (when the instructions 118 * need to be updated) 119 * 120 * @author Michael Klier <chi@chimeric.de> 121 */ 122 function parse_instructions($id, &$ins) { 123 $num = count($ins); 124 125 $lvl = 0; 126 $mode = ''; 127 $page = ''; 128 $flags = array(); 129 130 for($i=0; $i<$num; $i++) { 131 // set current level 132 if($ins[$i][0] == 'section_open') { 133 $lvl = $ins[$i][1][0]; 134 } 135 if($ins[$i][0] == 'plugin' && $ins[$i][1][0] == 'include_include' ) { 136 $mode = $ins[$i][1][1][0]; 137 $page = $ins[$i][1][1][1]; 138 $sect = $ins[$i][1][1][2]; 139 $flags = $ins[$i][1][1][3]; 140 141 $page = $this->_apply_macro($page); 142 resolve_pageid(getNS($id), $page, $exists); // resolve shortcuts 143 $flags = $this->get_flags($flags); 144 $ins_inc = $this->_get_instructions($page, $sect, $mode, $lvl, $flags); 145 146 if(!empty($ins_inc)) { 147 // combine instructions and reset counter 148 $ins_start = array_slice($ins, 0, $i+1); 149 $ins_end = array_slice($ins, $i+1); 150 $ins = array_merge($ins_start, $ins_inc, $ins_end); 151 $num = count($ins); 152 } 153 } 154 } 155 } 156 157 /** 158 * Returns the converted instructions of a give page/section 159 * 160 * @author Michael Klier <chi@chimeric.de> 161 */ 162 function _get_instructions($page, $sect, $mode, $lvl, $flags) { 163 global $ID; 164 165 if($ID == $page || !page_exists($page) || (page_exists($page) && auth_quickaclcheck($page) < AUTH_READ)) return array(); 166 $key = (!$sect) ? $page . '#' . $sect : $page; 167 168 // prevent recursion 169 if(!$this->includes[$key]) { 170 $ins = p_cached_instructions(wikiFN($page)); 171 $this->includes[$key] = true; 172 $this->_convert_instructions($ins, $lvl, $page, $sect, $flags); 173 return $ins; 174 } 175 } 176 177 /** 178 * Converts instructions of the included page 179 * 180 * @author Michael Klier <chi@chimeric.de> 181 */ 182 function _convert_instructions(&$ins, $lvl, $page, $sect, $flags) { 183 184 if(!empty($sect)) { 185 $this->_get_section($ins, $sect); // section required 186 } elseif($flags['firstsec']) { 187 $this->_get_firstsec($ins, $page); // only first section 188 } 189 190 $has_permalink = false; 191 $footer_lvl = false; 192 $ns = getNS($page); 193 $num = count($ins); 194 $top_lvl = $lvl; // save toplevel for later use 195 for($i=0; $i<$num; $i++) { 196 switch($ins[$i][0]) { 197 case 'document_start': 198 case 'document_end': 199 case 'section_edit': 200 unset($ins[$i]); 201 break; 202 case 'header': 203 $ins[$i][1][1] = $this->_get_level($lvl, $ins[$i][1][1]); 204 $lvl = $ins[$i][1][1]; 205 if(!$footer_lvl) $footer_lvl = $lvl; 206 if($sect && !$sect_title) { 207 $sect_title = $ins[$i][1][0]; 208 } 209 if($flags['link'] && !$has_permalink) { 210 $this->_permalink($ins[$i], $page, $sect); 211 $has_permalink = true; 212 } 213 break; 214 case 'section_open': 215 $ins[$i][1][0] = $this->_get_level($lvl, $ins[$i][1][0]); 216 $lvl = $ins[$i][1][0]; 217 break; 218 case 'internallink': 219 case 'internalmedia': 220 if($ins[$i][1][0]{0} == '.') { 221 if($ins[$i][1][0]{1} == '.') { 222 $ins[$i][1][0] = getNS($ns) . ':' . substr($ins[$i][1][0], 2); // parent namespace 223 } else { 224 $ins[$i][1][0] = $ns . ':' . substr($ins[$i][1][0], 1); // current namespace 225 } 226 } elseif (strpos($ins[$i][1][0], ':') === false) { 227 $ins[$i][1][0] = $ns . ':' . $ins[$i][1][0]; // relative links 228 } 229 break; 230 case 'plugin': 231 // FIXME skip others? 232 if($ins[$i][1][0] == 'tag_tag') unset($ins[$i]); // skip tags 233 if($ins[$i][1][0] == 'discussion_comments') unset($ins[$i]); // skip comments 234 break; 235 default: 236 break; 237 } 238 } 239 240 if($flags['footer']) $this->_footer($ins, $page, $sect, $sect_title, $flags, $footer_lvl); 241 242 // close previous section if any and re-open after inclusion 243 if($top_lvl != 0) { 244 array_unshift($ins, array('section_close')); 245 $ins[] = array('section_open', array($top_lvl)); 246 } 247 } 248 249 /** 250 * Appends instruction item for the include plugin footer 251 * 252 * @author Michael Klier <chi@chimeric.de> 253 */ 254 function _footer(&$ins, $page, $sect, $sect_title, $flags, $footer_lvl) { 255 $footer = array(); 256 $footer[0] = 'plugin'; 257 $footer[1] = array('include_footer', array($page, $sect, $sect_title, $flags, $this->toplevel_id, $footer_lvl)); 258 $ins[] = $footer; 259 } 260 261 /** 262 * Return the correct level 263 * 264 * @author Michael Klier <chi@chimeric.de> 265 */ 266 function _get_level($lvl, $curr_lvl) { 267 if($curr_lvl == $lvl) { 268 // current level equals inclusion level 269 // return current level increased by 1 270 return (($curr_lvl + 1) <= 5) ? ($curr_lvl + 1) : 5; 271 272 } elseif(($curr_lvl < $lvl) && (($lvl - $curr_lvl) <= 1)) { 273 // if current level is small than inclusion level and difference 274 // between inclusion level and current level is less than 1 275 // return current level increased by 1 276 return (($curr_lvl + 1) <= 5) ? ($curr_lvl + 1) : 5; 277 278 } elseif(($curr_lvl < $lvl) && (($lvl - $curr_lvl) > 1)) { 279 // if current level is less than inclusion level and 280 // difference between inclusion level and the curren level is 281 // greater than 1 return inclusion level increased by 1 282 return (($lvl + 1) <= 5) ? ($lvl + 1) : 5; 283 } 284 } 285 286 /** 287 * Get a section including its subsections 288 * 289 * @author Michael Klier <chi@chimeric.de> 290 */ 291 function _get_section(&$ins, $sect) { 292 $num = count($ins); 293 $offset = false; 294 $lvl = false; 295 296 for($i=0; $i<$num; $i++) { 297 if ($ins[$i][0] == 'header') { 298 299 // found the right header 300 if (cleanID($ins[$i][1][0]) == $sect) { 301 $offset = $i; 302 $lvl = $ins[$i][1][1]; 303 } elseif ($offset && $lvl && ($ins[$i][1][1] <= $lvl)) { 304 $ins = array_slice($ins, $offset, ($i - $offset)); 305 } 306 } 307 } 308 } 309 310 /** 311 * Only display the first section of a page and a readmore link 312 * 313 * @author Michael Klier <chi@chimeric.de> 314 */ 315 function _get_firstsec(&$ins, $page) { 316 $num = count($ins); 317 for($i=0; $i<$num; $i++) { 318 if($ins[$i][0] == 'section_close') { 319 $ins = array_slice($ins, 0, $i); 320 $ins[] = array('p_open', array()); 321 $ins[] = array('internallink',array($page, $this->getLang('readmore'))); 322 $ins[] = array('p_close', array()); 323 $ins[] = array('section_close'); 324 return; 325 } 326 } 327 } 328 329 /** 330 * Makes user or date dependent includes possible 331 */ 332 function _apply_macro($id) { 333 global $INFO; 334 global $auth; 335 336 // if we don't have an auth object, do nothing 337 if (!$auth) return $id; 338 339 $user = $_SERVER['REMOTE_USER']; 340 $userdata = $auth->getUserData($user); 341 $group = $userdata['grps'][0]; 342 343 $replace = array( 344 '@USER@' => cleanID($user), 345 '@NAME@' => cleanID($INFO['userinfo']['name']), 346 '@GROUP@' => cleanID($group), 347 '@YEAR@' => date('Y'), 348 '@MONTH@' => date('m'), 349 '@DAY@' => date('d'), 350 ); 351 return str_replace(array_keys($replace), array_values($replace), $id); 352 } 353 354 /** 355 * Create instruction item for a permalink header 356 * 357 * @param string $text: Headline text 358 * @param integer $level: Headline level 359 * @param integer $pos: I wish I knew what this is for... (me too ;-)) 360 * 361 * @author Gina Haeussge <osd@foosel.net> 362 * @author Michael Klier <chi@chimeric.de> 363 */ 364 function _permalink(&$ins, $page, $sect) { 365 $ins[0] = 'plugin'; 366 $ins[1] = array('include_header', array($ins[1][0], $ins[1][1], $page, $sect)); 367 } 368 369 /** 370 * Optionally display logo for the first tag found in the included page 371 * 372 * FIXME erm what was this for again? 373 */ 374 function _showTagLogos() { 375 if ((!$this->getConf('showtaglogos')) 376 || (plugin_isdisabled('tag')) 377 || (!$taghelper =& plugin_load('helper', 'tag'))) 378 return ''; 379 380 $subject = p_get_metadata($this->page['id'], 'subject'); 381 if (is_array($subject)) $tag = $subject[0]; 382 else list($tag, $rest) = explode(' ', $subject, 2); 383 $title = str_replace('_', ' ', noNS($tag)); 384 resolve_pageid($taghelper->namespace, $tag, $exists); // resolve shortcuts 385 386 $logosrc = mediaFN($logoID); 387 $types = array('.png', '.jpg', '.gif'); // auto-detect filetype 388 foreach ($types as $type) { 389 if (!@file_exists($logosrc.$type)) continue; 390 $logoID = $tag.$type; 391 $logosrc .= $type; 392 list($w, $h, $t, $a) = getimagesize($logosrc); 393 return ' style="min-height: '.$h.'px">'. 394 '<img class="mediaright" src="'.ml($logoID).'" alt="'.$title.'"/'; 395 } 396 return ''; 397 } 398} 399//vim:ts=4:sw=4:et:enc=utf-8: 400