1<?php 2/** 3 * 4 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 5 * @author Andreas Gohr <andi@splitbrain.org> 6 */ 7// must be run within Dokuwiki 8if(!defined('DOKU_INC')) die(); 9require_once(dirname(__FILE__) . '/table.php'); 10 11/** 12 * Class syntax_plugin_dataau_cloud 13 */ 14class syntax_plugin_dataau_cloud extends syntax_plugin_dataau_table { 15 16 /** 17 * will hold the dataau helper plugin 18 * @var $dthlp helper_plugin_data 19 */ 20 var $dthlp = null; 21 22 /** 23 * Constructor. Load helper plugin 24 */ 25 public function __construct() { 26 $this->dthlp = plugin_load('helper', 'dataau'); 27 if(!$this->dthlp) msg('Loading the dataau helper failed. Make sure the dataau plugin is installed.', -1); 28 } 29 30 /** 31 * What kind of syntax are we? 32 */ 33 public function getType() { 34 return 'substition'; 35 } 36 37 /** 38 * What about paragraphs? 39 */ 40 public function getPType() { 41 return 'block'; 42 } 43 44 /** 45 * Where to sort in? 46 */ 47 public function getSort() { 48 return 155; 49 } 50 51 /** 52 * Connect pattern to lexer 53 */ 54 public function connectTo($mode) { 55 $this->Lexer->addSpecialPattern('----+ *dataaucloud(?: [ a-zA-Z0-9_]*)?-+\n.*?\n----+', $mode, 'plugin_dataau_cloud'); 56 } 57 58 /** 59 * Builds the SQL query from the given data 60 * 61 * @param array &$dataau instruction by handler 62 * @return bool|string SQL query or false 63 */ 64 public function _buildSQL(&$dataau) { 65 $ckey = array_keys($dataau['cols']); 66 $ckey = $ckey[0]; 67 68 $from = ' '; 69 $where = ' '; 70 $pagesjoin = ''; 71 $tables = array(); 72 73 $sqlite = $this->dthlp->_getDB(); 74 if(!$sqlite) return false; 75 76 $fields = array( 77 'pageid' => 'page', 78 'class' => 'class', 79 'title' => 'title' 80 ); 81 // prepare filters (no request filters - we set them ourselves) 82 if(is_array($dataau['filter']) && count($dataau['filter'])) { 83 $cnt = 0; 84 85 foreach($dataau['filter'] as $filter) { 86 $col = $filter['key']; 87 $closecompare = ($filter['compare'] == 'IN(' ? ')' : ''); 88 89 if(preg_match('/^%(\w+)%$/', $col, $m) && isset($fields[$m[1]])) { 90 $where .= " " . $filter['logic'] . " pages." . $fields[$m[1]] . 91 " " . $filter['compare'] . " '" . $filter['value'] . "'" . $closecompare; 92 $pagesjoin = ' LEFT JOIN pages ON pages.pid = dataau.pid'; 93 } else { 94 // filter by hidden column? 95 if(!$tables[$col]) { 96 $tables[$col] = 'T' . (++$cnt); 97 $from .= ' LEFT JOIN dataau AS ' . $tables[$col] . ' ON ' . $tables[$col] . '.pid = dataau.pid'; 98 $from .= ' AND ' . $tables[$col] . ".key = " . $sqlite->quote_string($col); 99 } 100 101 $where .= ' ' . $filter['logic'] . ' ' . $tables[$col] . '.value ' . $filter['compare'] . 102 " '" . $filter['value'] . "'" . $closecompare; //value is already escaped 103 } 104 } 105 } 106 107 // build query 108 $sql = "SELECT dataau.value AS value, COUNT(dataau.pid) AS cnt 109 FROM dataau $from $pagesjoin 110 WHERE dataau.key = " . $sqlite->quote_string($ckey) . " 111 $where 112 GROUP BY dataau.value"; 113 if(isset($dataau['min'])) { 114 $sql .= ' HAVING cnt >= ' . $dataau['min']; 115 } 116 $sql .= ' ORDER BY cnt DESC'; 117 if($dataau['limit']) { 118 $sql .= ' LIMIT ' . $dataau['limit']; 119 } 120 121 return $sql; 122 } 123 124 protected $before_item = '<ul class="dataauplugin_cloud %s">'; 125 protected $after_item = '</ul>'; 126 protected $before_val = '<li class="cl%s">'; 127 protected $after_val = '</li>'; 128 129 /** 130 * Create output or save the data 131 */ 132 public function render($format, Doku_Renderer $renderer, $dataau) { 133 global $ID; 134 135 if($format != 'xhtml') return false; 136 if(is_null($dataau)) return false; 137 if(!$this->dthlp->ready()) return false; 138 $renderer->info['cache'] = false; 139 140 $sqlite = $this->dthlp->_getDB(); 141 if(!$sqlite) return false; 142 143 $ckey = array_keys($dataau['cols']); 144 $ckey = $ckey[0]; 145 146 if(!isset($dataau['page'])) { 147 $dataau['page'] = $ID; 148 } 149 150 $this->dthlp->_replacePlaceholdersInSQL($dataau); 151 152 // build cloud data 153 $res = $sqlite->query($dataau['sql']); 154 $rows = $sqlite->res2arr($res); 155 $min = 0; 156 $max = 0; 157 $tags = array(); 158 foreach($rows as $row) { 159 if(!$max) { 160 $max = $row['cnt']; 161 } 162 $min = $row['cnt']; 163 $tags[$row['value']]['cnt'] = $row['cnt']; 164 $tags[$row['value']]['value'] = $row['value']; 165 } 166 $this->_cloud_weight($tags, $min, $max, 5); 167 168 // output cloud 169 $renderer->doc .= sprintf($this->before_item, hsc($dataau['classes'])); 170 foreach($tags as $tag) { 171 $tagLabelText = hsc($tag['value']); 172 if($dataau['summarize'] == 1) { 173 $tagLabelText .= '<sub>(' . $tag['cnt'] . ')</sub>'; 174 } 175 176 $renderer->doc .= sprintf($this->before_val, $tag['lvl']); 177 $renderer->doc .= '<a href="' . wl($dataau['page'], $this->dthlp->_getTagUrlparam($dataau['cols'][$ckey], $tag['value'])) . 178 '" title="' . sprintf($this->getLang('tagfilter'), hsc($tag['value'])) . 179 '" class="wikilink1">' . $tagLabelText . '</a>'; 180 $renderer->doc .= $this->after_val; 181 } 182 $renderer->doc .= $this->after_item; 183 return true; 184 } 185 186 /** 187 * Create a weighted tag distribution 188 * 189 * @param $tags array ref The tags to weight ( tag => count) 190 * @param $min int The lowest count of a single tag 191 * @param $max int The highest count of a single tag 192 * @param $levels int The number of levels you want. A 5 gives levels 0 to 4. 193 */ 194 protected function _cloud_weight(&$tags, $min, $max, $levels) { 195 $levels--; 196 197 // calculate tresholds 198 $tresholds = array(); 199 for($i = 0; $i <= $levels; $i++) { 200 $tresholds[$i] = pow($max - $min + 1, $i / $levels) + $min - 1; 201 } 202 203 // assign weights 204 foreach($tags as $tag) { 205 foreach($tresholds as $tresh => $val) { 206 if($tag['cnt'] <= $val) { 207 $tags[$tag['value']]['lvl'] = $tresh; 208 break; 209 } 210 $tags[$tag['value']]['lvl'] = $levels; 211 } 212 } 213 214 // sort 215 ksort($tags); 216 } 217 218} 219 220