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