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