PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/syntax.php

https://github.com/dokufreaks/plugin-cloud
PHP | 284 lines | 193 code | 41 blank | 50 comment | 51 complexity | dc113f671cdcd5909e1d97b3ef633b7a MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * Cloud Plugin: shows a cloud of the most frequently used words
  4. *
  5. * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
  6. * @author Esther Brunner <wikidesign@gmail.com>
  7. */
  8. class syntax_plugin_cloud extends DokuWiki_Syntax_Plugin {
  9. protected $knownFlags = array('showCount');
  10. protected $stopwords = null;
  11. /**
  12. * Constructor. Loads stopwords.
  13. */
  14. public function __construct() {
  15. $this->stopwords = $this->_getStopwords();
  16. }
  17. function getType() { return 'substition'; }
  18. function getPType() { return 'block'; }
  19. function getSort() { return 98; }
  20. function connectTo($mode) {
  21. $this->Lexer->addSpecialPattern('~~\w*?CLOUD.*?~~', $mode, 'plugin_cloud');
  22. }
  23. function handle($match, $state, $pos, Doku_Handler $handler) {
  24. $match = substr($match, 2, -2); // strip markup
  25. if (substr($match, 0, 3) == 'TAG') {
  26. $type = 'tag';
  27. } elseif (substr($match, 0, 6) == 'SEARCH') {
  28. $type = 'search';
  29. } else {
  30. $type = 'word';
  31. }
  32. list($num, $ns) = explode('>', $match, 2);
  33. list($junk, $num) = explode(':', $num, 2);
  34. $flags = null;
  35. if (preg_match ('/\[.*\]/', $junk, $flags) === 1) {
  36. $flags = trim ($flags [0], '[]');
  37. $found = explode(',', $flags);
  38. $flags = array();
  39. foreach ($found as $flag) {
  40. if (in_array($flag, $this->knownFlags)) {
  41. // Actually we just set flags as present
  42. // Later we might add values to flags like key=value pairs
  43. $flags [$flag] = true;
  44. }
  45. }
  46. }
  47. if (!is_numeric($num)) $num = 50;
  48. if(!is_null($ns)) $namespaces = explode('|', $ns);
  49. else $namespaces = null;
  50. return array($type, $num, $namespaces, $flags);
  51. }
  52. function render($mode, Doku_Renderer $renderer, $data) {
  53. global $conf;
  54. list($type, $num, $namespaces, $flags) = $data;
  55. if ($mode == 'xhtml') {
  56. if ($type == 'tag') { // we need the tag helper plugin
  57. /** @var helper_plugin_tag $tag */
  58. if (plugin_isdisabled('tag') || (!$tag = plugin_load('helper', 'tag'))) {
  59. msg('The Tag Plugin must be installed to display tag clouds.', -1);
  60. return false;
  61. }
  62. $cloud = $this->_getTagCloud($num, $min, $max, $namespaces, $tag);
  63. } elseif($type == 'search') {
  64. /** @var helper_plugin_searchstats $helper */
  65. $helper = plugin_load('helper', 'searchstats');
  66. if($helper) {
  67. $cloud = $helper->getSearchWordArray($num);
  68. $this->_filterCloud($cloud, 'search_blacklist');
  69. // calculate min/max values
  70. $min = PHP_INT_MAX;
  71. $max = 0;
  72. foreach ($cloud as $size) {
  73. $min = min($size, $min);
  74. $max = max($size, $max);
  75. }
  76. } else {
  77. msg('You have to install the searchstats plugin to use this feature.', -1);
  78. return false;
  79. }
  80. } else {
  81. $cloud = $this->_getWordCloud($num, $min, $max);
  82. }
  83. if (!is_array($cloud) || empty($cloud)) return false;
  84. $delta = ($max-$min)/16;
  85. // prevent caching to ensure the included pages are always fresh
  86. $renderer->nocache();
  87. // and render the cloud
  88. $renderer->doc .= '<div class="cloud">'.DOKU_LF;
  89. foreach ($cloud as $word => $size) {
  90. if ($size < $min+round($delta)) $class = 'cloud1';
  91. elseif ($size < $min+round(2*$delta)) $class = 'cloud2';
  92. elseif ($size < $min+round(4*$delta)) $class = 'cloud3';
  93. elseif ($size < $min+round(8*$delta)) $class = 'cloud4';
  94. else $class = 'cloud5';
  95. $name = $word;
  96. if ($type == 'tag' && isset($tag)) {
  97. $id = $word;
  98. $exists = false;
  99. resolve_pageID($tag->namespace, $id, $exists);
  100. if($exists) {
  101. $link = wl($id);
  102. if($conf['useheading']) {
  103. $name = p_get_first_heading($id, false);
  104. if (empty($name)) {
  105. $name = $word;
  106. }
  107. }
  108. } else {
  109. $link = wl($id, array('do'=>'showtag', 'tag'=>$word));
  110. }
  111. $title = $word;
  112. $class .= ($exists ? '_tag1' : '_tag2');
  113. } else {
  114. if($conf['userewrite'] == 2) {
  115. $link = wl($word, array('do'=>'search', 'id'=>$word));
  116. $title = $size;
  117. } else {
  118. $link = wl($word, 'do=search');
  119. $title = $size;
  120. }
  121. }
  122. if ($flags ['showCount'] === true) {
  123. $name .= '('.$size.')';
  124. }
  125. $renderer->doc .= DOKU_TAB . '<a href="' . $link . '" class="' . $class .'"'
  126. .' title="' . $title . '">' . hsc($name) . '</a>' . DOKU_LF;
  127. }
  128. $renderer->doc .= '</div>' . DOKU_LF;
  129. return true;
  130. }
  131. return false;
  132. }
  133. /**
  134. * Helper function for loading and returning the array with stopwords.
  135. *
  136. * Stopwords files are loaded from two locations:
  137. * - inc/lang/"actual language"/stopwords.txt
  138. * - conf/stopwords.txt
  139. *
  140. * If both files exists, then both files are used - the content is merged.
  141. */
  142. protected function _getStopwords() {
  143. // load stopwords
  144. $swfile = DOKU_INC.'inc/lang/'.$conf['lang'].'/stopwords.txt';
  145. if (@file_exists($swfile)) $stopwords = file($swfile, FILE_IGNORE_NEW_LINES);
  146. else $stopwords = array();
  147. // load extra local stopwords
  148. $swfile = DOKU_CONF.'stopwords.txt';
  149. if (@file_exists($swfile)) $stopwords = array_merge($stopwords, file($swfile, FILE_IGNORE_NEW_LINES));
  150. if (count($stopwords) == 0) {
  151. return null;
  152. }
  153. return $stopwords;
  154. }
  155. /**
  156. * Applies filters on the cloud:
  157. * - removes all short words, see config option 'minimum_word_length'
  158. * - removes all words in configured blacklist $balcklistName from $cloud array
  159. */
  160. function _filterCloud(&$cloud, $balcklistName) {
  161. // Remove to short words
  162. $min = $this->getConf('minimum_word_length');
  163. foreach ($cloud as $key => $count) {
  164. if (iconv_strlen($key) < $min)
  165. unset($cloud[$key]);
  166. }
  167. // Remove stopwords
  168. if ($this->stopwords != null)
  169. {
  170. foreach ($this->stopwords as $word) {
  171. if (isset($cloud[$word]))
  172. unset($cloud[$word]);
  173. }
  174. }
  175. // Remove word which are on the blacklist
  176. $blacklist = $this->getConf($balcklistName);
  177. if(!empty($blacklist)) {
  178. $blacklist = explode(',', $blacklist);
  179. $blacklist = str_replace(' ', '', $blacklist); // remove spaces
  180. foreach ($blacklist as $word) {
  181. if (isset($cloud[$word]))
  182. unset($cloud[$word]);
  183. }
  184. }
  185. }
  186. /**
  187. * Returns the sorted word cloud array
  188. */
  189. function _getWordCloud($num, &$min, &$max) {
  190. global $conf;
  191. $cloud = array();
  192. if (@file_exists($conf['indexdir'].'/page.idx')) { // new word-length based index
  193. require_once(DOKU_INC.'inc/indexer.php');
  194. $lengths = idx_indexLengths(0);
  195. foreach ($lengths as $len) {
  196. $idx = idx_getIndex('i', $len);
  197. $word_idx = idx_getIndex('w', $len);
  198. $this->_addWordsToCloud($cloud, $idx, $word_idx);
  199. }
  200. } else { // old index
  201. $idx = file($conf['cachedir'].'/index.idx');
  202. $word_idx = file($conf['cachedir'].'/word.idx');
  203. $this->_addWordsToCloud($cloud, $idx, $word_idx);
  204. }
  205. $this->_filterCloud($cloud, 'word_blacklist');
  206. return $this->_sortCloud($cloud, $num, $min, $max);
  207. }
  208. /**
  209. * Adds all words in given index as $word => $freq to $cloud array
  210. */
  211. function _addWordsToCloud(&$cloud, $idx, $word_idx) {
  212. $wcount = count($word_idx);
  213. // collect the frequency of the words
  214. for ($i = 0; $i < $wcount; $i++) {
  215. $key = trim($word_idx[$i]);
  216. $value = explode(':', $idx[$i]);
  217. if (!trim($value[0])) continue;
  218. $cloud[$key] = count($value);
  219. }
  220. }
  221. /**
  222. * Returns the sorted tag cloud array
  223. */
  224. function _getTagCloud($num, &$min, &$max, $namespaces = NULL, helper_plugin_tag &$tag) {
  225. $cloud = $tag->tagOccurrences(NULL, $namespaces, true, $this->getConf('list_tags_of_subns'));
  226. $this->_filterCloud($cloud, 'tag_blacklist');
  227. return $this->_sortCloud($cloud, $num, $min, $max);
  228. }
  229. /**
  230. * Sorts and slices the cloud
  231. */
  232. function _sortCloud($cloud, $num, &$min, &$max) {
  233. if(empty($cloud)) return $cloud;
  234. // sort by frequency, then alphabetically
  235. arsort($cloud);
  236. $cloud = array_chunk($cloud, $num, true);
  237. $max = current($cloud[0]);
  238. $min = end($cloud[0]);
  239. ksort($cloud[0]);
  240. return $cloud[0];
  241. }
  242. }
  243. // vim:ts=4:sw=4:et: