1f61105deSAdrian Lang<?php 2f61105deSAdrian Lang 3f61105deSAdrian Langif(!defined('DOKU_INC')) die(); 4e4543b6dSAdrian Langclass helper_plugin_tagging extends DokuWiki_Plugin { 5f61105deSAdrian Lang 6289f50bdSAndreas Gohr /** 7289f50bdSAndreas Gohr * @return helper_plugin_sqlite 8289f50bdSAndreas Gohr */ 9289f50bdSAndreas Gohr public function getDB() { 10302a38efSAndreas Gohr static $db = null; 11f61105deSAdrian Lang if (!is_null($db)) { 12f61105deSAdrian Lang return $db; 13f61105deSAdrian Lang } 14f61105deSAdrian Lang 15302a38efSAndreas Gohr /** @var helper_plugin_sqlite $db */ 16f61105deSAdrian Lang $db = plugin_load('helper', 'sqlite'); 17f61105deSAdrian Lang if (is_null($db)) { 18f61105deSAdrian Lang msg('The tagging plugin needs the sqlite plugin', -1); 19f61105deSAdrian Lang return false; 20f61105deSAdrian Lang } 217e6afce6SAndreas Gohr $db->init('tagging',dirname(__FILE__).'/db/'); 22302a38efSAndreas Gohr $db->create_function('CLEANTAG', array($this, 'cleanTag'), 1); 23f61105deSAdrian Lang return $db; 24f61105deSAdrian Lang } 25f61105deSAdrian Lang 26302a38efSAndreas Gohr /** 27302a38efSAndreas Gohr * Canonicalizes the tag to its lower case nospace form 28302a38efSAndreas Gohr * 29302a38efSAndreas Gohr * @param $tag 30302a38efSAndreas Gohr * @return string 31302a38efSAndreas Gohr */ 32302a38efSAndreas Gohr public function cleanTag($tag) { 33302a38efSAndreas Gohr $tag = str_replace(' ', '', $tag); 34302a38efSAndreas Gohr $tag = utf8_strtolower($tag); 35302a38efSAndreas Gohr return $tag; 36302a38efSAndreas Gohr } 37302a38efSAndreas Gohr 38302a38efSAndreas Gohr 39f61105deSAdrian Lang public function replaceTags($id, $user, $tags) { 40f61105deSAdrian Lang $db = $this->getDB(); 41f61105deSAdrian Lang $db->query('BEGIN TRANSACTION'); 42f61105deSAdrian Lang $queries = array(array('DELETE FROM taggings WHERE pid = ? AND tagger = ?', $id, $user)); 43f61105deSAdrian Lang foreach ($tags as $tag) { 44f61105deSAdrian Lang $queries[] = array('INSERT INTO taggings (pid, tagger, tag) VALUES(?, ?, ?)', $id, $user, $tag); 45f61105deSAdrian Lang } 46f61105deSAdrian Lang 47f61105deSAdrian Lang foreach ($queries as $query) { 48f61105deSAdrian Lang if (!call_user_func_array(array($db, 'query'), $query)) { 49f61105deSAdrian Lang $db->query('ROLLBACK TRANSACTION'); 50f61105deSAdrian Lang return false; 51f61105deSAdrian Lang } 52f61105deSAdrian Lang } 53f61105deSAdrian Lang return $db->query('COMMIT TRANSACTION'); 54f61105deSAdrian Lang } 55f61105deSAdrian Lang 56*0a518a11SAndreas Gohr /** 57*0a518a11SAndreas Gohr * Get a list of Tags or Pages matching a search criteria 58*0a518a11SAndreas Gohr * 59*0a518a11SAndreas Gohr * @param array $search What to search for array('field' => 'searchterm') 60*0a518a11SAndreas Gohr * @param string $return What field to return 'tag'|'pid' 61*0a518a11SAndreas Gohr * @return array associative array in form of value => count 62*0a518a11SAndreas Gohr * @todo this is a really ugly function. It should be split into separate ones 63*0a518a11SAndreas Gohr */ 64f61105deSAdrian Lang public function getTags($search, $return) { 65f61105deSAdrian Lang $where = '1=1'; 66f61105deSAdrian Lang foreach($search as $k => $v) { 67b2073787SDominik Eckelmann if ($k === 'tag') { 68302a38efSAndreas Gohr $k = 'CLEANTAG(tag)'; 69b2073787SDominik Eckelmann } 70b2073787SDominik Eckelmann 71b2073787SDominik Eckelmann if ($this->useLike($v)) { 72b2073787SDominik Eckelmann $where .= " AND $k LIKE"; 73f61105deSAdrian Lang } else { 74b2073787SDominik Eckelmann $where .= " AND $k ="; 75b2073787SDominik Eckelmann } 76b2073787SDominik Eckelmann 77302a38efSAndreas Gohr if ($k === 'CLEANTAG(tag)') { 78302a38efSAndreas Gohr $where .= ' CLEANTAG(?)'; 79b2073787SDominik Eckelmann } else { 80b2073787SDominik Eckelmann $where .= ' ?'; 81f61105deSAdrian Lang } 82f61105deSAdrian Lang } 83b2073787SDominik Eckelmann 84*0a518a11SAndreas Gohr if($return == 'tag') { 85*0a518a11SAndreas Gohr $groupby = 'CLEANTAG(tag)'; 86*0a518a11SAndreas Gohr } else { 87*0a518a11SAndreas Gohr $groupby = $return; 88*0a518a11SAndreas Gohr } 89*0a518a11SAndreas Gohr 90f61105deSAdrian Lang $db = $this->getDB(); 91f61105deSAdrian Lang $res = $db->query('SELECT ' . $return . ', COUNT(*) ' . 92*0a518a11SAndreas Gohr 'FROM taggings WHERE ' . $where . ' GROUP BY ' . $groupby . 930dbdc3fcSAdrian Lang ' ORDER BY tag', 94f61105deSAdrian Lang array_values($search)); 95f61105deSAdrian Lang 96f61105deSAdrian Lang $res = $db->res2arr($res); 97f61105deSAdrian Lang $ret = array(); 98f61105deSAdrian Lang foreach ($res as $v) { 99f61105deSAdrian Lang $ret[$v[$return]] = $v['COUNT(*)']; 100f61105deSAdrian Lang } 101f61105deSAdrian Lang return $ret; 102f61105deSAdrian Lang } 103f61105deSAdrian Lang 104b2073787SDominik Eckelmann private function useLike($v) { 105b2073787SDominik Eckelmann return strpos($v, '%') === 0 || strrpos($v, '%') === strlen($v) - 1; 106b2073787SDominik Eckelmann } 107b2073787SDominik Eckelmann 108302a38efSAndreas Gohr /** 109302a38efSAndreas Gohr * Constructs the URL to search for a tag 110302a38efSAndreas Gohr * 111302a38efSAndreas Gohr * @param $tag 112302a38efSAndreas Gohr * @return string 113302a38efSAndreas Gohr */ 114f61105deSAdrian Lang public function getTagSearchURL($tag) { 115302a38efSAndreas Gohr return '?do=search&id=' . rawurlencode($tag); 116f61105deSAdrian Lang } 117f61105deSAdrian Lang 118f61105deSAdrian Lang public function cloudData($tags, $levels = 10) { 119f61105deSAdrian Lang $min = min($tags); 120f61105deSAdrian Lang $max = max($tags); 121f61105deSAdrian Lang 122f61105deSAdrian Lang // calculate tresholds 123f61105deSAdrian Lang $tresholds = array(); 124f61105deSAdrian Lang for($i=0; $i<=$levels; $i++){ 125f61105deSAdrian Lang $tresholds[$i] = pow($max - $min + 1, $i/$levels) + $min - 1; 126f61105deSAdrian Lang } 127f61105deSAdrian Lang 128f61105deSAdrian Lang // assign weights 129f61105deSAdrian Lang foreach($tags as $tag => $cnt){ 130f61105deSAdrian Lang foreach($tresholds as $tresh => $val){ 131f61105deSAdrian Lang if($cnt <= $val){ 132f61105deSAdrian Lang $tags[$tag] = $tresh; 133f61105deSAdrian Lang break; 134f61105deSAdrian Lang } 135f61105deSAdrian Lang $tags[$tag] = $levels; 136f61105deSAdrian Lang } 137f61105deSAdrian Lang } 138f61105deSAdrian Lang return $tags; 139f61105deSAdrian Lang } 140f61105deSAdrian Lang 141f61105deSAdrian Lang public function html_cloud($tags, $type, $func, $wrap = true, $return = false) { 142f61105deSAdrian Lang $ret = ''; 1430dbdc3fcSAdrian Lang if ($wrap) $ret .= '<ul class="tagging_cloud clearfix">'; 144f61105deSAdrian Lang if (count($tags) === 0) { 145f61105deSAdrian Lang // Produce valid XHTML (ul needs a child) 146f61105deSAdrian Lang $this->setupLocale(); 147f61105deSAdrian Lang $ret .= '<li><div class="li">' . $this->lang['js']['no' . $type . 's'] . '</div></li>'; 148f61105deSAdrian Lang } else { 149f61105deSAdrian Lang $tags = $this->cloudData($tags); 150f61105deSAdrian Lang foreach ($tags as $val => $size) { 151f61105deSAdrian Lang $ret .= '<li class="t' . $size . '"><div class="li">'; 152f61105deSAdrian Lang $ret .= call_user_func($func, $val); 153f61105deSAdrian Lang $ret .= '</div></li>'; 154f61105deSAdrian Lang } 155f61105deSAdrian Lang } 156f61105deSAdrian Lang if ($wrap) $ret .= '</ul>'; 157f61105deSAdrian Lang if ($return) return $ret; 158f61105deSAdrian Lang echo $ret; 159f61105deSAdrian Lang } 160f61105deSAdrian Lang 161302a38efSAndreas Gohr protected function linkToSearch($tag) { 162f61105deSAdrian Lang return '<a href="' . hsc($this->getTagSearchURL($tag)) . '">' . 163f61105deSAdrian Lang $tag . '</a>'; 164f61105deSAdrian Lang } 165f61105deSAdrian Lang 166f61105deSAdrian Lang public function tpl_tags() { 167f61105deSAdrian Lang global $ID; 168f61105deSAdrian Lang global $INFO; 169f61105deSAdrian Lang global $lang; 170f61105deSAdrian Lang $tags = $this->getTags(array('pid' => $ID), 'tag'); 171f61105deSAdrian Lang $this->html_cloud($tags, 'tag', array($this, 'linkToSearch')); 172f61105deSAdrian Lang 173f61105deSAdrian Lang if (isset($_SERVER['REMOTE_USER']) && $INFO['writable']) { 174f61105deSAdrian Lang $lang['btn_tagging_edit'] = $lang['btn_secedit']; 175f61105deSAdrian Lang echo html_btn('tagging_edit', $ID, '', array()); 176289f50bdSAndreas Gohr $form = new Doku_Form(array('id' => 'tagging__edit')); 177e4543b6dSAdrian Lang $form->addHidden('tagging[id]', $ID); 178289f50bdSAndreas Gohr $form->addHidden('call', 'plugin_tagging_save'); 179e4543b6dSAdrian Lang $form->addElement(form_makeTextField('tagging[tags]', implode(', ', array_keys($this->getTags(array('pid' => $ID, 'tagger' => $_SERVER['REMOTE_USER']), 'tag'))))); 180289f50bdSAndreas Gohr $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('id' => 'tagging__edit_save'))); 181289f50bdSAndreas Gohr $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel'], array('id' => 'tagging__edit_cancel'))); 182f61105deSAdrian Lang $form->printForm(); 183f61105deSAdrian Lang } 184f61105deSAdrian Lang } 185872edc7cSRené Corinth 1868a1a3846SAndreas Gohr /** 1878a1a3846SAndreas Gohr * @return array 1888a1a3846SAndreas Gohr */ 189872edc7cSRené Corinth public function getAllTags(){ 190872edc7cSRené Corinth 191872edc7cSRené Corinth $db = $this->getDb(); 1928a1a3846SAndreas Gohr $res = $db->query('SELECT pid, tag, tagger FROM taggings ORDER BY tag'); 193872edc7cSRené Corinth 194872edc7cSRené Corinth $tags_tmp = $db->res2arr($res); 195872edc7cSRené Corinth $tags = array(); 196872edc7cSRené Corinth foreach ($tags_tmp as $tag) { 197302a38efSAndreas Gohr $tid = $this->cleanTag($tag['tag']); 198872edc7cSRené Corinth 199302a38efSAndreas Gohr //$tags[$tid]['pid'][] = $tag['pid']; 200872edc7cSRené Corinth 201302a38efSAndreas Gohr if (isset($tags[$tid]['count'])) { 202302a38efSAndreas Gohr $tags[$tid]['count']++; 203302a38efSAndreas Gohr $tags[$tid]['tagger'][] = $tag['tagger']; 204872edc7cSRené Corinth } else { 205302a38efSAndreas Gohr $tags[$tid]['count'] = 1; 206302a38efSAndreas Gohr $tags[$tid]['tagger'] = array($tag['tagger']); 207872edc7cSRené Corinth } 208872edc7cSRené Corinth } 209872edc7cSRené Corinth return $tags; 210872edc7cSRené Corinth } 211872edc7cSRené Corinth 2128a1a3846SAndreas Gohr /** 2138a1a3846SAndreas Gohr * Renames a tag 2148a1a3846SAndreas Gohr * 2158a1a3846SAndreas Gohr * @param string $formerTagName 2168a1a3846SAndreas Gohr * @param string $newTagName 2178a1a3846SAndreas Gohr */ 218872edc7cSRené Corinth public function renameTag($formerTagName, $newTagName) { 219872edc7cSRené Corinth 220872edc7cSRené Corinth if(empty($formerTagName) || empty($newTagName)) { 2218a1a3846SAndreas Gohr msg($this->getLang("admin enter tag names"), -1); 2228a1a3846SAndreas Gohr return; 223872edc7cSRené Corinth } 224872edc7cSRené Corinth 225872edc7cSRené Corinth $db = $this->getDb(); 226872edc7cSRené Corinth 227302a38efSAndreas Gohr $res = $db->query('SELECT pid FROM taggings WHERE tag= ?', $formerTagName); 228872edc7cSRené Corinth $check = $db->res2arr($res); 229872edc7cSRené Corinth 230872edc7cSRené Corinth if (empty($check)) { 2318a1a3846SAndreas Gohr msg($this->getLang("admin tag does not exists"), -1); 2328a1a3846SAndreas Gohr return; 233872edc7cSRené Corinth } 234872edc7cSRené Corinth 235302a38efSAndreas Gohr $res = $db->query("UPDATE taggings SET tag = ? WHERE tag = ?", $newTagName, $formerTagName); 236872edc7cSRené Corinth $db->res2arr($res); 237872edc7cSRené Corinth 2388a1a3846SAndreas Gohr msg($this->getLang("admin saved"), 1); 2398a1a3846SAndreas Gohr return; 240872edc7cSRené Corinth } 241872edc7cSRené Corinth 242f61105deSAdrian Lang} 243