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