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