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