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 public function getTags($search, $return) { 57 $where = '1=1'; 58 foreach($search as $k => $v) { 59 if ($k === 'tag') { 60 $k = 'CLEANTAG(tag)'; 61 } 62 63 if ($this->useLike($v)) { 64 $where .= " AND $k LIKE"; 65 } else { 66 $where .= " AND $k ="; 67 } 68 69 if ($k === 'CLEANTAG(tag)') { 70 $where .= ' CLEANTAG(?)'; 71 } else { 72 $where .= ' ?'; 73 } 74 } 75 76 $db = $this->getDB(); 77 $res = $db->query('SELECT ' . $return . ', COUNT(*) ' . 78 'FROM taggings WHERE ' . $where . ' GROUP BY ' . $return . 79 ' ORDER BY tag', 80 array_values($search)); 81 82 $res = $db->res2arr($res); 83 $ret = array(); 84 foreach ($res as $v) { 85 $ret[$v[$return]] = $v['COUNT(*)']; 86 } 87 return $ret; 88 } 89 90 private function useLike($v) { 91 return strpos($v, '%') === 0 || strrpos($v, '%') === strlen($v) - 1; 92 } 93 94 /** 95 * Constructs the URL to search for a tag 96 * 97 * @param $tag 98 * @return string 99 */ 100 public function getTagSearchURL($tag) { 101 return '?do=search&id=' . rawurlencode($tag); 102 } 103 104 public function cloudData($tags, $levels = 10) { 105 $min = min($tags); 106 $max = max($tags); 107 108 // calculate tresholds 109 $tresholds = array(); 110 for($i=0; $i<=$levels; $i++){ 111 $tresholds[$i] = pow($max - $min + 1, $i/$levels) + $min - 1; 112 } 113 114 // assign weights 115 foreach($tags as $tag => $cnt){ 116 foreach($tresholds as $tresh => $val){ 117 if($cnt <= $val){ 118 $tags[$tag] = $tresh; 119 break; 120 } 121 $tags[$tag] = $levels; 122 } 123 } 124 return $tags; 125 } 126 127 public function html_cloud($tags, $type, $func, $wrap = true, $return = false) { 128 $ret = ''; 129 if ($wrap) $ret .= '<ul class="tagging_cloud clearfix">'; 130 if (count($tags) === 0) { 131 // Produce valid XHTML (ul needs a child) 132 $this->setupLocale(); 133 $ret .= '<li><div class="li">' . $this->lang['js']['no' . $type . 's'] . '</div></li>'; 134 } else { 135 $tags = $this->cloudData($tags); 136 foreach ($tags as $val => $size) { 137 $ret .= '<li class="t' . $size . '"><div class="li">'; 138 $ret .= call_user_func($func, $val); 139 $ret .= '</div></li>'; 140 } 141 } 142 if ($wrap) $ret .= '</ul>'; 143 if ($return) return $ret; 144 echo $ret; 145 } 146 147 protected function linkToSearch($tag) { 148 return '<a href="' . hsc($this->getTagSearchURL($tag)) . '">' . 149 $tag . '</a>'; 150 } 151 152 public function tpl_tags() { 153 global $ID; 154 global $INFO; 155 global $lang; 156 $tags = $this->getTags(array('pid' => $ID), 'tag'); 157 $this->html_cloud($tags, 'tag', array($this, 'linkToSearch')); 158 159 if (isset($_SERVER['REMOTE_USER']) && $INFO['writable']) { 160 $lang['btn_tagging_edit'] = $lang['btn_secedit']; 161 echo html_btn('tagging_edit', $ID, '', array()); 162 $form = new Doku_Form(array('id' => 'tagging__edit')); 163 $form->addHidden('tagging[id]', $ID); 164 $form->addHidden('call', 'plugin_tagging_save'); 165 $form->addElement(form_makeTextField('tagging[tags]', implode(', ', array_keys($this->getTags(array('pid' => $ID, 'tagger' => $_SERVER['REMOTE_USER']), 'tag'))))); 166 $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('id' => 'tagging__edit_save'))); 167 $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel'], array('id' => 'tagging__edit_cancel'))); 168 $form->printForm(); 169 } 170 } 171 172 /** 173 * @return array 174 */ 175 public function getAllTags(){ 176 177 $db = $this->getDb(); 178 $res = $db->query('SELECT pid, tag, tagger FROM taggings ORDER BY tag'); 179 180 $tags_tmp = $db->res2arr($res); 181 $tags = array(); 182 foreach ($tags_tmp as $tag) { 183 $tid = $this->cleanTag($tag['tag']); 184 185 //$tags[$tid]['pid'][] = $tag['pid']; 186 187 if (isset($tags[$tid]['count'])) { 188 $tags[$tid]['count']++; 189 $tags[$tid]['tagger'][] = $tag['tagger']; 190 } else { 191 $tags[$tid]['count'] = 1; 192 $tags[$tid]['tagger'] = array($tag['tagger']); 193 } 194 } 195 return $tags; 196 } 197 198 /** 199 * Renames a tag 200 * 201 * @param string $formerTagName 202 * @param string $newTagName 203 */ 204 public function renameTag($formerTagName, $newTagName) { 205 206 if(empty($formerTagName) || empty($newTagName)) { 207 msg($this->getLang("admin enter tag names"), -1); 208 return; 209 } 210 211 $db = $this->getDb(); 212 213 $res = $db->query('SELECT pid FROM taggings WHERE tag= ?', $formerTagName); 214 $check = $db->res2arr($res); 215 216 if (empty($check)) { 217 msg($this->getLang("admin tag does not exists"), -1); 218 return; 219 } 220 221 $res = $db->query("UPDATE taggings SET tag = ? WHERE tag = ?", $newTagName, $formerTagName); 222 $db->res2arr($res); 223 224 msg($this->getLang("admin saved"), 1); 225 return; 226 } 227 228} 229