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 16require_once(DOKU_INC.'inc/search.php'); 17 18class helper_plugin_include extends DokuWiki_Plugin { // DokuWiki_Helper_Plugin 19 20 var $defaults = array(); 21 var $sec_close = true; 22 23 /** 24 * Constructor loads default config settings once 25 */ 26 function helper_plugin_include() { 27 $this->defaults['firstsec'] = $this->getConf('firstseconly'); 28 $this->defaults['editbtn'] = $this->getConf('showeditbtn'); 29 $this->defaults['taglogos'] = $this->getConf('showtaglogos'); 30 $this->defaults['footer'] = $this->getConf('showfooter'); 31 $this->defaults['redirect'] = $this->getConf('doredirect'); 32 $this->defaults['date'] = $this->getConf('showdate'); 33 $this->defaults['user'] = $this->getConf('showuser'); 34 $this->defaults['comments'] = $this->getConf('showcomments'); 35 $this->defaults['linkbacks'] = $this->getConf('showlinkbacks'); 36 $this->defaults['tags'] = $this->getConf('showtags'); 37 $this->defaults['link'] = $this->getConf('showlink'); 38 $this->defaults['permalink'] = $this->getConf('showpermalink'); 39 $this->defaults['indent'] = $this->getConf('doindent'); 40 } 41 42 /** 43 * Available methods for other plugins 44 */ 45 function getMethods() { 46 $result = array(); 47 $result[] = array( 48 'name' => 'get_flags', 49 'desc' => 'overrides standard values for showfooter and firstseconly settings', 50 'params' => array('flags' => 'array'), 51 ); 52 return $result; 53 } 54 55 /** 56 * Overrides standard values for showfooter and firstseconly settings 57 */ 58 function get_flags($setflags) { 59 // load defaults 60 $flags = array(); 61 $flags = $this->defaults; 62 foreach ($setflags as $flag) { 63 switch ($flag) { 64 case 'footer': 65 $flags['footer'] = 1; 66 break; 67 case 'nofooter': 68 $flags['footer'] = 0; 69 break; 70 case 'firstseconly': 71 case 'firstsectiononly': 72 $flags['firstsec'] = 1; 73 break; 74 case 'fullpage': 75 $flags['firstsec'] = 0; 76 break; 77 case 'noheader': 78 $flags['noheader'] = 1; 79 break; 80 case 'editbtn': 81 case 'editbutton': 82 $flags['editbtn'] = 1; 83 break; 84 case 'noeditbtn': 85 case 'noeditbutton': 86 $flags['editbtn'] = 0; 87 break; 88 case 'permalink': 89 $flags['permalink'] = 1; 90 break; 91 case 'nopermalink': 92 $flags['permalink'] = 0; 93 break; 94 case 'redirect': 95 $flags['redirect'] = 1; 96 break; 97 case 'noredirect': 98 $flags['redirect'] = 0; 99 break; 100 case 'link': 101 $flags['link'] = 1; 102 break; 103 case 'nolink': 104 $flags['link'] = 0; 105 break; 106 case 'user': 107 $flags['user'] = 1; 108 break; 109 case 'nouser': 110 $flags['user'] = 0; 111 break; 112 case 'comments': 113 $flags['comments'] = 1; 114 break; 115 case 'nocomments': 116 $flags['comments'] = 0; 117 break; 118 case 'linkbacks': 119 $flags['linkbacks'] = 1; 120 break; 121 case 'nolinkbacks': 122 $flags['linkbacks'] = 0; 123 break; 124 case 'tags': 125 $flags['tags'] = 1; 126 break; 127 case 'notags': 128 $flags['tags'] = 0; 129 break; 130 case 'date': 131 $flags['date'] = 1; 132 break; 133 case 'nodate': 134 $flags['date'] = 0; 135 break; 136 case 'indent': 137 $flags['indent'] = 1; 138 break; 139 case 'noindent': 140 $flags['indent'] = 0; 141 break; 142 } 143 } 144 return $flags; 145 } 146 147 /** 148 * Returns the converted instructions of a give page/section 149 * 150 * @author Michael Klier <chi@chimeric.de> 151 * @author Michael Hamann <michael@content-space.de> 152 */ 153 function _get_instructions($page, $sect, $mode, $lvl, $flags, $root_id) { 154 $ins = p_cached_instructions(wikiFN($page)); 155 $this->_convert_instructions($ins, $lvl, $page, $sect, $flags, $root_id); 156 return $ins; 157 } 158 159 /** 160 * Converts instructions of the included page 161 * 162 * The funcion iterates over the given list of instructions and generates 163 * an index of header and section indicies. It also removes document 164 * start/end instructions, converts links, and removes unwanted 165 * instructions like tags, comments, linkbacks. 166 * 167 * Later all header/section levels are convertet to match the current 168 * inclusion level. 169 * 170 * @author Michael Klier <chi@chimeric.de> 171 */ 172 function _convert_instructions(&$ins, $lvl, $page, $sect, $flags, $root_id) { 173 174 // filter instructions if needed 175 if(!empty($sect)) { 176 $this->_get_section($ins, $sect); // section required 177 } 178 179 if($flags['firstsec']) { 180 $this->_get_firstsec($ins, $page); // only first section 181 } 182 183 $ns = getNS($page); 184 $num = count($ins); 185 186 $conv_idx = array(); // conversion index 187 $lvl_max = false; // max level 188 $first_header = -1; 189 $no_header = false; 190 $sect_title = false; 191 192 for($i=0; $i<$num; $i++) { 193 switch($ins[$i][0]) { 194 case 'document_start': 195 case 'document_end': 196 case 'section_edit': 197 unset($ins[$i]); 198 break; 199 case 'header': 200 // get section title of first section 201 if($sect && !$sect_title) { 202 $sect_title = $ins[$i][1][0]; 203 } 204 // check if we need to skip the first header 205 if((!$no_header) && $flags['noheader']) { 206 $no_header = true; 207 } 208 209 $conv_idx[] = $i; 210 // get index of first header 211 if($first_header == -1) $first_header = $i; 212 // get max level of this instructions set 213 if(!$lvl_max || ($ins[$i][1][1] < $lvl_max)) { 214 $lvl_max = $ins[$i][1][1]; 215 } 216 break; 217 case 'section_open': 218 $conv_idx[] = $i; 219 break; 220 case 'internallink': 221 case 'internalmedia': 222 if($ins[$i][1][0]{0} == '.') { 223 if($ins[$i][1][0]{1} == '.') { 224 $ins[$i][1][0] = getNS($ns) . ':' . substr($ins[$i][1][0], 2); // parent namespace 225 } else { 226 $ins[$i][1][0] = $ns . ':' . substr($ins[$i][1][0], 1); // current namespace 227 } 228 } elseif (strpos($ins[$i][1][0], ':') === false) { 229 $ins[$i][1][0] = $ns . ':' . $ins[$i][1][0]; // relative links 230 } 231 break; 232 case 'plugin': 233 // FIXME skip other plugins? 234 switch($ins[$i][1][0]) { 235 case 'tag_tag': // skip tags 236 case 'discussion_comments': // skip comments 237 case 'linkback': // skip linkbacks 238 case 'data_entry': // skip data plugin 239 case 'meta': // skip meta plugin 240 unset($ins[$i]); 241 break; 242 // adapt indentation level of nested includes 243 case 'include_include': 244 $ins[$i][1][1][4] += $lvl; 245 break; 246 } 247 break; 248 default: 249 break; 250 } 251 } 252 253 // calculate difference between header/section level and include level 254 $diff = 0; 255 if (!isset($lvl_max)) $lvl_max = 0; // if no level found in target, set to 0 256 $diff = $lvl - $lvl_max + 1; 257 if ($no_header) $diff -= 1; // push up one level if "noheader" 258 259 // convert headers and set footer/permalink 260 $hdr_deleted = false; 261 $has_permalink = false; 262 $footer_lvl = false; 263 foreach($conv_idx as $idx) { 264 if($ins[$idx][0] == 'header') { 265 if($no_header && !$hdr_deleted) { 266 unset ($ins[$idx]); 267 $hdr_deleted = true; 268 continue; 269 } 270 271 if($flags['indent']) { 272 $lvl_new = (($ins[$idx][1][1] + $diff) > 5) ? 5 : ($ins[$idx][1][1] + $diff); 273 $ins[$idx][1][1] = $lvl_new; 274 } 275 276 // set permalink 277 if($flags['link'] && !$has_permalink && ($idx == $first_header)) { 278 $this->_permalink($ins[$idx], $page, $sect, $flags); 279 $has_permalink = true; 280 } 281 282 // set footer level 283 if(!$footer_lvl && ($idx == $first_header) && !$no_header) { 284 if($flags['indent']) { 285 $footer_lvl = $lvl_new; 286 } else { 287 $footer_lvl = $lvl_max; 288 } 289 } 290 } else { 291 // it's a section 292 if($flags['indent']) { 293 $lvl_new = (($ins[$idx][1][0] + $diff) > 5) ? 5 : ($ins[$idx][1][0] + $diff); 294 $ins[$idx][1][0] = $lvl_new; 295 } 296 297 // check if noheader is used and set the footer level to the first section 298 if($no_header && !$footer_lvl) { 299 if($flags['indent']) { 300 $footer_lvl = $lvl_new; 301 } else { 302 $footer_lvl = $lvl_max; 303 } 304 } 305 } 306 } 307 308 // add edit button 309 if($flags['editbtn'] && (auth_quickaclcheck($page) >= AUTH_EDIT)) { 310 $this->_editbtn($ins, $page, $sect, $sect_title, $root_id); 311 } 312 313 // add footer 314 if($flags['footer']) { 315 $ins[] = $this->_footer($page, $sect, $sect_title, $flags, $footer_lvl, $root_id); 316 } 317 318 // add instructions entry divs 319 array_unshift($ins, array('plugin', array('include_div', array('open', $page)))); 320 array_push($ins, array('plugin', array('include_div', array('close')))); 321 322 // close previous section if any and re-open after inclusion 323 if($lvl != 0 && $this->sec_close) { 324 array_unshift($ins, array('section_close', array())); 325 $ins[] = array('section_open', array($lvl)); 326 } 327 } 328 329 /** 330 * Appends instruction item for the include plugin footer 331 * 332 * @author Michael Klier <chi@chimeric.de> 333 */ 334 function _footer($page, $sect, $sect_title, $flags, $footer_lvl, $root_id) { 335 $footer = array(); 336 $footer[0] = 'plugin'; 337 $footer[1] = array('include_footer', array($page, $sect, $sect_title, $flags, $root_id, $footer_lvl)); 338 return $footer; 339 } 340 341 /** 342 * Appends instruction item for an edit button 343 * 344 * @author Michael Klier <chi@chimeric.de> 345 */ 346 function _editbtn(&$ins, $page, $sect, $sect_title, $root_id) { 347 $editbtn = array(); 348 $editbtn[0] = 'plugin'; 349 $editbtn[1] = array('include_editbtn', array($page, $sect, $sect_title, $root_id)); 350 $ins[] = $editbtn; 351 } 352 353 /** 354 * Convert instruction item for a permalink header 355 * 356 * @author Michael Klier <chi@chimeric.de> 357 */ 358 function _permalink(&$ins, $page, $sect, $flags) { 359 $ins[0] = 'plugin'; 360 $ins[1] = array('include_header', array($ins[1][0], $ins[1][1], $page, $sect, $flags)); 361 } 362 363 /** 364 * Get a section including its subsections 365 * 366 * @author Michael Klier <chi@chimeric.de> 367 */ 368 function _get_section(&$ins, $sect) { 369 $num = count($ins); 370 $offset = false; 371 $lvl = false; 372 $end = false; 373 374 for($i=0; $i<$num; $i++) { 375 if ($ins[$i][0] == 'header') { 376 377 // found the right header 378 if (cleanID($ins[$i][1][0]) == $sect) { 379 $offset = $i; 380 $lvl = $ins[$i][1][1]; 381 } elseif ($offset && $lvl && ($ins[$i][1][1] <= $lvl)) { 382 $end = $i - $offset; 383 break; 384 } 385 } 386 } 387 $offset = $offset ? $offset : 0; 388 $end = $end ? $end : ($num - 1); 389 if(is_array($ins)) { 390 $ins = array_slice($ins, $offset, $end); 391 } 392 } 393 394 /** 395 * Only display the first section of a page and a readmore link 396 * 397 * @author Michael Klier <chi@chimeric.de> 398 */ 399 function _get_firstsec(&$ins, $page) { 400 $num = count($ins); 401 $first_sect = false; 402 for($i=0; $i<$num; $i++) { 403 if($ins[$i][0] == 'section_close') { 404 $first_sect = $i; 405 } 406 if(($first_sect) && ($ins[$i][0] == 'section_open')) { 407 $ins = array_slice($ins, 0, $first_sect); 408 $ins[] = array('p_open', array()); 409 $ins[] = array('internallink', array($page, $this->getLang('readmore'))); 410 $ins[] = array('p_close', array()); 411 $ins[] = array('section_close', array()); 412 return; 413 } 414 } 415 } 416 417 /** 418 * Makes user or date dependent includes possible 419 */ 420 function _apply_macro($id) { 421 global $INFO; 422 global $auth; 423 424 // if we don't have an auth object, do nothing 425 if (!$auth) return $id; 426 427 $user = $_SERVER['REMOTE_USER']; 428 $group = $INFO['userinfo']['grps'][0]; 429 430 $replace = array( 431 '@USER@' => cleanID($user), 432 '@NAME@' => cleanID($INFO['userinfo']['name']), 433 '@GROUP@' => cleanID($group), 434 '@YEAR@' => date('Y'), 435 '@MONTH@' => date('m'), 436 '@DAY@' => date('d'), 437 ); 438 return str_replace(array_keys($replace), array_values($replace), $id); 439 } 440} 441//vim:ts=4:sw=4:et:enc=utf-8: 442