* @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 .= "";
}
/**
* 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 .= "";
}
$this->_print($renderer, $val["sub"], $sub_ns, $open_items);
$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;
}
}