1<?php 2 3namespace dokuwiki\plugin\struct\types; 4 5use dokuwiki\plugin\struct\meta\QueryBuilder; 6use dokuwiki\plugin\struct\meta\QueryBuilderWhere; 7 8/** 9 * Class Page 10 * 11 * Represents a single page in the wiki. Will be linked in output. 12 * 13 * @package dokuwiki\plugin\struct\types 14 */ 15class Page extends AbstractMultiBaseType 16{ 17 18 protected $config = array( 19 'usetitles' => false, 20 'autocomplete' => array( 21 'mininput' => 2, 22 'maxresult' => 5, 23 'namespace' => '', 24 'postfix' => '', 25 ), 26 ); 27 28 /** 29 * Output the stored data 30 * 31 * @param string $value the value stored in the database - JSON when titles are used 32 * @param \Doku_Renderer $R the renderer currently used to render the data 33 * @param string $mode The mode the output is rendered in (eg. XHTML) 34 * @return bool true if $mode could be satisfied 35 */ 36 public function renderValue($value, \Doku_Renderer $R, $mode) 37 { 38 if ($this->config['usetitles']) { 39 list($id, $title) = \helper_plugin_struct::decodeJson($value); 40 } else { 41 $id = $value; 42 $title = null; 43 } 44 45 if (!$id) return true; 46 47 $R->internallink(":$id", $title); 48 return true; 49 } 50 51 /** 52 * Cleans the link 53 * 54 * @param string $rawvalue 55 * @return string 56 */ 57 public function validate($rawvalue) 58 { 59 list($page, $fragment) = explode('#', $rawvalue, 2); 60 return cleanID($page) . (strlen(cleanID($fragment)) > 0 ? '#' . cleanID($fragment) : ''); 61 } 62 63 /** 64 * Autocompletion support for pages 65 * 66 * @return array 67 */ 68 public function handleAjax() 69 { 70 global $INPUT; 71 72 // check minimum length 73 $lookup = trim($INPUT->str('search')); 74 if (utf8_strlen($lookup) < $this->config['autocomplete']['mininput']) return array(); 75 76 // results wanted? 77 $max = $this->config['autocomplete']['maxresult']; 78 if ($max <= 0) return array(); 79 80 // lookup with namespace and postfix applied 81 $namespace = $this->config['autocomplete']['namespace']; 82 if ($namespace) { 83 // namespace may be relative, resolve in current context 84 $namespace .= ':foo'; // resolve expects pageID 85 resolve_pageid($INPUT->str('ns'), $namespace, $exists); 86 $namespace = getNS($namespace); 87 } 88 $postfix = $this->config['autocomplete']['postfix']; 89 if ($namespace) $lookup .= ' @' . $namespace; 90 91 $data = ft_pageLookup($lookup, true, $this->config['usetitles']); 92 if (!count($data)) return array(); 93 94 // this basically duplicates what we do in ajax_qsearch() 95 $result = array(); 96 $counter = 0; 97 foreach ($data as $id => $title) { 98 if ($this->config['usetitles']) { 99 $name = $title . ' (' . $id . ')'; 100 } else { 101 $ns = getNS($id); 102 if ($ns) { 103 $name = noNS($id) . ' (' . $ns . ')'; 104 } else { 105 $name = $id; 106 } 107 } 108 109 // check suffix 110 if ($postfix && substr($id, -1 * strlen($postfix)) != $postfix) { 111 continue; // page does not end in postfix, don't suggest it 112 } 113 114 $result[] = array( 115 'label' => $name, 116 'value' => $id 117 ); 118 119 $counter++; 120 if ($counter > $max) break; 121 } 122 123 return $result; 124 } 125 126 /** 127 * When using titles, we need ot join the titles table 128 * 129 * @param QueryBuilder $QB 130 * @param string $tablealias 131 * @param string $colname 132 * @param string $alias 133 */ 134 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) 135 { 136 if (!$this->config['usetitles']) { 137 parent::select($QB, $tablealias, $colname, $alias); 138 return; 139 } 140 $rightalias = $QB->generateTableAlias(); 141 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.$colname = $rightalias.pid"); 142 $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $rightalias.title)", $alias); 143 } 144 145 /** 146 * When using titles, sort by them first 147 * 148 * @param QueryBuilder $QB 149 * @param string $tablealias 150 * @param string $colname 151 * @param string $order 152 */ 153 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 154 { 155 if (!$this->config['usetitles']) { 156 parent::sort($QB, $tablealias, $colname, $order); 157 return; 158 } 159 160 $rightalias = $QB->generateTableAlias(); 161 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.$colname = $rightalias.pid"); 162 $QB->addOrderBy("$rightalias.title $order"); 163 $QB->addOrderBy("$tablealias.$colname $order"); 164 } 165 166 /** 167 * Return the pageid only 168 * 169 * @param string $value 170 * @return string 171 */ 172 public function rawValue($value) 173 { 174 if ($this->config['usetitles']) { 175 list($value) = \helper_plugin_struct::decodeJson($value); 176 } 177 return $value; 178 } 179 180 /** 181 * Return the title only 182 * 183 * @param string $value 184 * @return string 185 */ 186 public function displayValue($value) 187 { 188 if ($this->config['usetitles']) { 189 list($pageid, $value) = \helper_plugin_struct::decodeJson($value); 190 if (blank($value)) { 191 $value = $pageid; 192 } 193 } 194 return $value; 195 } 196 197 /** 198 * When using titles, we need to compare against the title table, too 199 * 200 * @param QueryBuilderWhere $add 201 * @param string $tablealias 202 * @param string $colname 203 * @param string $comp 204 * @param string $value 205 * @param string $op 206 */ 207 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 208 { 209 if (!$this->config['usetitles']) { 210 parent::filter($add, $tablealias, $colname, $comp, $value, $op); 211 return; 212 } 213 214 $QB = $add->getQB(); 215 $rightalias = $QB->generateTableAlias(); 216 $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.$colname = $rightalias.pid"); 217 218 // compare against page and title 219 $sub = $add->where($op); 220 $pl = $QB->addValue($value); 221 $sub->whereOr("$tablealias.$colname $comp $pl"); 222 $pl = $QB->addValue($value); 223 $sub->whereOr("$rightalias.title $comp $pl"); 224 } 225} 226