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