1<?php 2 3namespace dokuwiki\plugin\struct\types; 4 5use dokuwiki\plugin\struct\meta\Column; 6use dokuwiki\plugin\struct\meta\QueryBuilder; 7use dokuwiki\plugin\struct\meta\QueryBuilderWhere; 8use dokuwiki\plugin\struct\meta\SearchConfigParameters; 9 10class Tag extends AbstractMultiBaseType 11{ 12 protected $config = array( 13 'page' => '', 14 'autocomplete' => array( 15 'mininput' => 2, 16 'maxresult' => 5, 17 ), 18 ); 19 20 /** 21 * @param int|string $value 22 * @param \Doku_Renderer $R 23 * @param string $mode 24 * @return bool 25 */ 26 public function renderValue($value, \Doku_Renderer $R, $mode) 27 { 28 $context = $this->getContext(); 29 $filter = SearchConfigParameters::$PARAM_FILTER . 30 '[' . $context->getTable() . '.' . $context->getLabel() . '*~]=' . $value; 31 32 $page = trim($this->config['page']); 33 if (!$page) $page = cleanID($context->getLabel()); 34 35 $R->internallink($page . '?' . $filter, $value); 36 return true; 37 } 38 39 /** 40 * Autocomplete from existing tags 41 * 42 * @return array 43 */ 44 public function handleAjax() 45 { 46 global $INPUT; 47 48 // check minimum length 49 $lookup = trim($INPUT->str('search')); 50 if (utf8_strlen($lookup) < $this->config['autocomplete']['mininput']) return array(); 51 52 // results wanted? 53 $max = $this->config['autocomplete']['maxresult']; 54 if ($max <= 0) return array(); 55 56 $context = $this->getContext(); 57 $sql = $this->buildSQLFromContext($context); 58 $opt = array("%$lookup%"); 59 60 /** @var \helper_plugin_struct_db $hlp */ 61 $hlp = plugin_load('helper', 'struct_db'); 62 $sqlite = $hlp->getDB(); 63 $res = $sqlite->query($sql, $opt); 64 $rows = $sqlite->res2arr($res); 65 $sqlite->res_close($res); 66 67 $result = array(); 68 foreach ($rows as $row) { 69 $result[] = array( 70 'label' => $row['value'], 71 'value' => $row['value'], 72 ); 73 } 74 75 return $result; 76 } 77 78 /** 79 * Create the sql to query the database for tags to do autocompletion 80 * 81 * This method both handles multi columns and page schemas that need access checking 82 * 83 * @param Column $context 84 * 85 * @return string The sql with a single "?" placeholde for the search value 86 */ 87 protected function buildSQLFromContext(Column $context) 88 { 89 $sql = ''; 90 if ($context->isMulti()) { 91 /** @noinspection SqlResolve */ 92 $sql .= "SELECT DISTINCT value 93 FROM multi_{$context->getTable()} AS M, data_{$context->getTable()} AS D 94 WHERE M.pid = D.pid 95 AND M.rev = D.rev 96 AND M.colref = {$context->getColref()}\n"; 97 } else { 98 /** @noinspection SqlResolve */ 99 $sql .= "SELECT DISTINCT col{$context->getColref()} AS value 100 FROM data_{$context->getTable()} AS D 101 WHERE 1 = 1\n"; 102 } 103 104 $sql .= "AND ( D.pid = '' OR ("; 105 $sql .= "PAGEEXISTS(D.pid) = 1\n"; 106 $sql .= "AND GETACCESSLEVEL(D.pid) > 0\n"; 107 $sql .= ")) "; 108 109 $sql .= "AND D.latest = 1\n"; 110 $sql .= "AND value LIKE ?\n"; 111 $sql .= 'ORDER BY value'; 112 113 return $sql; 114 } 115 116 /** 117 * Normalize tags before comparing 118 * 119 * @param QueryBuilder $QB 120 * @param string $tablealias 121 * @param string $colname 122 * @param string $comp 123 * @param string|string[] $value 124 * @param string $op 125 */ 126 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 127 { 128 /** @var QueryBuilderWhere $add Where additionional queries are added to */ 129 if (is_array($value)) { 130 $add = $add->where($op); // sub where group 131 $op = 'OR'; 132 } 133 foreach ((array)$value as $item) { 134 $pl = $add->getQB()->addValue($item); 135 $add->where($op, "LOWER(REPLACE($tablealias.$colname, ' ', '')) $comp LOWER(REPLACE($pl, ' ', ''))"); 136 } 137 } 138} 139