1<?php
2/**
3 * Helper Component for the TwistieNav Plugin
4 *
5 * @author   Simon DELAGE <sdelage@gmail.com>
6 * @license: CC Attribution-Share Alike 3.0 Unported <http://creativecommons.org/licenses/by-sa/3.0/>
7 */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class helper_plugin_twistienav extends DokuWiki_Plugin {
13
14    protected $title_metadata   = array();
15    protected $exclusions       = array();
16    protected $nsignore         = array();
17
18    function build_titlemetafields() {
19        // Known plugins that set title and corresponding metadata keys
20        $this->title_metadata = array(
21            'croissant' => 'plugin_croissant_bctitle',
22            'pagetitle' => 'shorttitle',
23        );
24        foreach (array_keys($this->title_metadata) as $plugin) {
25            if(plugin_isdisabled($plugin)) unset($this->title_metadata[$plugin]);
26        }
27        $this->title_metadata['dokuwiki'] = 'title';
28        return $this->title_metadata;
29    }
30
31    function build_exclusions() {
32        global $conf;
33
34        // Convert "exclusions" config setting to array
35        foreach (explode(',', $this->getConf('exclusions')) as $exclusion) {
36            if (substr($exclusion, 0, 1) === '@') {
37                $this->nsignore[] = ltrim($exclusion, "@");
38            } else {
39                switch ($exclusion) {   // care pre-defined keys in multicheckbox
40                    case 'start':
41                        $this->exclusions[] = $conf['start'];
42                        break;
43                    case 'sidebar':
44                        $this->exclusions[] = $conf['sidebar'];
45                        break;
46                    default:
47                        $this->exclusions[] = $exclusion;
48                }
49            }
50        }
51        return array($this->exclusions, $this->nsignore);
52    }
53
54    /**
55     * Build a namespace index (list sub-namespaces and pages).
56     *
57     * @param (str)     $idx namespace ID, must not be a page ID.
58     *                  Could be provided with : cleanID(getNS($ID))
59     * @param (bool)    $useexclusions use `exclusions` setting or not
60     * @param (bool)    $split return a simple level or more complex array
61     * @return (arr)    list of sub namespaces and pages found within $idx namespace
62     *
63     * See https://www.dokuwiki.org/plugin:twistienav?do=draft#helper_component for details
64     *
65     */
66    function get_idx_data($idx = null, $useexclusions = true, $split = false) {
67        global $conf, $ID;
68        // From an ajax call (ie. a click on a TwistieNav), $ID value isn't available so we need to get it from another way
69        if ($ID == null) {
70            $ajaxId = ltrim(explode("id=", $_SERVER["HTTP_REFERER"])[1], ":");
71        } else {
72            $ajaxId = null;
73        }
74
75        $dir  = utf8_encodeFN(str_replace(':','/',$idx));
76        $data = array();
77        search($data,$conf['datadir'],'search_index',array('ns' => $idx),$dir);
78
79        if (count($data) != 0) {
80            foreach ($data as $datakey => $item) {
81                // Unset item if is in 'exclusions'
82                if (($useexclusions) && (in_array(noNS($item['id']), $this->exclusions))) {
83                    unset($data[$datakey]);
84                    continue;
85                // Unset item if it is in 'nsignore'
86                } elseif (($useexclusions) && (in_array(explode(":", $item['id'])[0], $this->nsignore))) {
87                    unset($data[$datakey]);
88                    continue;
89                // Unset item if it starts with "playground" or is equal to current $ID
90                } elseif ((explode(":", $item['id'])[0] == "playground") or ($item['id'] == $ID) or ($item['id'] == $ajaxId)) {
91                    unset($data[$datakey]);
92                    continue;
93                }
94                // If item is a directory, we need an ID that points to that namespace's start page (even if it doesn't exist)
95                if ($item['type'] == 'd') {
96                    $target = $item['id'].':'.$conf['start'];
97                    $classes = "is_ns ";
98                // Or just keep current item ID
99                } else {
100                    $target = $item['id'];
101                    $classes = "is_page ";
102                }
103                // Add (non-)existence class
104                if (page_exists($target)) {
105                    $classes .= "wikilink1";
106                } else {
107                    $classes .= "wikilink2";
108                }
109                // Get page title from metadata
110                $title = null;
111                if ($this->getConf('useheading')) {
112                    foreach ($this->title_metadata as $plugin => $pluginkey) {
113                        $title = p_get_metadata($target, $pluginkey, METADATA_DONT_RENDER);
114                        if ($title != null) break;
115                    }
116                }
117                $data[$datakey]['id'] = $target;
118                $title = @$title ?: hsc(noNS($item['id']));
119                // Store a link to the page in the data that will be sent back
120                $data[$datakey]['link'] = '<a href="'.wl($target).'" class="'.$classes.'">'.$title.'</a>';
121            }
122        }
123        if ($split) {
124            $result = array();
125            $result['namespaces'] = array_values(array_filter($data, function ($row) {
126                return $row["type"] == "d";
127            }));
128            $result['pages'] = array_values(array_filter($data, function ($row) {
129                return $row["type"] == "f";
130            }));
131            return $result;
132        } else {
133            return array_values($data);
134        }
135    }
136
137}
138