1<?php
2/**
3 * DokuWiki Plugin tagfilter (Action Component)
4 *
5 * inserts a button into the toolbar
6 *
7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @author  lisps
9 */
10
11class action_plugin_tagfilter extends DokuWiki_Action_Plugin
12{
13    /**
14     * Register the eventhandlers
15     */
16    public function register(Doku_Event_Handler $controller)
17    {
18        $controller->register_hook('TOOLBAR_DEFINE', 'AFTER', $this, 'insert_button', array());
19        $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, '_addparams');
20        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, '_ajax_call');
21    }
22
23    public function _addparams($event, $param)
24    {
25        global $JSINFO;
26        global $INPUT;
27        // filter for ft* in GET
28        $f = function ($value) {
29            return strpos($value, "tf") === 0;
30        };
31        $get_tagfilter = array_filter(array_keys($_GET), $f);
32
33        //filter for ft<key>_<label> and add it to JSINFO to select it via JavaScript
34        foreach ($get_tagfilter as $param) {
35            $ret = preg_match('/^tf(\d+)\_([\w\:äöü\-\/#]+)/', $param, $matches);
36
37            if ($ret && is_numeric($matches[1]) && $matches[2]) {
38                $JSINFO['tagfilter'][] = [
39                    'key' => $matches[1],
40                    'label' => $matches[2],
41                    'values' => $INPUT->str($param) ? [$INPUT->str($param)] : $INPUT->arr($param)
42                ];
43            }
44        }
45
46    }
47
48    /**
49     * Inserts the toolbar button
50     */
51    public function insert_button($event, $param)
52    {
53        $event->data[] = [
54            'type' => 'format',
55            'title' => 'Tagfilter plugin',
56            'icon' => '../../plugins/tagfilter/tagfilter.png',
57            'sample' => '<namespace>? <Label>=<TagAusdruck>=<Tags Selected>|... &<pagelistoptions (&multi&nouser&chosen)>',
58            'open' => '{{tagfilter>',
59            'close' => '}}',
60            'insert' => '',
61        ];
62    }
63
64    /**
65     * ajax Request Handler
66     */
67    public function _ajax_call($event, $param)
68    {
69        if ($event->data !== 'plugin_tagfilter') {
70            return;
71        }
72        //no other ajax call handlers needed
73        $event->stopPropagation();
74        $event->preventDefault();
75
76        global $INPUT;
77        global $lang;
78
79        //Variables
80        $tagfilterFormId = $INPUT->int('id');
81        $form = $this->getJSONdecodedUrlParameter('form');
82        $ns = $this->getJSONdecodedUrlParameter('ns');
83        $flags = (array)$this->getJSONdecodedUrlParameter('flags');
84        //print_r($flags);
85        $tagfilterFlags = isset($flags[1]) ? (array)$flags[1] : [];  //TODO parse?
86        $pagelistFlags = isset($flags[0]) ? (array)$flags[0] : [];
87        //print_r($tagfilterFlags);
88        $pagesearch = $this->getJSONdecodedUrlParameter('pagesearch');
89
90        //load tagfilter plugin
91        /** @var helper_plugin_tagfilter $Htagfilter */
92        $Htagfilter = $this->loadHelper('tagfilter', false);
93
94        //load tag plugin
95        /** @var helper_plugin_tag $Htag */
96        if (is_null($Htag = $this->loadHelper('tag', false))) {
97            $this->sendResponse(sprintf($Htagfilter->getLang('missing_plugin'), 'tag'));
98            return;
99        }
100        //load tag plugin
101        /** @var helper_plugin_pagelist $Hpagelist */
102        if (is_null($Hpagelist = $this->loadHelper('pagelist', false))) {
103            $this->sendResponse(sprintf($Htagfilter->getLang('missing_plugin'), 'pagelist'));
104            return;
105        }
106
107        $form = array_filter($form);//remove empty entries
108
109        $tag_list_r = [];
110
111        foreach ($form as $item) { //rearrange entries for tag plugin lookup
112            $tag_list_r[] = implode(' ', array_filter($item));
113        }
114
115        $tag_list_r = array_filter($tag_list_r);
116
117        //lookup the pages from the taglist
118        //partially copied from tag->helper with less checks and no meta lookups
119        $page_names = [];
120        foreach ($tag_list_r as $key => $tag_list) {
121            $tags_parsed = $Htag->parseTagList($tag_list, true);
122            $pages_lookup = $Htag->getIndexedPagesMatchingTagQuery($tags_parsed);
123            foreach ($pages_lookup as $page_lookup) {
124                // filter by namespace, root namespace is identified with a dot
125                // root namespace is specified, discard all pages who lay outside the root namespace
126                if (($ns == '.' && getNS($page_lookup) === false)
127                    || ($ns && strpos(':' . getNS($page_lookup) . ':', ':' . $ns . ':') === 0)
128                    || $ns === '') {
129                    if (auth_quickaclcheck($page_lookup) >= AUTH_READ) {
130                        $page_names[$key][] = $page_lookup;
131                    }
132                }
133            }
134        }
135
136
137        //get the first item
138        if (!is_array($pages_intersect = array_shift($page_names))) {
139            $pages_intersect = [];
140        }
141
142        //find the intersections
143        foreach ($page_names as $names) {
144            $pages_intersect = array_intersect($pages_intersect, $names);
145        }
146
147        //intersections with the pagesearch
148        if (is_array($pagesearch) && !empty($pagesearch)) {
149            $pages_intersect = array_intersect($pages_intersect, $pagesearch);
150        }
151
152        //no matching pages
153        if (count($pages_intersect) === 0) {
154            $this->sendResponse('<i>' . $lang['nothingfound'] . '</i>');
155            return;
156        }
157
158        $pages_intersect = array_filter($pages_intersect, [$this, 'filterHide_Template']);
159
160        $pages = [];
161        foreach ($pages_intersect as $page) {
162            $title = p_get_metadata($page, 'title', METADATA_DONT_RENDER);
163            $pages[$title ?: $page] = [
164                'title' => $title ?: $page,
165                'id' => $page
166            ];
167        }
168        if (is_array($pagelistFlags) && in_array('rsort', $pagelistFlags)) {
169            krsort($pages, SORT_STRING | SORT_FLAG_CASE);
170        } else {
171            ksort($pages, SORT_STRING | SORT_FLAG_CASE);
172        }
173
174        if (!isset($tagfilterFlags['tagcolumn'])) {
175            $tagfilterFlags['tagcolumn'] = [];
176        }
177        foreach ($tagfilterFlags['tagcolumn'] as $tagcolumn) {
178            $Hpagelist->addColumn('tagfilter', hsc($tagcolumn));
179        }
180        $Hpagelist->setFlags($pagelistFlags);
181        $Hpagelist->startList();
182
183        foreach ($pages as $page) {
184            $Hpagelist->addPage($page);
185        }
186        $text = $Hpagelist->finishList();
187
188        $this->sendResponse($text);
189    }
190
191    private function getJSONdecodedUrlParameter($name)
192    {
193        global $INPUT;
194
195        $param = null;
196        if ($INPUT->has($name)) {
197            $param = $INPUT->param($name);
198            $param = json_decode($param);
199        }
200        return $param;
201    }
202
203    /**
204     * @param string $text
205     */
206    private function sendResponse($text)
207    {
208        global $INPUT;
209        $tagfilter_id = $INPUT->int('id');
210        echo json_encode(['id' => $tagfilter_id, 'text' => $text]);
211    }
212
213    /**
214     * Filter function
215     * show only pages different to _template and the have to exist
216     *
217     * @param string $val
218     * @return bool
219     */
220    private function filterHide_Template($val)
221    {
222        return strpos($val, "_template") === false && @file_exists(wikiFN($val));
223    }
224
225}
226
227
228
229
230
231