1<?php 2/** 3 * DokuWiki Plugin Cumulus (Syntax Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Håkan Sandell <sandell.hakan@gmail.com> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) die(); 11 12if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 13if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 15 16require_once(DOKU_PLUGIN.'syntax.php'); 17 18class syntax_plugin_cumulus extends DokuWiki_Syntax_Plugin { 19 20 function getInfo() { 21 return confToHash(dirname(__FILE__).'/plugin.info.txt'); 22 } 23 24 function getType() { return 'substition'; } 25 function getPType() { return 'block'; } 26 function getSort() { return 98; } 27 28 function connectTo($mode) { 29 $this->Lexer->addSpecialPattern('~~\w*?CUMULUS.*?~~',$mode,'plugin_cumulus'); 30 } 31 32 function handle($match, $state, $pos, &$handler) { 33 $flags = explode('&', substr($match, 2, -2)); 34 unset($flags[0]); 35 foreach ($flags as $flag) { 36 list($name, $value) = explode('=', $flag); 37 $data[strtolower(trim($name))] = trim($value); 38 } 39 return $data; 40 } 41 42 function render($mode, &$renderer, $data) { 43 if($mode != 'xhtml') return false; 44 45 // prevent caching to ensure the included pages are always fresh 46 $renderer->info['cache'] = false; 47 48 // flash movie options, input filtering 49 $options['width'] = (int)(is_numeric($data['width']) ? $data['width'] : $this->getConf('width')); 50 $options['height'] = (int)(is_numeric($data['height']) ? $data['height'] : $this->getConf('height')); 51 $options['tcolor'] = hsc(isset($data['tcolor']) ? $data['tcolor'] : $this->getConf('tcolor')); 52 $options['tcolor2'] = hsc(isset($data['tcolor2']) ? $data['tcolor2'] : $this->getConf('tcolor2')); 53 $options['hicolor'] = hsc(isset($data['hicolor']) ? $data['hicolor'] : $this->getConf('hicolor')); 54 $options['bgcolor'] = hsc(isset($data['bgcolor']) ? $data['bgcolor'] : $this->getConf('bgcolor')); 55 $options['speed'] = (int)(is_numeric($data['speed']) ? $data['speed'] : $this->getConf('speed')); 56 $options['distr'] = hsc(isset($data['distr']) ? $data['distr'] : $this->getConf('distr')); 57 $options['max'] = (int)(is_numeric($data['max']) ? $data['max'] : $this->getConf('max')); 58 $options['show'] = $data['show']; 59 if ($options['show'] == 'tag') $options['show'] = 'tags'; 60 if ($options['show'] == 'namespace') $options['show'] = 'namespaces'; 61 62 // get the tag cloud... 63 $tagcloud = $this->_getTagXml($options); 64 65 // add random seeds to so name and movie url to avoid collisions and force reloading (needed for IE) 66 $movie = 'cumulus/tagcloud.swf'; 67 if (!file_exists(DOKU_PLUGIN.$movie)) { 68 $renderer->doc .= $this->getLang('filenotfound'); 69 return true; 70 } 71 $movie = DOKU_BASE.'lib/plugins/'.$movie.'?r='.rand(0,9999999); 72 73 // write flash tag 74 $params = array( 75 'allowScriptAccess' => 'always' , 76 'bgcolor' => '#'.$options['bgcolor'] , 77 ); 78 79 if ($this->getConf('trans')) { 80 $params['wmode'] = 'transparent'; 81 } 82 83 $flashvars = array( 84 'tcolor' => '0x'.$options['tcolor'] , 85 'tcolor2' => '0x'.($options['tcolor2'] == "" ? $options['tcolor'] : $options['tcolor2']) , 86 'hicolor' => '0x'.($options['hicolor'] == "" ? $options['tcolor'] : $options['hicolor']) , 87 'tspeed' => (int)$options['speed'] , 88 'distr' => ($options['distr'] ? 'true' : 'false') , 89 'mode' => 'tags' , 90 'tagcloud' => '<tags>'.$tagcloud.'</tags>' , 91 ); 92 93 if ($this->getConf('showtags')) { 94 $alt = '<div id="cloud">'; 95 } else { 96 $alt = '<div id="cloud" style="display:none;">'; 97 } 98 $alt .= preg_replace('/style=".*?"/', '', urldecode($tagcloud)); 99 $alt .= '</div>'.DOKU_LF; 100 $alt .= '<p>Download <a href="http://www.macromedia.com/go/getflashplayer">Flash Player</a> 9 or better for full experience.</p>'.DOKU_LF; 101 102 $renderer->doc .= html_flashobject($movie, $options['width'], $options['height'], $params, $flashvars, null, $alt); 103 return true; 104 } 105 106 /** 107 * Returns <a></a> links with font style attribut representing number of ocurrences 108 * (inspired by DokuWiki Cloud plugin by Gina Häußge, Michael Klier, Esther Brunner) 109 */ 110 function _getTagXml($options) { 111 global $conf; 112 113 if ($options['show'] == 'tags') { // we need the tag helper plugin 114 if (plugin_isdisabled('tag') || (!$tag = plugin_load('helper', 'tag'))) { 115 msg('The Tag Plugin must be installed to display tag clouds.', -1); 116 return ''; 117 } 118 $cloud = $this->_getTagCloud($options['max'], $min, $max, $tag); 119 120 } elseif ($options['show'] == 'namespaces') { 121 $cloud = $this->_getNamespaceCloud($options['max'], $min, $max); 122 123 } else { 124 $cloud = $this->_getWordCloud($options['max'], $min, $max); 125 } 126 if (!is_array($cloud) || empty($cloud)) return ''; 127 128 $delta = ($max-$min)/16; 129 if ($delta == 0) $delta = 1; 130 131 foreach ($cloud as $word => $size) { 132 if ($size < $min+round($delta)) $class = 'cloud1'; 133 elseif ($size < $min+round(2*$delta)) $class = 'cloud2'; 134 elseif ($size < $min+round(4*$delta)) $class = 'cloud3'; 135 elseif ($size < $min+round(8*$delta)) $class = 'cloud4'; 136 else $class = 'cloud5'; 137 138 $name = $word; 139 if ($options['show'] == 'tags') { 140 $id = $word; 141 resolve_pageID($tag->namespace, $id, $exists); 142 if($exists) { 143 $link = wl($id, '', true); 144 if($conf['useheading']) { 145 $name = p_get_first_heading($id, false); 146 } 147 } else { 148 $link = wl($id, array('do'=>'showtag', 'tag'=>noNS($id)), true); 149 } 150 $title = $id; 151 $class .= ($exists ? '_tag1' : '_tag2'); 152 153 } elseif ($options['show'] == 'namespaces') { 154 $id =''; 155 resolve_pageID($word, $id, $exists); 156 $link = wl($id, '', true); 157 $title = $id; 158 $size = 108; 159 $class = 'cloud5'; 160 161 } else { 162 if($conf['userewrite'] == 2) { 163 $link = wl($word, array('do'=>'search', 'id'=>$word), true); 164 $title = $size; 165 } else { 166 $link = wl($word, 'do=search', true); 167 $title = $size; 168 } 169 } 170 171 $fsize = 8 + round(($size-$min)/$delta); 172 $xmlCloude .= '<a href="' .$link . '" class="' . $class .'"' .' title="' . $title . '" style="font-size: '. $fsize .'pt;">' . hsc($name) . '</a>' . DOKU_LF; 173 } 174 return $xmlCloude; 175 } 176 177 /** 178 * Returns the sorted namespace cloud array 179 */ 180 function _getNamespaceCloud($num, &$min, &$max) { 181 global $conf; 182 183 $cloud = array(); 184 $namesp = array(); 185 $opts = array(); 186 search($namesp,$conf['datadir'],'search_namespaces',$opts); 187 188 foreach ($namesp as $name) { 189 if ($name['ns'] == '') $cloud[$name['id']] = 100; 190 } 191 return $this->_sortCloud($cloud, $num, $min, $max); 192 } 193 194 /** 195 * Returns the sorted word cloud array 196 * (from DokuWiki Cloud plugin by Gina Häußge, Michael Klier, Esther Brunner) 197 */ 198 function _getWordCloud($num, &$min, &$max) { 199 global $conf; 200 201 // load stopwords 202 $swfile = DOKU_INC.'inc/lang/'.$conf['lang'].'/stopwords.txt'; 203 if (@file_exists($swfile)) $stopwords = file($swfile); 204 else $stopwords = array(); 205 206 // load extra local stopwords 207 $swfile = DOKU_CONF.'stopwords.txt'; 208 if (@file_exists($swfile)) $stopwords = array_merge($stopwords, file($swfile)); 209 210 $cloud = array(); 211 212 if (@file_exists($conf['indexdir'].'/page.idx')) { // new word-lenght based index 213 require_once(DOKU_INC.'inc/indexer.php'); 214 215 $n = 2; // minimum word length 216 $lengths = idx_indexLengths($n); 217 foreach ($lengths as $len) { 218 $idx = idx_getIndex('i', $len); 219 $word_idx = idx_getIndex('w', $len); 220 221 $this->_addWordsToCloud($cloud, $idx, $word_idx, $stopwords); 222 } 223 224 } else { // old index 225 $idx = file($conf['cachedir'].'/index.idx'); 226 $word_idx = file($conf['cachedir'].'/word.idx'); 227 228 $this->_addWordsToCloud($cloud, $idx, $word_idx, $stopwords); 229 } 230 return $this->_sortCloud($cloud, $num, $min, $max); 231 } 232 233 /** 234 * Adds all words in given index as $word => $freq to $cloud array 235 * (from DokuWiki Cloud plugin by Gina Häußge, Michael Klier, Esther Brunner) 236 */ 237 function _addWordsToCloud(&$cloud, $idx, $word_idx, &$stopwords) { 238 $wcount = count($word_idx); 239 240 // collect the frequency of the words 241 for ($i = 0; $i < $wcount; $i++) { 242 $key = trim($word_idx[$i]); 243 if (!is_int(array_search("$key\n", $stopwords))) { 244 $value = explode(':', $idx[$i]); 245 if (!trim($value[0])) continue; 246 $cloud[$key] = count($value); 247 } 248 } 249 } 250 251 /** 252 * Returns the sorted tag cloud array 253 * (from DokuWiki Cloud plugin by Gina Häußge, Michael Klier, Esther Brunner) 254 */ 255 function _getTagCloud($num, &$min, &$max, &$tag) { 256 $cloud = $tag->tagOccurrences(NULL, NULL, true); 257 return $this->_sortCloud($cloud, $num, $min, $max); 258 } 259 260 /** 261 * Sorts and slices the cloud 262 * (from DokuWiki Cloud plugin by Gina Häußge, Michael Klier, Esther Brunner) 263 */ 264 function _sortCloud($cloud, $num, &$min, &$max) { 265 if(empty($cloud)) return; 266 267 // sort by frequency, then alphabetically 268 arsort($cloud); 269 $cloud = array_chunk($cloud, $num, true); 270 $max = current($cloud[0]); 271 $min = end($cloud[0]); 272 ksort($cloud[0]); 273 274 return $cloud[0]; 275 } 276} 277 278// vim:ts=4:sw=4:et:enc=utf-8: 279