* @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @package syntax */ /** * Define the methods used by AcMenu plugin to produce the plugin's output. * * @package syntax_acmenu */ class syntax_plugin_acmenu extends DokuWiki_Syntax_Plugin { /** * Define the syntax types that this plugin applies when founds its token. * * @return string */ public function getType() { return "substition"; } /** * Define how the plugin's output is handled regarding paragraphs. * * Open paragraphs will be closed before plugin's output: *

foo

* *

bar

* * @return string */ public function getPType() { return "block"; } /** * Define the priority used to determine in which order modes are * added: the mode with the lowest sort number will win. * * Since this plugin provides internal links, it is sorted at: * AcMenu plugin < Doku_Parser_Mode_internallink (= 300) * * @return integer */ public function getSort() { return 295; } /** * Define the regular expression needed to match the plugin's syntax. * * The following general syntax is used: * * * @param string $mode * name for the format mode of the final output */ public function connectTo($mode) { $this->Lexer->addSpecialPattern("", $mode, "plugin_acmenu"); } /** * Handle the plugin's syntax matched. * * This function is called every time the parser encounters the * plugin's syntax in order to produce a list of instructions for the * renderer, which will be interpreted later. * * @param string $match * the text matched * @param integer $state * the lexer state * @param integer $pos * the character position of the matched text * @param object $handler * object reference to the Doku_Handler class * @return array $data * the parameters handled by the plugin's syntax */ public function handle($match, $state, $pos, Doku_Handler $handler) { $data = array(); $match = substr($match, 7, -1); // strips "" return $data; } /** * Process the list of instructions that render the final output. * * @param string $mode * name for the format mode of the final output * @param object $renderer * object reference to the Doku_Render class * @param array $data * the parameters handled by the plugin's syntax */ public function render($mode, Doku_Renderer $renderer, $data) { global $INFO; global $conf; // disable the cache $renderer->nocache(); // get the namespace genealogy of the current id // and store it as metadata to be later used in javascript $id = isset($INFO["id"]) ? $INFO["id"] : ""; $sub_ns = $this->_get_sub_ns($id); p_set_metadata($id, array("plugin" => array("plugin_acmenu" => array("sub_ns" => $sub_ns))), false, false); // build the namespace tree structure $ns_acmenu = $this->_get_ns_acmenu($sub_ns); // namespace where belongs $level = 0; $tree = $this->_tree($ns_acmenu, $level); $tree = $this->_sort_ns_pg($tree); // get heading and id of the namespace where belongs $base_id = implode(":", array_filter(array($ns_acmenu, $conf["start"]))); $base_heading = p_get_first_heading($base_id); if (!isset($base_heading)) { $base_heading = $ns_acmenu; } // get cookies $open_items = $this->_get_cookie(); // print the namespace tree structure $renderer->doc .= "
"; $renderer->doc .= "
    "; $renderer->doc .= "
  • "; $renderer->doc .= "
    "; $renderer->doc .= ""; $renderer->internallink($base_id, $base_heading); $renderer->doc .= ""; $renderer->doc .= "
    "; $renderer->doc .= "
      "; $this->_print($renderer, $tree, $sub_ns, $open_items); $renderer->doc .= "
    "; $renderer->doc .= "
  • "; $renderer->doc .= "
"; $renderer->doc .= "
"; } /** * Get cookies. * * @return array $open_items * the id of the pages to keep open in the form: * array { * [i] => (str) "::..::" * } */ private function _get_cookie() { $open_items = array(); if (array_key_exists("plugin_acmenu_open_items", $_COOKIE)) { $open_items = json_decode($_COOKIE["plugin_acmenu_open_items"]); } return $open_items; } /** * Get the parent namespace where belongs. * * Start from the current namespace (the namespace of the current page) * and go up till the base namespace (the namespace where belongs). * * @param array $sub_ns * the namespace genealogy of the current page id, in the form: * array { * [0] => (str) "::...:" * ... * [i-1] => (str) "" * [i] => (str) "" * } * @return string $ns_acmenu * the parent namespace where belongs, in the form: * */ private function _get_ns_acmenu($sub_ns) { global $INFO; global $conf; $dir = realpath($conf["savedir"] . "/pages"); $ns_acmenu = ""; if (!empty($INFO["namespace"])) { foreach ($sub_ns as $ns) { $sidebar = implode("/", array_filter(array(str_replace(":", "/", $ns), $conf["sidebar"]))); if (file_exists($dir . "/" . $sidebar . ".txt")) { $ns_acmenu = $ns; break; } } } return $ns_acmenu; } /** * Build the namespace tree structure. * * Start from the base namespace (the namespace where belongs) * and go down until the end. * * @param string $ns_acmenu * the parent namespace where belongs, in the form: * * @param string $level * the indentation level from which start to build the tree structure * @return array $tree * the namespace tree, in the form: * array { * [0] => array { * ["heading"] => (str) "" * ["id"] => (str) "" * ["level"] => (int) "" * ["type"] => (str) "ns" * ["sub"] => array { * [0] => array { * ["heading"] => (str) "" * ["id"] => (str) "" * ["level"] => (int) "" * ["type"] => (str) "pg" || "ext_ns" * } * [i] => array {...} * } * } * [i] => array {...} * } * where: * ["type"] = "ns" means "namespace" * ["type"] = "pg" means "page" * ["type"] = "ext_ns" means "external namespace" * so that only namespaces can have ["sub"] namespaces * and external namespaces are treated as pages */ private function _tree($ns_acmenu, $level) { global $conf; $tree = array(); $level = $level + 1; $dir = $conf["savedir"] ."/pages/" . str_replace(":", "/", $ns_acmenu); $files = array_diff(scandir($dir), array("..", ".")); foreach ($files as $file) { if (is_file($dir . "/" . $file)) { $pg_name = cleanID(utf8_decodeFN(substr_replace($file, "", -strlen(".txt")))); $id = implode(":", array_filter(array($ns_acmenu, $pg_name), "strlen")); if (!isHiddenPage($id)) { if (auth_quickaclcheck($id) >= AUTH_READ) { $heading = $pg_name; if (useheading("navigation") && $pg_name != $conf["start"]) { $heading = p_get_first_heading($id); } $tree[] = array("heading" => $heading, "id" => $id, "level" => $level, "type" => "pg"); } } } else { $id = implode(":", array_filter(array($ns_acmenu, $file, $conf["start"]), "strlen")); if ($conf["sneaky_index"] && auth_quickaclcheck($id) < AUTH_READ) { continue; } else { $heading = $file; if (useheading("navigation")) { $heading = p_get_first_heading($id); } if (file_exists($dir . "/" . $file . "/" . $conf["sidebar"] . ".txt")) { // subnamespace with sidebar (external namespace) will not be scanned $tree[] = array("heading" => $heading, "id" => $id, "level" => $level, "type" => "ext_ns"); continue; } else { $tree[] = array("heading" => $heading, "id" => $id, "level" => $level, "type" => "ns", "sub" => $this->_tree(implode(":", array_filter(array($ns_acmenu, $file), "strlen")), $level)); } } } } return $tree; } /** * Get the namespace genealogy of the given id. * * @param string $id * the current page id, in the form: * ::...:: * @return array $sub_ns * the namespace genealogy of the current page id, in the form: * array { * [0] => (str) "::...:" * ... * [i-1] => (str) "" * [i] => (str) "" * } */ private function _get_sub_ns($id) { $sub_ns = array(); $pieces = explode(":", $id); array_pop($pieces); // remove $cp_pieces = $pieces; foreach ($pieces as $key => $val) { $sub_ns[] = implode(":", $cp_pieces); array_pop($cp_pieces); } $sub_ns[] = ""; return $sub_ns; } /** * Print the namespace tree structure. * * @param object $renderer * object reference to the Doku_Render class * @param array $tree * the namespace tree, in the form: * array { * [0] => array { * ["heading"] => (str) "" * ["id"] => (str) "" * ["level"] => (int) "" * ["type"] => (str) "ns" * ["sub"] => array { * [0] => array { * ["heading"] => (str) "" * ["id"] => (str) "" * ["level"] => (int) "" * ["type"] => (str) "pg" || "ext_ns" * } * [i] => array {...} * } * } * [i] => array {...} * } * where: * ["type"] = "ns" means "namespace" * ["type"] = "pg" means "page" * ["type"] = "ext_ns" means "external namespace" * so that only namespaces can have ["sub"] namespaces * and external namespaces are treated as pages * @param array $sub_ns * the namespace genealogy of the current page id, in the form: * array { * [0] => (str) "::...:" * ... * [i-1] => (str) "" * [i] => (str) "" * } * @param array $open_items * the namespaces to keep open, in the form: * array { * [i] => (str) "::...:" * } */ private function _print($renderer, $tree, $sub_ns, $open_items) { global $conf; foreach ($tree as $key => $val) { if ($val["type"] == "pg") { $renderer->doc .= "
  • "; $renderer->doc .= "
    "; $renderer->internallink($val["id"], $val["heading"]); $renderer->doc .= "
    "; $renderer->doc .= "
  • "; } elseif ($val["type"] == "ext_ns") { $renderer->doc .= "
  • "; $renderer->doc .= "
    "; $renderer->internallink($val["id"], $val["heading"]); $renderer->doc .= "
    "; $renderer->doc .= "
  • "; } elseif ($val["type"] == "ns") { if (in_array(substr($val["id"], 0, -strlen(":" . $conf["start"])), $sub_ns) || in_array($val["id"], $open_items)) { $renderer->doc .= "
  • "; } else { $renderer->doc .= "
  • "; } $renderer->doc .= "
    "; if (in_array(substr($val["id"], 0, -strlen(":" . $conf["start"])), $sub_ns)) { $renderer->doc .= ""; $renderer->internallink($val["id"], $val["heading"]); $renderer->doc .= ""; } else { $renderer->internallink($val["id"], $val["heading"]); } $renderer->doc .= "
    "; if (in_array(substr($val["id"], 0, -strlen(":" . $conf["start"])), $sub_ns) || in_array($val["id"], $open_items)) { $renderer->doc .= "
      "; } else { $renderer->doc .= ""; $renderer->doc .= ""; } } } /** * Sort the tree namespace in ascending order. * * The tree is sorted in this order: * 1) namespaces; * 2) pages. * * @param array $tree * the namespace tree, in the form: * array { * [0] => array { * ["heading"] => (str) "" * ["id"] => (str) "" * ["level"] => (int) "" * ["type"] => (str) "ns" * ["sub"] => array { * [0] => array { * ["heading"] => (str) "" * ["id"] => (str) "" * ["level"] => (int) "" * ["type"] => (str) "pg" || "ext_ns" * } * [i] => array {...} * } * } * [i] => array {...} * } * where: * ["type"] = "ns" means "namespace" * ["type"] = "pg" means "page" * ["type"] = "ext_ns" means "external namespace" * so that only namespaces can have ["sub"] namespaces * and external namespaces are treated as pages * @return array $tree * the tree namespace sorted */ private function _sort_ns_pg($tree) { global $conf; $ns = array(); $pg = array(); foreach ($tree as $key => $val) { if ($val["type"] == "ns") { $val["sub"] = $this->_sort_ns_pg($val["sub"]); $ns[] = $val; } else { $pg[] = $val; } } sort($ns); sort($pg); $tree = array_merge($ns, $pg); foreach ($tree as $key => $array_val) { if ($array_val["heading"] == $conf["start"]) { unset($tree[$key]); array_unshift($tree, $array_val); } } return $tree; } }