summaryrefslogtreecommitdiff
path: root/platform/www/lib/plugins/tag/syntax/searchtags.php
blob: 67d62b355938c7190a8d8f04b4afd5e9d9a77685 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
<?php
/**
 * Syntax plugin part for displaying a tag search form with results.
 *
 * Usage: {{tagsearch[&flags]}}
 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author Michael Hamann <michael@content-space.de>
 */

/**
 * Tagsearch syntax, displays a tag search form with results similar to the topic syntax
 */
class syntax_plugin_tag_searchtags extends DokuWiki_Syntax_Plugin {
    /**
     * @return string Syntax type
     */
    function getType() { return 'substition'; }

    /**
     * @return string Paragraph type
     */
    function getPType() { return 'block'; }

    /**
     * @return int Sort order
     */
    function getSort() { return 295; }

    /**
     * @param string $mode Parser mode
     */
    function connectTo($mode) {
        $this->Lexer->addSpecialPattern('\{\{searchtags\}\}',$mode,'plugin_tag_searchtags');
        // make sure that flags really start with & and media files starting with "searchtags" still work
        $this->Lexer->addSpecialPattern('\{\{searchtags&.*?\}\}',$mode,'plugin_tag_searchtags');
    }

    /**
     * Handle matches of the searchtags syntax
     *
     * @param string $match The match of the syntax
     * @param int    $state The state of the handler
     * @param int    $pos The position in the document
     * @param Doku_Handler    $handler The handler
     * @return array Data for the renderer
     */
    function handle($match, $state, $pos, Doku_Handler $handler) {
        $flags = substr($match, 10, -2); // strip {{searchtags from start and }} from end
        // remove empty flags by using array_filter (removes elements == false)
        $flags = array_filter(explode('&', $flags));

        return $flags;
    }

    /**
     * Render xhtml output or metadata
     *
     * @param string         $mode      Renderer mode (supported modes: xhtml and metadata)
     * @param Doku_Renderer  $renderer  The renderer
     * @param array          $data      The data from the handler function
     * @return bool If rendering was successful.
     */
    function render($mode, Doku_Renderer $renderer, $data) {
        global $lang;
        $flags = $data;

        if ($mode == 'xhtml') {
            /* @var Doku_Renderer_xhtml $renderer */

            // prevent caching to ensure content is always fresh
            $renderer->nocache();

            /* @var helper_plugin_pagelist $pagelist */
            // let Pagelist Plugin do the work for us
            if ((!$pagelist = $this->loadHelper('pagelist'))) {
                return false;
            }

            // Prepare the flags for the pagelist plugin
            $configflags = explode(',', str_replace(" ", "", $this->getConf('pagelist_flags')));
            $flags = array_merge($configflags, $flags);
            foreach($flags as $key => $flag) {
                if($flag == "")	unset($flags[$key]);
            }

            // print the search form
            $nonsform = in_array('nonsform', $flags);
            $renderer->doc .= $this->getForm($nonsform);

            // get the tag input data
            $tags = $this->getTagSearchString();

            if ($tags != NULL) {
                /* @var helper_plugin_tag $my */
                if ($my = $this->loadHelper('tag')) $pages = $my->getTopic($this->getNS(), '', $tags);

                // Display a message when no pages were found
                if (!isset($pages) || !$pages) {
                    $renderer->p_open();
                    $renderer->cdata($lang['nothingfound']);
                    $renderer->p_close();
                } else {

                    // display the actual search results
                    $pagelist->setFlags($flags);
                    $pagelist->startList();
                    foreach ($pages as $page) {
                        $pagelist->addPage($page);
                    }
                    $renderer->doc .= $pagelist->finishList();
                }
            }

            return true;
        }
        return false;
    }

