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 var $taghelper = null; 23 var $includes = array(); // deprecated - compatibility code for the blog plugin 24 25 /** 26 * Constructor loads default config settings once 27 */ 28 function helper_plugin_include() { 29 $this->defaults['firstsec'] = $this->getConf('firstseconly'); 30 $this->defaults['editbtn'] = $this->getConf('showeditbtn'); 31 $this->defaults['taglogos'] = $this->getConf('showtaglogos'); 32 $this->defaults['footer'] = $this->getConf('showfooter'); 33 $this->defaults['redirect'] = $this->getConf('doredirect'); 34 $this->defaults['date'] = $this->getConf('showdate'); 35 $this->defaults['user'] = $this->getConf('showuser'); 36 $this->defaults['comments'] = $this->getConf('showcomments'); 37 $this->defaults['linkbacks'] = $this->getConf('showlinkbacks'); 38 $this->defaults['tags'] = $this->getConf('showtags'); 39 $this->defaults['link'] = $this->getConf('showlink'); 40 $this->defaults['permalink'] = $this->getConf('showpermalink'); 41 $this->defaults['indent'] = $this->getConf('doindent'); 42 $this->defaults['linkonly'] = $this->getConf('linkonly'); 43 $this->defaults['title'] = $this->getConf('title'); 44 $this->defaults['pageexists'] = $this->getConf('pageexists'); 45 $this->defaults['parlink'] = $this->getConf('parlink'); 46 $this->defaults['inline'] = false; 47 $this->defaults['order'] = $this->getConf('order'); 48 $this->defaults['rsort'] = $this->getConf('rsort'); 49 $this->defaults['depth'] = $this->getConf('depth'); 50 } 51 52 /** 53 * Available methods for other plugins 54 */ 55 function getMethods() { 56 $result = array(); 57 $result[] = array( 58 'name' => 'get_flags', 59 'desc' => 'overrides standard values for showfooter and firstseconly settings', 60 'params' => array('flags' => 'array'), 61 ); 62 return $result; 63 } 64 65 /** 66 * Overrides standard values for showfooter and firstseconly settings 67 */ 68 function get_flags($setflags) { 69 // load defaults 70 $flags = $this->defaults; 71 foreach ($setflags as $flag) { 72 $value = ''; 73 if (strpos($flag, '=') !== -1) { 74 list($flag, $value) = explode('=', $flag, 2); 75 } 76 switch ($flag) { 77 case 'footer': 78 $flags['footer'] = 1; 79 break; 80 case 'nofooter': 81 $flags['footer'] = 0; 82 break; 83 case 'firstseconly': 84 case 'firstsectiononly': 85 $flags['firstsec'] = 1; 86 break; 87 case 'fullpage': 88 $flags['firstsec'] = 0; 89 break; 90 case 'noheader': 91 $flags['noheader'] = 1; 92 break; 93 case 'editbtn': 94 case 'editbutton': 95 $flags['editbtn'] = 1; 96 break; 97 case 'noeditbtn': 98 case 'noeditbutton': 99 $flags['editbtn'] = 0; 100 break; 101 case 'permalink': 102 $flags['permalink'] = 1; 103 break; 104 case 'nopermalink': 105 $flags['permalink'] = 0; 106 break; 107 case 'redirect': 108 $flags['redirect'] = 1; 109 break; 110 case 'noredirect': 111 $flags['redirect'] = 0; 112 break; 113 case 'link': 114 $flags['link'] = 1; 115 break; 116 case 'nolink': 117 $flags['link'] = 0; 118 break; 119 case 'user': 120 $flags['user'] = 1; 121 break; 122 case 'nouser': 123 $flags['user'] = 0; 124 break; 125 case 'comments': 126 $flags['comments'] = 1; 127 break; 128 case 'nocomments': 129 $flags['comments'] = 0; 130 break; 131 case 'linkbacks': 132 $flags['linkbacks'] = 1; 133 break; 134 case 'nolinkbacks': 135 $flags['linkbacks'] = 0; 136 break; 137 case 'tags': 138 $flags['tags'] = 1; 139 break; 140 case 'notags': 141 $flags['tags'] = 0; 142 break; 143 case 'date': 144 $flags['date'] = 1; 145 break; 146 case 'nodate': 147 $flags['date'] = 0; 148 break; 149 case 'indent': 150 $flags['indent'] = 1; 151 break; 152 case 'noindent': 153 $flags['indent'] = 0; 154 break; 155 case 'linkonly': 156 $flags['linkonly'] = 1; 157 break; 158 case 'nolinkonly': 159 case 'include_content': 160 $flags['linkonly'] = 0; 161 break; 162 case 'inline': 163 $flags['inline'] = 1; 164 break; 165 case 'title': 166 $flags['title'] = 1; 167 break; 168 case 'pageexists': 169 $flags['pageexists'] = 1; 170 break; 171 case 'existlink': 172 $flags['pageexists'] = 1; 173 $flags['linkonly'] = 1; 174 break; 175 case 'noparlink': 176 $flags['parlink'] = 0; 177 break; 178 case 'order': 179 $flags['order'] = $value; 180 break; 181 case 'sort': 182 $flags['rsort'] = 0; 183 break; 184 case 'rsort': 185 $flags['rsort'] = 1; 186 break; 187 case 'depth': 188 $flags['depth'] = $value; 189 break; 190 } 191 } 192 // the include_content URL parameter overrides flags 193 if (isset($_REQUEST['include_content'])) 194 $flags['linkonly'] = 0; 195 return $flags; 196 } 197 198 /** 199 * Returns the converted instructions of a give page/section 200 * 201 * @author Michael Klier <chi@chimeric.de> 202 * @author Michael Hamann <michael@content-space.de> 203 */ 204 function _get_instructions($page, $sect, $mode, $lvl, $flags, $root_id = null) { 205 $key = ($sect) ? $page . '#' . $sect : $page; 206 $this->includes[$key] = true; // legacy code for keeping compatibility with other plugins 207 208 // keep compatibility with other plugins that don't know the $root_id parameter 209 if (is_null($root_id)) { 210 global $ID; 211 $root_id = $ID; 212 } 213 214 if ($flags['linkonly']) { 215 if (page_exists($page) || $flags['pageexists'] == 0) { 216 $title = ''; 217 if ($flags['title']) 218 $title = p_get_first_heading($page); 219 if($flags['parlink']) { 220 $ins = array( 221 array('p_open', array()), 222 array('internallink', array(':'.$key, $title)), 223 array('p_close', array()), 224 ); 225 } else { 226 $ins = array(array('internallink', array(':'.$key,$title))); 227 } 228 }else { 229 $ins = array(); 230 } 231 } else { 232 if (page_exists($page)) { 233 global $ID; 234 $backupID = $ID; 235 $ID = $page; // Change the global $ID as otherwise plugins like the discussion plugin will save data for the wrong page 236 $ins = p_cached_instructions(wikiFN($page)); 237 $ID = $backupID; 238 } else { 239 $ins = array(); 240 } 241 242 $this->_convert_instructions($ins, $lvl, $page, $sect, $flags, $root_id); 243 } 244 return $ins; 245 } 246 247 /** 248 * Converts instructions of the included page 249 * 250 * The funcion iterates over the given list of instructions and generates 251 * an index of header and section indicies. It also removes document 252 * start/end instructions, converts links, and removes unwanted 253 * instructions like tags, comments, linkbacks. 254 * 255 * Later all header/section levels are convertet to match the current 256 * inclusion level. 257 * 258 * @author Michael Klier <chi@chimeric.de> 259 */ 260 function _convert_instructions(&$ins, $lvl, $page, $sect, $flags, $root_id) { 261 global $conf; 262 263 // filter instructions if needed 264 if(!empty($sect)) { 265 $this->_get_section($ins, $sect); // section required 266 } 267 268 if($flags['firstsec']) { 269 $this->_get_firstsec($ins, $page); // only first section 270 } 271 272 $ns = getNS($page); 273 $num = count($ins); 274 275 $conv_idx = array(); // conversion index 276 $lvl_max = false; // max level 277 $first_header = -1; 278 $no_header = false; 279 $sect_title = false; 280 $endpos = null; // end position of the raw wiki text 281 282 for($i=0; $i<$num; $i++) { 283 switch($ins[$i][0]) { 284 case 'document_start': 285 case 'document_end': 286 case 'section_edit': 287 unset($ins[$i]); 288 break; 289 case 'header': 290 // get section title of first section 291 if($sect && !$sect_title) { 292 $sect_title = $ins[$i][1][0]; 293 } 294 // check if we need to skip the first header 295 if((!$no_header) && $flags['noheader']) { 296 $no_header = true; 297 } 298 299 $conv_idx[] = $i; 300 // get index of first header 301 if($first_header == -1) $first_header = $i; 302 // get max level of this instructions set 303 if(!$lvl_max || ($ins[$i][1][1] < $lvl_max)) { 304 $lvl_max = $ins[$i][1][1]; 305 } 306 break; 307 case 'section_open': 308 if ($flags['inline']) 309 unset($ins[$i]); 310 else 311 $conv_idx[] = $i; 312 break; 313 case 'section_close': 314 if ($flags['inline']) 315 unset($ins[$i]); 316 break; 317 case 'internallink': 318 case 'internalmedia': 319 // make sure parameters aren't touched 320 $link_params = ''; 321 $link_id = $ins[$i][1][0]; 322 $link_parts = explode('?', $link_id, 2); 323 if (count($link_parts) === 2) { 324 $link_id = $link_parts[0]; 325 $link_params = $link_parts[1]; 326 } 327 // resolve the id without cleaning it 328 $link_id = resolve_id($ns, $link_id, false); 329 // this id is internal (i.e. absolute) now, add ':' to make resolve_id work again 330 if ($link_id{0} != ':') $link_id = ':'.$link_id; 331 // restore parameters 332 $ins[$i][1][0] = ($link_params != '') ? $link_id.'?'.$link_params : $link_id; 333 break; 334 case 'plugin': 335 // FIXME skip other plugins? 336 switch($ins[$i][1][0]) { 337 case 'tag_tag': // skip tags 338 case 'discussion_comments': // skip comments 339 case 'linkback': // skip linkbacks 340 case 'data_entry': // skip data plugin 341 case 'meta': // skip meta plugin 342 case 'indexmenu_tag': // skip indexmenu sort tag 343 case 'include_sorttag': // skip include plugin sort tag 344 unset($ins[$i]); 345 break; 346 // adapt indentation level of nested includes 347 case 'include_include': 348 if (!$flags['inline'] && $flags['indent']) 349 $ins[$i][1][1][4] += $lvl; 350 break; 351 /* 352 * if there is already a closelastsecedit instruction (was added by one of the section 353 * functions), store its position but delete it as it can't be determined yet if it is needed, 354 * i.e. if there is a header which generates a section edit (depends on the levels, level 355 * adjustments, $no_header, ...) 356 */ 357 case 'include_closelastsecedit': 358 $endpos = $ins[$i][1][1][0]; 359 unset($ins[$i]); 360 break; 361 } 362 break; 363 default: 364 break; 365 } 366 } 367 368 // calculate difference between header/section level and include level 369 $diff = 0; 370 if (!isset($lvl_max)) $lvl_max = 0; // if no level found in target, set to 0 371 $diff = $lvl - $lvl_max + 1; 372 if ($no_header) $diff -= 1; // push up one level if "noheader" 373 374 // convert headers and set footer/permalink 375 $hdr_deleted = false; 376 $has_permalink = false; 377 $footer_lvl = false; 378 $contains_secedit = false; 379 $section_close_at = false; 380 foreach($conv_idx as $idx) { 381 if($ins[$idx][0] == 'header') { 382 if ($section_close_at === false) { 383 // store the index of the first heading (the begin of the first section) 384 $section_close_at = $idx; 385 } 386 387 if($no_header && !$hdr_deleted) { 388 unset ($ins[$idx]); 389 $hdr_deleted = true; 390 continue; 391 } 392 393 if($flags['indent']) { 394 $lvl_new = (($ins[$idx][1][1] + $diff) > 5) ? 5 : ($ins[$idx][1][1] + $diff); 395 $ins[$idx][1][1] = $lvl_new; 396 } 397 398 if($ins[$idx][1][1] <= $conf['maxseclevel']) 399 $contains_secedit = true; 400 401 // set permalink 402 if($flags['link'] && !$has_permalink && ($idx == $first_header)) { 403 $this->_permalink($ins[$idx], $page, $sect, $flags); 404 $has_permalink = true; 405 } 406 407 // set footer level 408 if(!$footer_lvl && ($idx == $first_header) && !$no_header) { 409 if($flags['indent']) { 410 $footer_lvl = $lvl_new; 411 } else { 412 $footer_lvl = $lvl_max; 413 } 414 } 415 } else { 416 // it's a section 417 if($flags['indent']) { 418 $lvl_new = (($ins[$idx][1][0] + $diff) > 5) ? 5 : ($ins[$idx][1][0] + $diff); 419 $ins[$idx][1][0] = $lvl_new; 420 } 421 422 // check if noheader is used and set the footer level to the first section 423 if($no_header && !$footer_lvl) { 424 if($flags['indent']) { 425 $footer_lvl = $lvl_new; 426 } else { 427 $footer_lvl = $lvl_max; 428 } 429 } 430 } 431 } 432 433 // close last open section of the included page if there is any 434 if ($contains_secedit) { 435 array_push($ins, array('plugin', array('include_closelastsecedit', array($endpos)))); 436 } 437 438 // add edit button 439 if($flags['editbtn']) { 440 $this->_editbtn($ins, $page, $sect, $sect_title, ($flags['redirect'] ? $root_id : false)); 441 } 442 443 // add footer 444 if($flags['footer']) { 445 $ins[] = $this->_footer($page, $sect, $sect_title, $flags, $footer_lvl, $root_id); 446 } 447 448 // wrap content at the beginning of the include that is not in a section in a section 449 if ($lvl > 0 && $section_close_at !== 0 && $flags['indent'] && !$flags['inline']) { 450 if ($section_close_at === false) { 451 $ins[] = array('section_close', array()); 452 array_unshift($ins, array('section_open', array($lvl))); 453 } else { 454 $section_close_idx = array_search($section_close_at, array_keys($ins)); 455 if ($section_close_idx > 0) { 456 $before_ins = array_slice($ins, 0, $section_close_idx); 457 $after_ins = array_slice($ins, $section_close_idx); 458 $ins = array_merge($before_ins, array(array('section_close', array())), $after_ins); 459 array_unshift($ins, array('section_open', array($lvl))); 460 } 461 } 462 } 463 464 // add instructions entry wrapper 465 array_unshift($ins, array('plugin', array('include_wrap', array('open', $page, $flags['redirect'])))); 466 array_push($ins, array('plugin', array('include_wrap', array('close')))); 467 468 // close previous section if any and re-open after inclusion 469 if($lvl != 0 && $this->sec_close && !$flags['inline']) { 470 array_unshift($ins, array('section_close', array())); 471 $ins[] = array('section_open', array($lvl)); 472 } 473 } 474 475 /** 476 * Appends instruction item for the include plugin footer 477 * 478 * @author Michael Klier <chi@chimeric.de> 479 */ 480 function _footer($page, $sect, $sect_title, $flags, $footer_lvl, $root_id) { 481 $footer = array(); 482 $footer[0] = 'plugin'; 483 $footer[1] = array('include_footer', array($page, $sect, $sect_title, $flags, $root_id, $footer_lvl)); 484 return $footer; 485 } 486 487 /** 488 * Appends instruction item for an edit button 489 * 490 * @author Michael Klier <chi@chimeric.de> 491 */ 492 function _editbtn(&$ins, $page, $sect, $sect_title, $root_id) { 493 $title = ($sect) ? $sect_title : $page; 494 $editbtn = array(); 495 $editbtn[0] = 'plugin'; 496 $editbtn[1] = array('include_editbtn', array($title)); 497 $ins[] = $editbtn; 498 } 499 500 /** 501 * Convert instruction item for a permalink header 502 * 503 * @author Michael Klier <chi@chimeric.de> 504 */ 505 function _permalink(&$ins, $page, $sect, $flags) { 506 $ins[0] = 'plugin'; 507 $ins[1] = array('include_header', array($ins[1][0], $ins[1][1], $ins[1][2], $page, $sect, $flags)); 508 } 509 510 /** 511 * Get a section including its subsections 512 * 513 * @author Michael Klier <chi@chimeric.de> 514 */ 515 function _get_section(&$ins, $sect) { 516 $num = count($ins); 517 $offset = false; 518 $lvl = false; 519 $end = false; 520 $endpos = null; // end position in the input text, needed for section edit buttons 521 522 $check = array(); // used for sectionID() in order to get the same ids as the xhtml renderer 523 524 for($i=0; $i<$num; $i++) { 525 if ($ins[$i][0] == 'header') { 526 527 // found the right header 528 if (sectionID($ins[$i][1][0], $check) == $sect) { 529 $offset = $i; 530 $lvl = $ins[$i][1][1]; 531 } elseif ($offset && $lvl && ($ins[$i][1][1] <= $lvl)) { 532 $end = $i - $offset; 533 $endpos = $ins[$i][1][2]; // the position directly after the found section, needed for the section edit button 534 break; 535 } 536 } 537 } 538 $offset = $offset ? $offset : 0; 539 $end = $end ? $end : ($num - 1); 540 if(is_array($ins)) { 541 $ins = array_slice($ins, $offset, $end); 542 // store the end position in the include_closelastsecedit instruction so it can generate a matching button 543 $ins[] = array('plugin', array('include_closelastsecedit', array($endpos))); 544 } 545 } 546 547 /** 548 * Only display the first section of a page and a readmore link 549 * 550 * @author Michael Klier <chi@chimeric.de> 551 */ 552 function _get_firstsec(&$ins, $page) { 553 $num = count($ins); 554 $first_sect = false; 555 $endpos = null; // end position in the input text 556 for($i=0; $i<$num; $i++) { 557 if($ins[$i][0] == 'section_close') { 558 $first_sect = $i; 559 } 560 if ($ins[$i][0] == 'header') { 561 /* 562 * Store the position of the last header that is encountered. As section_close/open-instruction are 563 * always (unless some plugin modifies this) around a header instruction this means that the last 564 * position that is stored here is exactly the position of the section_close/open at which the content 565 * is truncated. 566 */ 567 $endpos = $ins[$i][1][2]; 568 } 569 // only truncate the content and add the read more link when there is really 570 // more than that first section 571 if(($first_sect) && ($ins[$i][0] == 'section_open')) { 572 $ins = array_slice($ins, 0, $first_sect); 573 $ins[] = array('plugin', array('include_readmore', array($page))); 574 $ins[] = array('section_close', array()); 575 // store the end position in the include_closelastsecedit instruction so it can generate a matching button 576 $ins[] = array('plugin', array('include_closelastsecedit', array($endpos))); 577 return; 578 } 579 } 580 } 581 582 /** 583 * Gives a list of pages for a given include statement 584 * 585 * @author Michael Hamann <michael@content-space.de> 586 */ 587 function _get_included_pages($mode, $page, $sect, $parent_id, $flags) { 588 global $conf; 589 $pages = array(); 590 switch($mode) { 591 case 'namespace': 592 $page = cleanID($page); 593 $ns = utf8_encodeFN(str_replace(':', '/', $page)); 594 // depth is absolute depth, not relative depth, but 0 has a special meaning. 595 $depth = $flags['depth'] ? $flags['depth'] + substr_count($page, ':') + ($page ? 1 : 0) : 0; 596 search($pagearrays, $conf['datadir'], 'search_allpages', array('depth' => $depth), $ns); 597 if (is_array($pagearrays)) { 598 foreach ($pagearrays as $pagearray) { 599 if (!isHiddenPage($pagearray['id'])) // skip hidden pages 600 $pages[] = $pagearray['id']; 601 } 602 } 603 break; 604 case 'tagtopic': 605 if (!$this->taghelper) 606 $this->taghelper =& plugin_load('helper', 'tag'); 607 if(!$this->taghelper) { 608 msg('You have to install the tag plugin to use this functionality!', -1); 609 return array(); 610 } 611 $tag = $page; 612 $sect = ''; 613 $pagearrays = $this->taghelper->getTopic('', null, $tag); 614 foreach ($pagearrays as $pagearray) { 615 $pages[] = $pagearray['id']; 616 } 617 break; 618 default: 619 $page = $this->_apply_macro($page); 620 resolve_pageid(getNS($parent_id), $page, $exists); // resolve shortcuts and clean ID 621 if (auth_quickaclcheck($page) >= AUTH_READ) 622 $pages[] = $page; 623 } 624 625 if (count($pages) > 1) { 626 if ($flags['order'] === 'id') { 627 if ($flags['rsort']) { 628 usort($pages, array($this, '_r_strnatcasecmp')); 629 } else { 630 natcasesort($pages); 631 } 632 } else { 633 $ordered_pages = array(); 634 foreach ($pages as $page) { 635 $key = ''; 636 switch ($flags['order']) { 637 case 'title': 638 $key = p_get_first_heading($page); 639 break; 640 case 'created': 641 $key = p_get_metadata($page, 'date created', METADATA_DONT_RENDER); 642 break; 643 case 'modified': 644 $key = p_get_metadata($page, 'date modified', METADATA_DONT_RENDER); 645 break; 646 case 'indexmenu': 647 $key = p_get_metadata($page, 'indexmenu_n', METADATA_RENDER_USING_SIMPLE_CACHE); 648 if ($key === null) 649 $key = ''; 650 break; 651 case 'custom': 652 $key = p_get_metadata($page, 'include_n', METADATA_RENDER_USING_SIMPLE_CACHE); 653 if ($key === null) 654 $key = ''; 655 break; 656 } 657 $key .= '_'.$page; 658 $ordered_pages[$key] = $page; 659 } 660 if ($flags['rsort']) { 661 uksort($ordered_pages, array($this, '_r_strnatcasecmp')); 662 } else { 663 uksort($ordered_pages, 'strnatcasecmp'); 664 } 665 $pages = $ordered_pages; 666 } 667 } 668 669 $result = array(); 670 foreach ($pages as $page) { 671 $exists = page_exists($page); 672 $result[] = array('id' => $page, 'exists' => $exists, 'parent_id' => $parent_id); 673 } 674 return $result; 675 } 676 677 /** 678 * String comparisons using a "natural order" algorithm in reverse order 679 * 680 * @link http://php.net/manual/en/function.strnatcmp.php 681 * @param string $a First string 682 * @param string $b Second string 683 * @return int Similar to other string comparison functions, this one returns < 0 if 684 * str1 is greater than str2; > 685 * 0 if str1 is lesser than 686 * str2, and 0 if they are equal. 687 */ 688 function _r_strnatcasecmp($a, $b) { 689 return strnatcasecmp($b, $a); 690 } 691 692 /** 693 * This function generates the list of all included pages from a list of metadata 694 * instructions. 695 */ 696 function _get_included_pages_from_meta_instructions($instructions) { 697 $pages = array(); 698 foreach ($instructions as $instruction) { 699 $mode = $instruction['mode']; 700 $page = $instruction['page']; 701 $sect = $instruction['sect']; 702 $parent_id = $instruction['parent_id']; 703 $flags = $instruction['flags']; 704 $pages = array_merge($pages, $this->_get_included_pages($mode, $page, $sect, $parent_id, $flags)); 705 } 706 return $pages; 707 } 708 709 /** 710 * Makes user or date dependent includes possible 711 */ 712 function _apply_macro($id) { 713 global $INFO; 714 global $auth; 715 716 // if we don't have an auth object, do nothing 717 if (!$auth) return $id; 718 719 $user = $_SERVER['REMOTE_USER']; 720 $group = $INFO['userinfo']['grps'][0]; 721 722 $time_stamp = time(); 723 if(preg_match('/@DATE(\w+)@/',$id,$matches)) { 724 switch($matches[1]) { 725 case 'PMONTH': 726 $time_stamp = strtotime("-1 month"); 727 break; 728 case 'NMONTH': 729 $time_stamp = strtotime("+1 month"); 730 break; 731 case 'NWEEK': 732 $time_stamp = strtotime("+1 week"); 733 break; 734 case 'PWEEK': 735 $time_stamp = strtotime("-1 week"); 736 break; 737 case 'TOMORROW': 738 $time_stamp = strtotime("+1 day"); 739 break; 740 case 'YESTERDAY': 741 $time_stamp = strtotime("-1 day"); 742 break; 743 case 'NYEAR': 744 $time_stamp = strtotime("+1 year"); 745 break; 746 case 'PYEAR': 747 $time_stamp = strtotime("-1 year"); 748 break; 749 } 750 $id = preg_replace('/@DATE(\w+)@/','', $id); 751 } 752 753 $replace = array( 754 '@USER@' => cleanID($user), 755 '@NAME@' => cleanID($INFO['userinfo']['name']), 756 '@GROUP@' => cleanID($group), 757 '@YEAR@' => date('Y',$time_stamp), 758 '@MONTH@' => date('m',$time_stamp), 759 '@WEEK@' => date('W',$time_stamp), 760 '@DAY@' => date('d',$time_stamp), 761 '@YEARPMONTH@' => date('Ym',strtotime("-1 month")), 762 '@PMONTH@' => date('m',strtotime("-1 month")), 763 '@NMONTH@' => date('m',strtotime("+1 month")), 764 '@YEARNMONTH@' => date('Ym',strtotime("+1 month")), 765 '@YEARPWEEK@' => date('YW',strtotime("-1 week")), 766 '@PWEEK@' => date('W',strtotime("-1 week")), 767 '@NWEEK@' => date('W',strtotime("+1 week")), 768 '@YEARNWEEK@' => date('YW',strtotime("+1 week")), 769 ); 770 return str_replace(array_keys($replace), array_values($replace), $id); 771 } 772} 773// vim:ts=4:sw=4:et: 774