1<?php 2 3namespace dokuwiki\plugin\struct\types; 4 5use dokuwiki\plugin\struct\meta\Column; 6use dokuwiki\plugin\struct\meta\PageColumn; 7use dokuwiki\plugin\struct\meta\QueryBuilder; 8use dokuwiki\plugin\struct\meta\QueryBuilderWhere; 9use dokuwiki\plugin\struct\meta\RevisionColumn; 10use dokuwiki\plugin\struct\meta\RowColumn; 11use dokuwiki\plugin\struct\meta\Schema; 12use dokuwiki\plugin\struct\meta\Search; 13use dokuwiki\plugin\struct\meta\SummaryColumn; 14use dokuwiki\plugin\struct\meta\UserColumn; 15use dokuwiki\plugin\struct\meta\Value; 16 17class Lookup extends Dropdown 18{ 19 protected $config = array( 20 'schema' => '', 21 'field' => '' 22 ); 23 24 /** @var Column caches the referenced column */ 25 protected $column = null; 26 27 /** 28 * Dropdown constructor. 29 * 30 * @param array|null $config 31 * @param string $label 32 * @param bool $ismulti 33 * @param int $tid 34 */ 35 public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) 36 { 37 parent::__construct($config, $label, $ismulti, $tid); 38 $this->config['schema'] = Schema::cleanTableName($this->config['schema']); 39 } 40 41 /** 42 * Get the configured loojup column 43 * 44 * @return Column|false 45 */ 46 protected function getLookupColumn() 47 { 48 if ($this->column !== null) return $this->column; 49 $this->column = $this->getColumn($this->config['schema'], $this->config['field']); 50 return $this->column; 51 } 52 53 /** 54 * Gets the given column, applies language place holder 55 * 56 * @param string $table 57 * @param string $infield 58 * @return Column|false 59 */ 60 protected function getColumn($table, $infield) 61 { 62 global $conf; 63 64 $table = new Schema($table); 65 if (!$table->getId()) { 66 // schema does not exist 67 msg(sprintf('Schema %s does not exist', $table), -1); 68 return false; 69 } 70 71 // apply language replacement 72 $field = str_replace('$LANG', $conf['lang'], $infield); 73 $column = $table->findColumn($field); 74 if (!$column) { 75 $field = str_replace('$LANG', 'en', $infield); // fallback to en 76 $column = $table->findColumn($field); 77 } 78 if (!$column) { 79 if ($infield == '%pageid%') { 80 $column = new PageColumn(0, new Page(), $table); 81 } 82 if ($infield == '%title%') { 83 $column = new PageColumn(0, new Page(array('usetitles' => true)), $table); 84 } 85 if ($infield == '%lastupdate%') { 86 $column = new RevisionColumn(0, new DateTime(), $table); 87 } 88 if ($infield == '%lasteditor%') { 89 $column = new UserColumn(0, new User(), $table); 90 } 91 if ($infield == '%lastsummary%') { 92 return new SummaryColumn(0, new AutoSummary(), $table); 93 } 94 if ($infield == '%rowid%') { 95 $column = new RowColumn(0, new Decimal(), $table); 96 } 97 } 98 if (!$column) { 99 // field does not exist 100 msg(sprintf('Field %s.%s does not exist', $table, $infield), -1); 101 return false; 102 } 103 104 if ($column->isMulti()) { 105 // field is multi 106 msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $table, $field), -1); 107 return false; 108 } 109 110 return $column; 111 } 112 113 /** 114 * Creates the options array 115 * 116 * @return array 117 */ 118 protected function getOptions() 119 { 120 $schema = $this->config['schema']; 121 $column = $this->getLookupColumn(); 122 if (!$column) return array(); 123 $field = $column->getLabel(); 124 125 $search = new Search(); 126 $search->addSchema($schema); 127 $search->addColumn($field); 128 $search->addSort($field); 129 $result = $search->execute(); 130 $pids = $search->getPids(); 131 $rids = $search->getRids(); 132 $len = count($result); 133 134 /** @var Value[][] $result */ 135 $options = array('' => ''); 136 for ($i = 0; $i < $len; $i++) { 137 $val = json_encode([$pids[$i], (int)$rids[$i]]); 138 $options[$val] = $result[$i][0]->getDisplayValue(); 139 } 140 return $options; 141 } 142 143 144 /** 145 * Render using linked field 146 * 147 * @param int|string $value 148 * @param \Doku_Renderer $R 149 * @param string $mode 150 * @return bool 151 */ 152 public function renderValue($value, \Doku_Renderer $R, $mode) 153 { 154 list(, $value) = \helper_plugin_struct::decodeJson($value); 155 $column = $this->getLookupColumn(); 156 if (!$column) return false; 157 return $column->getType()->renderValue($value, $R, $mode); 158 } 159 160 /** 161 * Render using linked field 162 * 163 * @param \int[]|\string[] $values 164 * @param \Doku_Renderer $R 165 * @param string $mode 166 * @return bool 167 */ 168 public function renderMultiValue($values, \Doku_Renderer $R, $mode) 169 { 170 $values = array_map( 171 function ($val) { 172 list(, $val) = \helper_plugin_struct::decodeJson($val); 173 return $val; 174 }, 175 $values 176 ); 177 $column = $this->getLookupColumn(); 178 if (!$column) return false; 179 return $column->getType()->renderMultiValue($values, $R, $mode); 180 } 181 182 /** 183 * @param string $value 184 * @return string 185 */ 186 public function rawValue($value) 187 { 188 list($value) = \helper_plugin_struct::decodeJson($value); 189 return $value; 190 } 191 192 /** 193 * @param string $value 194 * @return string 195 */ 196 public function displayValue($value) 197 { 198 list(, $value) = \helper_plugin_struct::decodeJson($value); 199 $column = $this->getLookupColumn(); 200 if ($column) { 201 return $column->getType()->displayValue($value); 202 } else { 203 return ''; 204 } 205 } 206 207 /** 208 * This is the value to be used as argument to a filter for another column. 209 * 210 * In a sense this is the counterpart to the @param string $value 211 * 212 * @return string 213 * @see filter() function 214 * 215 */ 216 public function compareValue($value) 217 { 218 list(, $value) = \helper_plugin_struct::decodeJson($value); 219 $column = $this->getLookupColumn(); 220 if ($column) { 221 return $column->getType()->rawValue($value); 222 } else { 223 return ''; 224 } 225 } 226 227 228 /** 229 * Merge with lookup table 230 * 231 * @param QueryBuilder $QB 232 * @param string $tablealias 233 * @param string $colname 234 * @param string $alias 235 */ 236 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) 237 { 238 $schema = 'data_' . $this->config['schema']; 239 $column = $this->getLookupColumn(); 240 if (!$column) { 241 parent::select($QB, $tablealias, $colname, $alias); 242 return; 243 } 244 245 $field = $column->getColName(); 246 $rightalias = $QB->generateTableAlias(); 247 $QB->addLeftJoin( 248 $tablealias, 249 $schema, 250 $rightalias, 251 "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) " . 252 "AND $rightalias.latest = 1" 253 ); 254 $column->getType()->select($QB, $rightalias, $field, $alias); 255 $sql = $QB->getSelectStatement($alias); 256 $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias); 257 } 258 259 /** 260 * Compare against lookup table 261 * 262 * @param QueryBuilderWhere $add 263 * @param string $tablealias 264 * @param string $colname 265 * @param string $comp 266 * @param string|\string[] $value 267 * @param string $op 268 */ 269 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 270 { 271 $schema = 'data_' . $this->config['schema']; 272 $column = $this->getLookupColumn(); 273 if (!$column) { 274 parent::filter($add, $tablealias, $colname, $comp, $value, $op); 275 return; 276 } 277 $field = $column->getColName(); 278 279 // compare against lookup field 280 $QB = $add->getQB(); 281 $rightalias = $QB->generateTableAlias(); 282 $QB->addLeftJoin( 283 $tablealias, 284 $schema, 285 $rightalias, 286 "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " . 287 "$rightalias.latest = 1" 288 ); 289 $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op); 290 } 291 292 /** 293 * Sort by lookup table 294 * 295 * @param QueryBuilder $QB 296 * @param string $tablealias 297 * @param string $colname 298 * @param string $order 299 */ 300 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 301 { 302 $schema = 'data_' . $this->config['schema']; 303 $column = $this->getLookupColumn(); 304 if (!$column) { 305 parent::sort($QB, $tablealias, $colname, $order); 306 return; 307 } 308 $field = $column->getColName(); 309 310 $rightalias = $QB->generateTableAlias(); 311 $QB->addLeftJoin( 312 $tablealias, 313 $schema, 314 $rightalias, 315 "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " . 316 "$rightalias.latest = 1" 317 ); 318 $column->getType()->sort($QB, $rightalias, $field, $order); 319 } 320} 321