    /**
     * Return the search form for the namespace and the tag selection
     *
     * @return string the HTML code of the search form
     */
    private function getForm($nonsform=false)  {
        global $conf, $lang;

        if (!$nonsform) {
            // Get the list of all namespaces for the dropdown
            $namespaces = array();
            search($namespaces,$conf['datadir'],'search_namespaces',array());

            // build the list in the form value => label from the namespace search result
            $ns_select = array('' => '');
            foreach ($namespaces as $ns) {
                // only display namespaces the user can access when sneaky index is on
                if ($ns['perm'] > 0 || $conf['sneaky_index'] == 0) {
                    $ns_select[$ns['id']] = $ns['id'];
                }
            }
        }

        $form = new Doku_Form(array('action' => '', 'method' => 'post', 'class' => 'plugin__tag_search'));

        // add a paragraph around the inputs in order to get some margin around the form elements
        $form->addElement(form_makeOpenTag('p'));
        // namespace select
        if (!$nonsform) {
            $form->addElement(form_makeMenuField('plugin__tag_search_namespace', $ns_select, $this->getNS(), $lang['namespaces']));
        }

        // checkbox for AND
        $attr = array();
        if ($this->useAnd()) $attr['checked'] = 'checked';
        $form->addElement(form_makeCheckboxField('plugin__tag_search_and', 1, $this->getLang('use_and'), '', '', $attr));
        $form->addElement(form_makeCloseTag('p'));

        // load the tag list - only tags that actually have pages assigned that the current user can access are listed
        /* @var helper_plugin_tag $my */
        if ($my = $this->loadHelper('tag')) $tags = $my->tagOccurrences(array(), NULL, true);
        // sort tags by name ($tags is in the form $tag => $count)
        ksort($tags);

        // display error message when no tags were found
        if (!isset($tags) || $tags == NULL) {
            $form->addElement(form_makeOpenTag('p'));
            $form->addElement($this->getLang('no_tags'));
            $form->addElement(form_makeCloseTag('p'));
        } else {
            // the tags table
            $form->addElement(form_makeOpenTag('div', array('class' => 'table')));
            $form->addElement(form_makeOpenTag('table', array('class' => 'inline')));
            // print table header
            $form->addElement(form_makeOpenTag('tr'));
            $form->addElement(form_makeOpenTag('th'));
            $form->addElement($this->getLang('include'));
            $form->addElement(form_makeCloseTag('th'));
            $form->addElement(form_makeOpenTag('th'));
            $form->addElement($this->getLang('exclude'));
            $form->addElement(form_makeCloseTag('th'));
            $form->addElement(form_makeOpenTag('th'));
            $form->addElement($this->getLang('tags'));
            $form->addElement(form_makeCloseTag('th'));
            $form->addElement(form_makeCloseTag('tr'));

            // print tag checkboxes
            foreach ($tags as $tag => $count) {
                $form->addElement(form_makeOpenTag('tr'));
                $form->addElement(form_makeOpenTag('td'));
                $attr = array();
                if ($this->isSelected($tag)) $attr['checked'] = 'checked';
                $form->addElement(form_makeCheckboxField('plugin__tag_search_tags[]', $tag, '+', '', 'plus', $attr));
                $form->addElement(form_makeCloseTag('td'));
                $form->addElement(form_makeOpenTag('td'));
                $attr = array();
                if ($this->isSelected('-'.$tag)) $attr['checked'] = 'checked';
                $form->addElement(form_makeCheckboxField('plugin__tag_search_tags[]', '-'.$tag, '-', '', 'minus', $attr));
                $form->addElement(form_makeCloseTag('td'));
                $form->addElement(form_makeOpenTag('td'));
                $form->addElement(hsc($tag).' ['.$count.']');
                $form->addElement(form_makeCloseTag('td'));
                $form->addElement(form_makeCloseTag('tr'));
            }

            $form->addElement(form_makeCloseTag('table'));
            $form->addElement(form_makeCloseTag('div'));

            // submit button (doesn't use the button form element because it always submits an action which is not
            // recognized for $preact in inc/actions.php and thus always causes a redirect)
            $form->addElement(form_makeOpenTag('p'));
            $form->addElement(form_makeTag('input', array('type' => 'submit', 'value' => $lang['btn_search'])));
            $form->addElement(form_makeCloseTag('p'));
        }

        return $form->getForm();
    }

    /**
     * Returns the currently selected namespace
     * @return string the cleaned namespace id
     */
    private function getNS() {
        if (isset($_POST['plugin__tag_search_namespace'])) {
            return cleanID($_POST['plugin__tag_search_namespace']);
        } else {
            return '';
        }
    }

    /**
     * Returns the tag search string from the selected tags
     * @return string|NULL the tag search or NULL when no tags were selected
     */
    private function getTagSearchString() {
        if (isset($_POST['plugin__tag_search_tags']) && is_array($_POST['plugin__tag_search_tags'])) {
            $tags = $_POST['plugin__tag_search_tags'];
            // wWhen and is set, prepend "+" to each tag
            $plus = (isset($_POST['plugin__tag_search_and']) ? '+' : '');
            $positive_tags = '';
            $negative_tags = '';
            foreach ($tags as $tag) {
                $tag = (string)$tag;
                if ($tag[0] == '-') {
                    $negative_tags .= $tag.' ';
                } else {
                    if ($positive_tags === '') {
                        $positive_tags = $tag.' ';
                    } else {
                        $positive_tags .= $plus.$tag.' ';
                    }
                }
            }
            return $positive_tags.$negative_tags;
        } else {
            return NULL; // return NULL when no tags were selected so no results will be displayed
        }
    }

    /**
     * Check if a tag was selected for search
     *
     * @param string $tag The tag to check
     * @return bool if the tag was checked
     */
    private function isSelected($tag) {
        if (isset($_POST['plugin__tag_search_tags']) && is_array($_POST['plugin__tag_search_tags'])) {
            return in_array($tag, $_POST['plugin__tag_search_tags'], true);
        } else {
            return false; // no tags in the post data - no tag selected
        }
    }

    /**
     * Check if the tag query should use AND (instead of OR)
     *
     * @return bool if the query should use AND
     */
    private function useAnd() {
        return isset($_POST['plugin__tag_search_and']);
    }
}
// vim:ts=4:sw=4:et: