xref: /plugin/tagging/helper.php (revision 0a518a116de248fb2e4d610ff7d903adbfd13d2e)
1<?php
2
3if(!defined('DOKU_INC')) die();
4class helper_plugin_tagging extends DokuWiki_Plugin {
5
6    /**
7     * @return helper_plugin_sqlite
8     */
9    public function getDB() {
10        static $db = null;
11        if (!is_null($db)) {
12            return $db;
13        }
14
15        /** @var helper_plugin_sqlite $db */
16        $db = plugin_load('helper', 'sqlite');
17        if (is_null($db)) {
18            msg('The tagging plugin needs the sqlite plugin', -1);
19            return false;
20        }
21        $db->init('tagging',dirname(__FILE__).'/db/');
22        $db->create_function('CLEANTAG', array($this, 'cleanTag'), 1);
23        return $db;
24    }
25
26    /**
27     * Canonicalizes the tag to its lower case nospace form
28     *
29     * @param $tag
30     * @return string
31     */
32    public function cleanTag($tag) {
33        $tag = str_replace(' ', '', $tag);
34        $tag = utf8_strtolower($tag);
35        return $tag;
36    }
37
38
39    public function replaceTags($id, $user, $tags) {
40        $db = $this->getDB();
41        $db->query('BEGIN TRANSACTION');
42        $queries = array(array('DELETE FROM taggings WHERE pid = ? AND tagger = ?', $id, $user));
43        foreach ($tags as $tag) {
44            $queries[] = array('INSERT INTO taggings (pid, tagger, tag) VALUES(?, ?, ?)', $id, $user, $tag);
45        }
46
47        foreach ($queries as $query) {
48            if (!call_user_func_array(array($db, 'query'), $query)) {
49                $db->query('ROLLBACK TRANSACTION');
50                return false;
51            }
52        }
53        return $db->query('COMMIT TRANSACTION');
54    }
55
56    /**
57     * Get a list of Tags or Pages matching a search criteria
58     *
59     * @param array  $search What to search for array('field' => 'searchterm')
60     * @param string $return What field to return 'tag'|'pid'
61     * @return array associative array in form of value => count
62     * @todo this is a really ugly function. It should be split into separate ones
63     */
64    public function getTags($search, $return) {
65        $where = '1=1';
66        foreach($search as $k => $v) {
67            if ($k === 'tag') {
68                $k = 'CLEANTAG(tag)';
69            }
70
71            if ($this->useLike($v)) {
72                $where .= " AND $k LIKE";
73            } else {
74                $where .= " AND $k =";
75            }
76
77            if ($k === 'CLEANTAG(tag)') {
78                $where .= ' CLEANTAG(?)';
79            } else {
80                $where .= ' ?';
81            }
82        }
83
84        if($return == 'tag') {
85            $groupby = 'CLEANTAG(tag)';
86        } else {
87            $groupby = $return;
88        }
89
90        $db = $this->getDB();
91        $res = $db->query('SELECT ' . $return . ', COUNT(*) ' .
92                          'FROM taggings WHERE ' . $where . ' GROUP BY ' . $groupby .
93                          ' ORDER BY tag',
94                          array_values($search));
95
96        $res = $db->res2arr($res);
97        $ret = array();
98        foreach ($res as $v) {
99            $ret[$v[$return]] = $v['COUNT(*)'];
100        }
101        return $ret;
102    }
103
104    private function useLike($v) {
105        return strpos($v, '%') === 0 || strrpos($v, '%') === strlen($v) - 1;
106    }
107
108    /**
109     * Constructs the URL to search for a tag
110     *
111     * @param $tag
112     * @return string
113     */
114    public function getTagSearchURL($tag) {
115        return '?do=search&id=' . rawurlencode($tag);
116    }
117
118    public function cloudData($tags, $levels = 10) {
119        $min = min($tags);
120        $max = max($tags);
121
122        // calculate tresholds
123        $tresholds = array();
124        for($i=0; $i<=$levels; $i++){
125            $tresholds[$i] = pow($max - $min + 1, $i/$levels) + $min - 1;
126        }
127
128        // assign weights
129        foreach($tags as $tag => $cnt){
130            foreach($tresholds as $tresh => $val){
131                if($cnt <= $val){
132                    $tags[$tag] = $tresh;
133                    break;
134                }
135                $tags[$tag] = $levels;
136            }
137        }
138        return $tags;
139    }
140
141    public function html_cloud($tags, $type, $func, $wrap = true, $return = false) {
142        $ret = '';
143        if ($wrap) $ret .= '<ul class="tagging_cloud clearfix">';
144        if (count($tags) === 0) {
145            // Produce valid XHTML (ul needs a child)
146            $this->setupLocale();
147            $ret .= '<li><div class="li">' . $this->lang['js']['no' . $type . 's'] . '</div></li>';
148        } else {
149            $tags = $this->cloudData($tags);
150            foreach ($tags as $val => $size) {
151                $ret .= '<li class="t' . $size . '"><div class="li">';
152                $ret .= call_user_func($func, $val);
153                $ret .= '</div></li>';
154            }
155        }
156        if ($wrap) $ret .= '</ul>';
157        if ($return) return $ret;
158        echo $ret;
159    }
160
161    protected function linkToSearch($tag) {
162        return '<a href="' . hsc($this->getTagSearchURL($tag)) . '">' .
163               $tag . '</a>';
164    }
165
166    public function tpl_tags() {
167        global $ID;
168        global $INFO;
169        global $lang;
170        $tags = $this->getTags(array('pid' => $ID), 'tag');
171        $this->html_cloud($tags, 'tag', array($this, 'linkToSearch'));
172
173        if (isset($_SERVER['REMOTE_USER']) && $INFO['writable']) {
174            $lang['btn_tagging_edit'] = $lang['btn_secedit'];
175            echo html_btn('tagging_edit', $ID, '', array());
176            $form = new Doku_Form(array('id' => 'tagging__edit'));
177            $form->addHidden('tagging[id]', $ID);
178            $form->addHidden('call', 'plugin_tagging_save');
179            $form->addElement(form_makeTextField('tagging[tags]', implode(', ', array_keys($this->getTags(array('pid' => $ID, 'tagger' => $_SERVER['REMOTE_USER']), 'tag')))));
180            $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('id' => 'tagging__edit_save')));
181            $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel'], array('id' => 'tagging__edit_cancel')));
182            $form->printForm();
183        }
184    }
185
186    /**
187     * @return array
188     */
189    public function getAllTags(){
190
191        $db = $this->getDb();
192        $res = $db->query('SELECT pid, tag, tagger FROM taggings ORDER BY tag');
193
194        $tags_tmp = $db->res2arr($res);
195        $tags = array();
196        foreach ($tags_tmp as $tag) {
197            $tid = $this->cleanTag($tag['tag']);
198
199            //$tags[$tid]['pid'][] = $tag['pid'];
200
201            if (isset($tags[$tid]['count'])) {
202                $tags[$tid]['count']++;
203                $tags[$tid]['tagger'][] = $tag['tagger'];
204            } else {
205                $tags[$tid]['count'] = 1;
206                $tags[$tid]['tagger'] = array($tag['tagger']);
207            }
208        }
209        return $tags;
210    }
211
212    /**
213     * Renames a tag
214     *
215     * @param string $formerTagName
216     * @param string $newTagName
217     */
218    public function renameTag($formerTagName, $newTagName) {
219
220        if(empty($formerTagName) || empty($newTagName)) {
221            msg($this->getLang("admin enter tag names"), -1);
222            return;
223        }
224
225        $db = $this->getDb();
226
227        $res = $db->query('SELECT pid FROM taggings WHERE tag= ?', $formerTagName);
228        $check = $db->res2arr($res);
229
230        if (empty($check)) {
231            msg($this->getLang("admin tag does not exists"), -1);
232            return;
233        }
234
235        $res = $db->query("UPDATE taggings SET tag = ? WHERE tag = ?", $newTagName, $formerTagName);
236        $db->res2arr($res);
237
238        msg($this->getLang("admin saved"), 1);
239        return;
240    }
241
242}
243