summaryrefslogtreecommitdiff
path: root/platform/www/lib/plugins/include/helper.php
diff options
context:
space:
mode:
Diffstat (limited to 'platform/www/lib/plugins/include/helper.php')
-rw-r--r--platform/www/lib/plugins/include/helper.php941
1 files changed, 941 insertions, 0 deletions
diff --git a/platform/www/lib/plugins/include/helper.php b/platform/www/lib/plugins/include/helper.php
new file mode 100644
index 0000000..ebde44f
--- /dev/null
+++ b/platform/www/lib/plugins/include/helper.php
@@ -0,0 +1,941 @@
+<?php
+/**
+ * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
+ * @author Esther Brunner <wikidesign@gmail.com>
+ * @author Christopher Smith <chris@jalakai.co.uk>
+ * @author Gina Häußge, Michael Klier <dokuwiki@chimeric.de>
+ * @author Michael Hamann <michael@content-space.de>
+ */
+
+/**
+ * Helper functions for the include plugin and other plugins that want to include pages.
+ */
+class helper_plugin_include extends DokuWiki_Plugin { // DokuWiki_Helper_Plugin
+
+ var $defaults = array();
+ var $sec_close = true;
+ /** @var helper_plugin_tag $taghelper */
+ var $taghelper = null;
+ var $includes = array(); // deprecated - compatibility code for the blog plugin
+
+ /**
+ * Constructor loads default config settings once
+ */
+ function __construct() {
+ $this->defaults['noheader'] = $this->getConf('noheader');
+ $this->defaults['firstsec'] = $this->getConf('firstseconly');
+ $this->defaults['editbtn'] = $this->getConf('showeditbtn');
+ $this->defaults['taglogos'] = $this->getConf('showtaglogos');
+ $this->defaults['footer'] = $this->getConf('showfooter');
+ $this->defaults['redirect'] = $this->getConf('doredirect');
+ $this->defaults['date'] = $this->getConf('showdate');
+ $this->defaults['mdate'] = $this->getConf('showmdate');
+ $this->defaults['user'] = $this->getConf('showuser');
+ $this->defaults['comments'] = $this->getConf('showcomments');
+ $this->defaults['linkbacks'] = $this->getConf('showlinkbacks');
+ $this->defaults['tags'] = $this->getConf('showtags');
+ $this->defaults['link'] = $this->getConf('showlink');
+ $this->defaults['permalink'] = $this->getConf('showpermalink');
+ $this->defaults['indent'] = $this->getConf('doindent');
+ $this->defaults['linkonly'] = $this->getConf('linkonly');
+ $this->defaults['title'] = $this->getConf('title');
+ $this->defaults['pageexists'] = $this->getConf('pageexists');
+ $this->defaults['parlink'] = $this->getConf('parlink');
+ $this->defaults['inline'] = false;
+ $this->defaults['order'] = $this->getConf('order');
+ $this->defaults['rsort'] = $this->getConf('rsort');
+ $this->defaults['depth'] = $this->getConf('depth');
+ $this->defaults['readmore'] = $this->getConf('readmore');
+ }
+
+ /**
+ * Available methods for other plugins
+ */
+ function getMethods() {
+ $result = array();
+ $result[] = array(
+ 'name' => 'get_flags',
+ 'desc' => 'overrides standard values for showfooter and firstseconly settings',
+ 'params' => array('flags' => 'array'),
+ );
+ return $result;
+ }
+
+ /**
+ * Overrides standard values for showfooter and firstseconly settings
+ */
+ function get_flags($setflags) {
+ // load defaults
+ $flags = $this->defaults;
+ foreach ($setflags as $flag) {
+ $value = '';
+ if (strpos($flag, '=') !== false) {
+ list($flag, $value) = explode('=', $flag, 2);
+ }
+ switch ($flag) {
+ case 'footer':
+ $flags['footer'] = 1;
+ break;
+ case 'nofooter':
+ $flags['footer'] = 0;
+ break;
+ case 'firstseconly':
+ case 'firstsectiononly':
+ $flags['firstsec'] = 1;
+ break;
+ case 'fullpage':
+ $flags['firstsec'] = 0;
+ break;
+ case 'showheader':
+ case 'header':
+ $flags['noheader'] = 0;
+ break;
+ case 'noheader':
+ $flags['noheader'] = 1;
+ break;
+ case 'editbtn':
+ case 'editbutton':
+ $flags['editbtn'] = 1;
+ break;
+ case 'noeditbtn':
+ case 'noeditbutton':
+ $flags['editbtn'] = 0;
+ break;
+ case 'permalink':
+ $flags['permalink'] = 1;
+ break;
+ case 'nopermalink':
+ $flags['permalink'] = 0;
+ break;
+ case 'redirect':
+ $flags['redirect'] = 1;
+ break;
+ case 'noredirect':
+ $flags['redirect'] = 0;
+ break;
+ case 'link':
+ $flags['link'] = 1;
+ break;
+ case 'nolink':
+ $flags['link'] = 0;
+ break;
+ case 'user':
+ $flags['user'] = 1;
+ break;
+ case 'nouser':
+ $flags['user'] = 0;
+ break;
+ case 'comments':
+ $flags['comments'] = 1;
+ break;
+ case 'nocomments':
+ $flags['comments'] = 0;
+ break;
+ case 'linkbacks':
+ $flags['linkbacks'] = 1;
+ break;
+ case 'nolinkbacks':
+ $flags['linkbacks'] = 0;
+ break;
+ case 'tags':
+ $flags['tags'] = 1;
+ break;
+ case 'notags':
+ $flags['tags'] = 0;
+ break;
+ case 'date':
+ $flags['date'] = 1;
+ break;
+ case 'nodate':
+ $flags['date'] = 0;
+ break;
+ case 'mdate':
+ $flags['mdate'] = 1;
+ break;
+ case 'nomdate':
+ $flags['mdate'] = 0;
+ break;
+ case 'indent':
+ $flags['indent'] = 1;
+ break;
+ case 'noindent':
+ $flags['indent'] = 0;
+ break;
+ case 'linkonly':
+ $flags['linkonly'] = 1;
+ break;
+ case 'nolinkonly':
+ case 'include_content':
+ $flags['linkonly'] = 0;
+ break;
+ case 'inline':
+ $flags['inline'] = 1;
+ break;
+ case 'title':
+ $flags['title'] = 1;
+ break;
+ case 'notitle':
+ $flags['title'] = 0;
+ break;
+ case 'pageexists':
+ $flags['pageexists'] = 1;
+ break;
+ case 'nopageexists':
+ $flags['pageexists'] = 0;
+ break;
+ case 'existlink':
+ $flags['pageexists'] = 1;
+ $flags['linkonly'] = 1;
+ break;
+ case 'parlink':
+ $flags['parlink'] = 1;
+ break;
+ case 'noparlink':
+ $flags['parlink'] = 0;
+ break;
+ case 'order':
+ $flags['order'] = $value;
+ break;
+ case 'sort':
+ $flags['rsort'] = 0;
+ break;
+ case 'rsort':
+ $flags['rsort'] = 1;
+ break;
+ case 'depth':
+ $flags['depth'] = max(intval($value), 0);
+ break;
+ case 'beforeeach':
+ $flags['beforeeach'] = $value;
+ break;
+ case 'aftereach':
+ $flags['aftereach'] = $value;
+ break;
+ case 'readmore':
+ $flags['readmore'] = 1;
+ break;
+ case 'noreadmore':
+ $flags['readmore'] = 0;
+ break;
+ case 'exclude':
+ $flags['exclude'] = $value;
+ break;
+ }
+ }
+ // the include_content URL parameter overrides flags
+ if (isset($_REQUEST['include_content']))
+ $flags['linkonly'] = 0;
+ return $flags;
+ }
+
+ /**
+ * Returns the converted instructions of a give page/section
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ * @author Michael Hamann <michael@content-space.de>
+ */
+ function _get_instructions($page, $sect, $mode, $lvl, $flags, $root_id = null, $included_pages = array()) {
+ $key = ($sect) ? $page . '#' . $sect : $page;
+ $this->includes[$key] = true; // legacy code for keeping compatibility with other plugins
+
+ // keep compatibility with other plugins that don't know the $root_id parameter
+ if (is_null($root_id)) {
+ global $ID;
+ $root_id = $ID;
+ }
+
+ if ($flags['linkonly']) {
+ if (page_exists($page) || $flags['pageexists'] == 0) {
+ $title = '';
+ if ($flags['title'])
+ $title = p_get_first_heading($page);
+ if($flags['parlink']) {
+ $ins = array(
+ array('p_open', array()),
+ array('internallink', array(':'.$key, $title)),
+ array('p_close', array()),
+ );
+ } else {
+ $ins = array(array('internallink', array(':'.$key,$title)));
+ }
+ }else {
+ $ins = array();
+ }
+ } else {
+ if (page_exists($page)) {
+ global $ID;
+ $backupID = $ID;
+ $ID = $page; // Change the global $ID as otherwise plugins like the discussion plugin will save data for the wrong page
+ $ins = p_cached_instructions(wikiFN($page), false, $page);
+ $ID = $backupID;
+ } else {
+ $ins = array();
+ }
+
+ $this->_convert_instructions($ins, $lvl, $page, $sect, $flags, $root_id, $included_pages);
+ }
+ return $ins;
+ }
+
+ /**
+ * Converts instructions of the included page
+ *
+ * The funcion iterates over the given list of instructions and generates
+ * an index of header and section indicies. It also removes document
+ * start/end instructions, converts links, and removes unwanted
+ * instructions like tags, comments, linkbacks.
+ *
+ * Later all header/section levels are convertet to match the current
+ * inclusion level.
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+ function _convert_instructions(&$ins, $lvl, $page, $sect, $flags, $root_id, $included_pages = array()) {
+ global $conf;
+
+ // filter instructions if needed
+ if(!empty($sect)) {
+ $this->_get_section($ins, $sect); // section required
+ }
+
+ if($flags['firstsec']) {
+ $this->_get_firstsec($ins, $page, $flags); // only first section
+ }
+
+ $ns = getNS($page);
+ $num = count($ins);
+
+ $conv_idx = array(); // conversion index
+ $lvl_max = false; // max level
+ $first_header = -1;
+ $no_header = false;
+ $sect_title = false;
+ $endpos = null; // end position of the raw wiki text
+
+ $this->adapt_links($ins, $page, $included_pages);
+
+ for($i=0; $i<$num; $i++) {
+ switch($ins[$i][0]) {
+ case 'document_start':
+ case 'document_end':
+ case 'section_edit':
+ unset($ins[$i]);
+ break;
+ case 'header':
+ // get section title of first section
+ if($sect && !$sect_title) {
+ $sect_title = $ins[$i][1][0];
+ }
+ // check if we need to skip the first header
+ if((!$no_header) && $flags['noheader']) {
+ $no_header = true;
+ }
+
+ $conv_idx[] = $i;
+ // get index of first header
+ if($first_header == -1) $first_header = $i;
+ // get max level of this instructions set
+ if(!$lvl_max || ($ins[$i][1][1] < $lvl_max)) {
+ $lvl_max = $ins[$i][1][1];
+ }
+ break;
+ case 'section_open':
+ if ($flags['inline'])
+ unset($ins[$i]);
+ else
+ $conv_idx[] = $i;
+ break;
+ case 'section_close':
+ if ($flags['inline'])
+ unset($ins[$i]);
+ break;
+ case 'nest':
+ $this->adapt_links($ins[$i][1][0], $page, $included_pages);
+ break;
+ case 'plugin':
+ // FIXME skip other plugins?
+ switch($ins[$i][1][0]) {
+ case 'tag_tag': // skip tags
+ case 'discussion_comments': // skip comments
+ case 'linkback': // skip linkbacks
+ case 'data_entry': // skip data plugin
+ case 'meta': // skip meta plugin
+ case 'indexmenu_tag': // skip indexmenu sort tag
+ case 'include_sorttag': // skip include plugin sort tag
+ unset($ins[$i]);
+ break;
+ // adapt indentation level of nested includes
+ case 'include_include':
+ if (!$flags['inline'] && $flags['indent'])
+ $ins[$i][1][1][4] += $lvl;
+ break;
+ /*
+ * if there is already a closelastsecedit instruction (was added by one of the section
+ * functions), store its position but delete it as it can't be determined yet if it is needed,
+ * i.e. if there is a header which generates a section edit (depends on the levels, level
+ * adjustments, $no_header, ...)
+ */
+ case 'include_closelastsecedit':
+ $endpos = $ins[$i][1][1][0];
+ unset($ins[$i]);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // calculate difference between header/section level and include level
+ $diff = 0;
+ if (!isset($lvl_max)) $lvl_max = 0; // if no level found in target, set to 0
+ $diff = $lvl - $lvl_max + 1;
+ if ($no_header) $diff -= 1; // push up one level if "noheader"
+
+ // convert headers and set footer/permalink
+ $hdr_deleted = false;
+ $has_permalink = false;
+ $footer_lvl = false;
+ $contains_secedit = false;
+ $section_close_at = false;
+ foreach($conv_idx as $idx) {
+ if($ins[$idx][0] == 'header') {
+ if ($section_close_at === false && isset($ins[$idx+1]) && $ins[$idx+1][0] == 'section_open') {
+ // store the index of the first heading that is followed by a new section
+ // the wrap plugin creates sections without section_open so the section shouldn't be closed before them
+ $section_close_at = $idx;
+ }
+
+ if($no_header && !$hdr_deleted) {
+ unset ($ins[$idx]);
+ $hdr_deleted = true;
+ continue;
+ }
+
+ if($flags['indent']) {
+ $lvl_new = (($ins[$idx][1][1] + $diff) > 5) ? 5 : ($ins[$idx][1][1] + $diff);
+ $ins[$idx][1][1] = $lvl_new;
+ }
+
+ if($ins[$idx][1][1] <= $conf['maxseclevel'])
+ $contains_secedit = true;
+
+ // set permalink
+ if($flags['link'] && !$has_permalink && ($idx == $first_header)) {
+ $this->_permalink($ins[$idx], $page, $sect, $flags);
+ $has_permalink = true;
+ }
+
+ // set footer level
+ if(!$footer_lvl && ($idx == $first_header) && !$no_header) {
+ if($flags['indent'] && isset($lvl_new)) {
+ $footer_lvl = $lvl_new;
+ } else {
+ $footer_lvl = $lvl_max;
+ }
+ }
+ } else {
+ // it's a section
+ if($flags['indent']) {
+ $lvl_new = (($ins[$idx][1][0] + $diff) > 5) ? 5 : ($ins[$idx][1][0] + $diff);
+ $ins[$idx][1][0] = $lvl_new;
+ }
+
+ // check if noheader is used and set the footer level to the first section
+ if($no_header && !$footer_lvl) {
+ if($flags['indent'] && isset($lvl_new)) {
+ $footer_lvl = $lvl_new;
+ } else {
+ $footer_lvl = $lvl_max;
+ }
+ }
+ }
+ }
+
+ // close last open section of the included page if there is any
+ if ($contains_secedit) {
+ array_push($ins, array('plugin', array('include_closelastsecedit', array($endpos))));
+ }
+
+ $include_secid = (isset($flags['include_secid']) ? $flags['include_secid'] : NULL);
+
+ // add edit button
+ if($flags['editbtn']) {
+ $this->_editbtn($ins, $page, $sect, $sect_title, ($flags['redirect'] ? $root_id : false), $include_secid);
+ }
+
+ // add footer
+ if($flags['footer']) {
+ $ins[] = $this->_footer($page, $sect, $sect_title, $flags, $footer_lvl, $root_id);
+ }
+
+ // wrap content at the beginning of the include that is not in a section in a section
+ if ($lvl > 0 && $section_close_at !== 0 && $flags['indent'] && !$flags['inline']) {
+ if ($section_close_at === false) {
+ $ins[] = array('section_close', array());
+ array_unshift($ins, array('section_open', array($lvl)));
+ } else {
+ $section_close_idx = array_search($section_close_at, array_keys($ins));
+ if ($section_close_idx > 0) {
+ $before_ins = array_slice($ins, 0, $section_close_idx);
+ $after_ins = array_slice($ins, $section_close_idx);
+ $ins = array_merge($before_ins, array(array('section_close', array())), $after_ins);
+ array_unshift($ins, array('section_open', array($lvl)));
+ }
+ }
+ }
+
+ // add instructions entry wrapper
+ array_unshift($ins, array('plugin', array('include_wrap', array('open', $page, $flags['redirect'], $include_secid))));
+ if (isset($flags['beforeeach']))
+ array_unshift($ins, array('entity', array($flags['beforeeach'])));
+ array_push($ins, array('plugin', array('include_wrap', array('close'))));
+ if (isset($flags['aftereach']))
+ array_push($ins, array('entity', array($flags['aftereach'])));
+
+ // close previous section if any and re-open after inclusion
+ if($lvl != 0 && $this->sec_close && !$flags['inline']) {
+ array_unshift($ins, array('section_close', array()));
+ $ins[] = array('section_open', array($lvl));
+ }
+ }
+
+ /**
+ * Appends instruction item for the include plugin footer
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+ function _footer($page, $sect, $sect_title, $flags, $footer_lvl, $root_id) {
+ $footer = array();
+ $footer[0] = 'plugin';
+ $footer[1] = array('include_footer', array($page, $sect, $sect_title, $flags, $root_id, $footer_lvl));
+ return $footer;
+ }
+
+ /**
+ * Appends instruction item for an edit button
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+ function _editbtn(&$ins, $page, $sect, $sect_title, $root_id, $hid = '') {
+ $title = ($sect) ? $sect_title : $page;
+ $editbtn = array();
+ $editbtn[0] = 'plugin';
+ $editbtn[1] = array('include_editbtn', array($title, $hid));
+ $ins[] = $editbtn;
+ }
+
+ /**
+ * Convert instruction item for a permalink header
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+ function _permalink(&$ins, $page, $sect, $flags) {
+ $ins[0] = 'plugin';
+ $ins[1] = array('include_header', array($ins[1][0], $ins[1][1], $ins[1][2], $page, $sect, $flags));
+ }
+
+ /**
+ * Convert internal and local links depending on the included pages
+ *
+ * @param array $ins The instructions that shall be adapted
+ * @param string $page The included page
+ * @param array $included_pages The array of pages that are included
+ */
+ private function adapt_links(&$ins, $page, $included_pages = null) {
+ $num = count($ins);
+ $ns = getNS($page);
+
+ for($i=0; $i<$num; $i++) {
+ // adjust links with image titles
+ if (strpos($ins[$i][0], 'link') !== false && isset($ins[$i][1][1]) && is_array($ins[$i][1][1]) && $ins[$i][1][1]['type'] == 'internalmedia') {
+ // resolve relative ids, but without cleaning in order to preserve the name
+ $media_id = resolve_id($ns, $ins[$i][1][1]['src']);
+ // make sure that after resolving the link again it will be the same link
+ if ($media_id[0] != ':') $media_id = ':'.$media_id;
+ $ins[$i][1][1]['src'] = $media_id;
+ }
+ switch($ins[$i][0]) {
+ case 'internallink':
+ case 'internalmedia':
+ // make sure parameters aren't touched
+ $link_params = '';
+ $link_id = $ins[$i][1][0];
+ $link_parts = explode('?', $link_id, 2);
+ if (count($link_parts) === 2) {
+ $link_id = $link_parts[0];
+ $link_params = $link_parts[1];
+ }
+ // resolve the id without cleaning it
+ $link_id = resolve_id($ns, $link_id, false);
+ // this id is internal (i.e. absolute) now, add ':' to make resolve_id work again
+ if ($link_id[0] != ':') $link_id = ':'.$link_id;
+ // restore parameters
+ $ins[$i][1][0] = ($link_params != '') ? $link_id.'?'.$link_params : $link_id;
+
+ if ($ins[$i][0] == 'internallink' && !empty($included_pages)) {
+ // change links to included pages into local links
+ // only adapt links without parameters
+ $link_id = $ins[$i][1][0];
+ $link_parts = explode('?', $link_id, 2);
+ if (count($link_parts) === 1) {
+ $exists = false;
+ resolve_pageid($ns, $link_id, $exists);
+
+ $link_parts = explode('#', $link_id, 2);
+ $hash = '';
+ if (count($link_parts) === 2) {
+ list($link_id, $hash) = $link_parts;
+ }
+ if (array_key_exists($link_id, $included_pages)) {
+ if ($hash) {
+ // hopefully the hash is also unique in the including page (otherwise this might be the wrong link target)
+ $ins[$i][0] = 'locallink';
+ $ins[$i][1][0] = $hash;
+ } else {
+ // the include section ids are different from normal section ids (so they won't conflict) but this
+ // also means that the normal locallink function can't be used
+ $ins[$i][0] = 'plugin';
+ $ins[$i][1] = array('include_locallink', array($included_pages[$link_id]['hid'], $ins[$i][1][1], $ins[$i][1][0]));
+ }
+ }
+ }
+ }
+ break;
+ case 'locallink':
+ /* Convert local links to internal links if the page hasn't been fully included */
+ if ($included_pages == null || !array_key_exists($page, $included_pages)) {
+ $ins[$i][0] = 'internallink';
+ $ins[$i][1][0] = ':'.$page.'#'.$ins[$i][1][0];
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get a section including its subsections
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+ function _get_section(&$ins, $sect) {
+ $num = count($ins);
+ $offset = false;
+ $lvl = false;
+ $end = false;
+ $endpos = null; // end position in the input text, needed for section edit buttons
+
+ $check = array(); // used for sectionID() in order to get the same ids as the xhtml renderer
+
+ for($i=0; $i<$num; $i++) {
+ if ($ins[$i][0] == 'header') {
+
+ // found the right header
+ if (sectionID($ins[$i][1][0], $check) == $sect) {
+ $offset = $i;
+ $lvl = $ins[$i][1][1];
+ } elseif ($offset && $lvl && ($ins[$i][1][1] <= $lvl)) {
+ $end = $i - $offset;
+ $endpos = $ins[$i][1][2]; // the position directly after the found section, needed for the section edit button
+ break;
+ }
+ }
+ }
+ $offset = $offset ? $offset : 0;
+ $end = $end ? $end : ($num - 1);
+ if(is_array($ins)) {
+ $ins = array_slice($ins, $offset, $end);
+ // store the end position in the include_closelastsecedit instruction so it can generate a matching button
+ $ins[] = array('plugin', array('include_closelastsecedit', array($endpos)));
+ }
+ }
+
+ /**
+ * Only display the first section of a page and a readmore link
+ *
+ * @author Michael Klier <chi@chimeric.de>
+ */
+ function _get_firstsec(&$ins, $page, $flags) {
+ $num = count($ins);
+ $first_sect = false;
+ $endpos = null; // end position in the input text
+ for($i=0; $i<$num; $i++) {
+ if($ins[$i][0] == 'section_close') {
+ $first_sect = $i;
+ }
+ if ($ins[$i][0] == 'header') {
+ /*
+ * Store the position of the last header that is encountered. As section_close/open-instruction are
+ * always (unless some plugin modifies this) around a header instruction this means that the last
+ * position that is stored here is exactly the position of the section_close/open at which the content
+ * is truncated.
+ */
+ $endpos = $ins[$i][1][2];
+ }
+ // only truncate the content and add the read more link when there is really
+ // more than that first section
+ if(($first_sect) && ($ins[$i][0] == 'section_open')) {
+ $ins = array_slice($ins, 0, $first_sect);
+ if ($flags['readmore']) {
+ $ins[] = array('plugin', array('include_readmore', array($page)));
+ }
+ $ins[] = array('section_close', array());
+ // store the end position in the include_closelastsecedit instruction so it can generate a matching button
+ $ins[] = array('plugin', array('include_closelastsecedit', array($endpos)));
+ return;
+ }
+ }
+ }
+
+ /**
+ * Gives a list of pages for a given include statement
+ *
+ * @author Michael Hamann <michael@content-space.de>
+ */
+ function _get_included_pages($mode, $page, $sect, $parent_id, $flags) {
+ global $conf;
+ $pages = array();
+ switch($mode) {
+ case 'namespace':
+ $page = cleanID($page);
+ $ns = utf8_encodeFN(str_replace(':', '/', $page));
+ // depth is absolute depth, not relative depth, but 0 has a special meaning.
+ $depth = $flags['depth'] ? $flags['depth'] + substr_count($page, ':') + ($page ? 1 : 0) : 0;
+ search($pagearrays, $conf['datadir'], 'search_allpages', array('depth' => $depth, 'skipacl' => false), $ns);
+ if (is_array($pagearrays)) {
+ foreach ($pagearrays as $pagearray) {
+ if (!isHiddenPage($pagearray['id'])) // skip hidden pages
+ $pages[] = $pagearray['id'];
+ }
+ }
+ break;
+ case 'tagtopic':
+ if (!$this->taghelper)
+ $this->taghelper = plugin_load('helper', 'tag');
+ if(!$this->taghelper) {
+ msg('You have to install the tag plugin to use this functionality!', -1);
+ return array();
+ }
+ $tag = $page;
+ $sect = '';
+ $pagearrays = $this->taghelper->getTopic('', null, $tag);
+ foreach ($pagearrays as $pagearray) {
+ $pages[] = $pagearray['id'];
+ }
+ break;
+ default:
+ $page = $this->_apply_macro($page, $parent_id);
+ resolve_pageid(getNS($parent_id), $page, $exists); // resolve shortcuts and clean ID
+ if (auth_quickaclcheck($page) >= AUTH_READ)
+ $pages[] = $page;
+ }
+
+ if (isset($flags['exclude']))
+ $pages = array_filter($pages, function ($page) use ($flags) {
+ if (@preg_match($flags['exclude'], $page))
+ return FALSE;
+ return TRUE;
+ });
+
+ if (count($pages) > 1) {
+ if ($flags['order'] === 'id') {
+ if ($flags['rsort']) {
+ usort($pages, array($this, '_r_strnatcasecmp'));
+ } else {
+ natcasesort($pages);
+ }
+ } else {
+ $ordered_pages = array();
+ foreach ($pages as $page) {
+ $key = '';
+ switch ($flags['order']) {
+ case 'title':
+ $key = p_get_first_heading($page);
+ break;
+ case 'created':
+ $key = p_get_metadata($page, 'date created', METADATA_DONT_RENDER);
+ break;
+ case 'modified':
+ $key = p_get_metadata($page, 'date modified', METADATA_DONT_RENDER);
+ break;
+ case 'indexmenu':
+ $key = p_get_metadata($page, 'indexmenu_n', METADATA_RENDER_USING_SIMPLE_CACHE);
+ if ($key === null)
+ $key = '';
+ break;
+ case 'custom':
+ $key = p_get_metadata($page, 'include_n', METADATA_RENDER_USING_SIMPLE_CACHE);
+ if ($key === null)
+ $key = '';
+ break;
+ }
+ $key .= '_'.$page;
+ $ordered_pages[$key] = $page;
+ }
+ if ($flags['rsort']) {
+ uksort($ordered_pages, array($this, '_r_strnatcasecmp'));
+ } else {
+ uksort($ordered_pages, 'strnatcasecmp');
+ }
+ $pages = $ordered_pages;
+ }
+ }
+
+ $result = array();
+ foreach ($pages as $page) {
+ $exists = page_exists($page);
+ $result[] = array('id' => $page, 'exists' => $exists, 'parent_id' => $parent_id);
+ }
+ return $result;
+ }
+
+ /**
+ * String comparisons using a "natural order" algorithm in reverse order
+ *
+ * @link http://php.net/manual/en/function.strnatcmp.php
+ * @param string $a First string
+ * @param string $b Second string
+ * @return int Similar to other string comparison functions, this one returns &lt; 0 if
+ * str1 is greater than str2; &gt;
+ * 0 if str1 is lesser than
+ * str2, and 0 if they are equal.
+ */
+ function _r_strnatcasecmp($a, $b) {
+ return strnatcasecmp($b, $a);
+ }
+
+ /**
+ * This function generates the list of all included pages from a list of metadata
+ * instructions.
+ */
+ function _get_included_pages_from_meta_instructions($instructions) {
+ $pages = array();
+ foreach ($instructions as $instruction) {
+ $mode = $instruction['mode'];
+ $page = $instruction['page'];
+ $sect = $instruction['sect'];
+ $parent_id = $instruction['parent_id'];
+ $flags = $instruction['flags'];
+ $pages = array_merge($pages, $this->_get_included_pages($mode, $page, $sect, $parent_id, $flags));
+ }
+ return $pages;
+ }
+
+ /**
+ * Get wiki language from "HTTP_ACCEPT_LANGUAGE"
+ * We allow the pattern e.g. "ja,en-US;q=0.7,en;q=0.3"
+ */
+ function _get_language_of_wiki($id, $parent_id) {
+ global $conf;
+ $result = $conf['lang'];
+ if(strpos($id, '@BROWSER_LANG@') !== false){
+ $brlangp = "/([a-zA-Z]{1,8}(-[a-zA-Z]{1,8})*|\*)(;q=(0(.[0-9]{0,3})?|1(.0{0,3})?))?/";
+ if(preg_match_all(
+ $brlangp, $_SERVER["HTTP_ACCEPT_LANGUAGE"],
+ $matches, PREG_SET_ORDER
+ )){
+ $langs = array();
+ foreach($matches as $match){
+ $langname = $match[1] == '*' ? $conf['lang'] : $match[1];
+ $qvalue = $match[4] == '' ? 1.0 : $match[4];
+ $langs[$langname] = $qvalue;
+ }
+ arsort($langs);
+ foreach($langs as $lang => $langq){
+ $testpage = $this->_apply_macro(str_replace('@BROWSER_LANG@', $lang, $id), $parent_id);
+ resolve_pageid(getNS($parent_id), $testpage, $exists);
+ if($exists){
+ $result = $lang;
+ break;
+ }
+ }
+ }
+ }
+ return cleanID($result);
+ }
+
+ /**
+ * Makes user or date dependent includes possible
+ */
+ function _apply_macro($id, $parent_id) {
+ global $USERINFO;
+ /* @var Input $INPUT */
+ global $INPUT;
+
+ // The following is basicaly copied from basicinfo() because
+ // this function can be called from within pageinfo() in
+ // p_get_metadata and thus we cannot rely on $INFO being set
+ if($INPUT->server->has('REMOTE_USER')) {
+ $user = $INPUT->server->str('REMOTE_USER');
+ } else {
+ // no registered user - use IP
+ $user = clientIP(true);
+ }
+
+ // Take user's name if possible, login name otherwise
+ if (!empty($USERINFO['name'])) {
+ $name = $USERINFO['name'];
+ } else {
+ $name = $user;
+ }
+
+ // Take first group if possible
+ if (!empty($USERINFO['grps'])) {
+ $group = $USERINFO['grps'][0];
+ } else {
+ $group = 'ALL';
+ }
+
+ $time_stamp = time();
+ if(preg_match('/@DATE(\w+)@/',$id,$matches)) {
+ switch($matches[1]) {
+ case 'PMONTH':
+ $time_stamp = strtotime("-1 month");
+ break;
+ case 'NMONTH':
+ $time_stamp = strtotime("+1 month");
+ break;
+ case 'NWEEK':
+ $time_stamp = strtotime("+1 week");
+ break;
+ case 'PWEEK':
+ $time_stamp = strtotime("-1 week");
+ break;
+ case 'TOMORROW':
+ $time_stamp = strtotime("+1 day");
+ break;
+ case 'YESTERDAY':
+ $time_stamp = strtotime("-1 day");
+ break;
+ case 'NYEAR':
+ $time_stamp = strtotime("+1 year");
+ break;
+ case 'PYEAR':
+ $time_stamp = strtotime("-1 year");
+ break;
+ }
+ $id = preg_replace('/@DATE(\w+)@/','', $id);
+ }
+
+ $replace = array(
+ '@USER@' => cleanID($user),
+ '@NAME@' => cleanID($name),
+ '@GROUP@' => cleanID($group),
+ '@BROWSER_LANG@' => $this->_get_language_of_wiki($id, $parent_id),
+ '@YEAR@' => date('Y',$time_stamp),
+ '@MONTH@' => date('m',$time_stamp),
+ '@WEEK@' => date('W',$time_stamp),
+ '@DAY@' => date('d',$time_stamp),
+ '@YEARPMONTH@' => date('Ym',strtotime("-1 month")),
+ '@PMONTH@' => date('m',strtotime("-1 month")),
+ '@NMONTH@' => date('m',strtotime("+1 month")),
+ '@YEARNMONTH@' => date('Ym',strtotime("+1 month")),
+ '@YEARPWEEK@' => date('YW',strtotime("-1 week")),
+ '@PWEEK@' => date('W',strtotime("-1 week")),
+ '@NWEEK@' => date('W',strtotime("+1 week")),
+ '@YEARNWEEK@' => date('YW',strtotime("+1 week")),
+ );
+ return str_replace(array_keys($replace), array_values($replace), $id);
+ }
+}
+// vim:ts=4:sw=4:et: