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(!$table->isLookup()) { 76 if($infield == '%pageid%') { 77 $column = new PageColumn(0, new Page(), $table); 78 } 79 if($infield == '%title%') { 80 $column = new PageColumn(0, new Page(array('usetitles' => true)), $table); 81 } 82 if($infield == '%lastupdate%') { 83 $column = new RevisionColumn(0, new DateTime(), $table); 84 } 85 if ($infield == '%lasteditor%') { 86 $column = new UserColumn(0, new User(), $table); 87 } 88 if ($infield == '%lastsummary%') { 89 return new SummaryColumn(0, new Summary(), $table); 90 } 91 } else { 92 if($infield == '%rowid%') { 93 $column = new RowColumn(0, new Decimal(), $table); 94 } 95 } 96 } 97 if(!$column) { 98 // field does not exist 99 msg(sprintf('Field %s.%s does not exist', $table, $infield), -1); 100 return false; 101 } 102 103 if($column->isMulti()) { 104 // field is multi 105 msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $table, $field), -1); 106 return false; 107 } 108 109 return $column; 110 } 111 112 /** 113 * Creates the options array 114 * 115 * @return array 116 */ 117 protected function getOptions() { 118 $schema = $this->config['schema']; 119 $column = $this->getLookupColumn(); 120 if(!$column) return array(); 121 $field = $column->getLabel(); 122 123 $search = new Search(); 124 $search->addSchema($schema); 125 $search->addColumn($field); 126 $search->addSort($field); 127 $result = $search->execute(); 128 $pids = $search->getPids(); 129 $len = count($result); 130 131 /** @var Value[][] $result */ 132 $options = array('' => ''); 133 for($i = 0; $i < $len; $i++) { 134 $options[$pids[$i]] = $result[$i][0]->getDisplayValue(); 135 } 136 return $options; 137 } 138 139 /** 140 * Render using linked field 141 * 142 * @param int|string $value 143 * @param \Doku_Renderer $R 144 * @param string $mode 145 * @return bool 146 */ 147 public function renderValue($value, \Doku_Renderer $R, $mode) { 148 list(, $value) = json_decode($value); 149 $column = $this->getLookupColumn(); 150 if(!$column) return false; 151 return $column->getType()->renderValue($value, $R, $mode); 152 } 153 154 /** 155 * Render using linked field 156 * 157 * @param \int[]|\string[] $values 158 * @param \Doku_Renderer $R 159 * @param string $mode 160 * @return bool 161 */ 162 public function renderMultiValue($values, \Doku_Renderer $R, $mode) { 163 $values = array_map( 164 function ($val) { 165 list(, $val) = json_decode($val); 166 return $val; 167 }, $values 168 ); 169 $column = $this->getLookupColumn(); 170 if(!$column) return false; 171 return $column->getType()->renderMultiValue($values, $R, $mode); 172 } 173 174 /** 175 * @param string $value 176 * @return string 177 */ 178 public function rawValue($value) { 179 list($value) = json_decode($value); 180 return $value; 181 } 182 183 /** 184 * @param string $value 185 * @return string 186 */ 187 public function displayValue($value) { 188 list(, $value) = json_decode($value); 189 $column = $this->getLookupColumn(); 190 if($column) { 191 return $column->getType()->displayValue($value); 192 } else { 193 return ''; 194 } 195 } 196 197 /** 198 * This is the value to be used as argument to a filter for another column. 199 * 200 * In a sense this is the counterpart to the @see filter() function 201 * 202 * @param string $value 203 * 204 * @return string 205 */ 206 public function compareValue($value) { 207 list(, $value) = json_decode($value); 208 $column = $this->getLookupColumn(); 209 if($column) { 210 return $column->getType()->rawValue($value); 211 } else { 212 return ''; 213 } 214 } 215 216 217 /** 218 * Merge with lookup table 219 * 220 * @param QueryBuilder $QB 221 * @param string $tablealias 222 * @param string $colname 223 * @param string $alias 224 */ 225 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) { 226 $schema = 'data_' . $this->config['schema']; 227 $column = $this->getLookupColumn(); 228 if(!$column) { 229 parent::select($QB, $tablealias, $colname, $alias); 230 return; 231 } 232 233 $field = $column->getColName(); 234 $rightalias = $QB->generateTableAlias(); 235 $QB->addLeftJoin( 236 $tablealias, $schema, $rightalias, 237 "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1" 238 ); 239 $column->getType()->select($QB, $rightalias, $field, $alias); 240 $sql = $QB->getSelectStatement($alias); 241 $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias); 242 } 243 244 /** 245 * Compare against lookup table 246 * 247 * @param QueryBuilderWhere $add 248 * @param string $tablealias 249 * @param string $colname 250 * @param string $comp 251 * @param string|\string[] $value 252 * @param string $op 253 */ 254 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) { 255 $schema = 'data_' . $this->config['schema']; 256 $column = $this->getLookupColumn(); 257 if(!$column) { 258 parent::filter($add, $tablealias, $colname, $comp, $value, $op); 259 return; 260 } 261 $field = $column->getColName(); 262 263 // compare against lookup field 264 $QB = $add->getQB(); 265 $rightalias = $QB->generateTableAlias(); 266 $QB->addLeftJoin( 267 $tablealias, $schema, $rightalias, 268 "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1" 269 ); 270 $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op); 271 } 272 273 /** 274 * Sort by lookup table 275 * 276 * @param QueryBuilder $QB 277 * @param string $tablealias 278 * @param string $colname 279 * @param string $order 280 */ 281 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) { 282 $schema = 'data_' . $this->config['schema']; 283 $column = $this->getLookupColumn(); 284 if(!$column) { 285 parent::sort($QB, $tablealias, $colname, $order); 286 return; 287 } 288 $field = $column->getColName(); 289 290 $rightalias = $QB->generateTableAlias(); 291 $QB->addLeftJoin( 292 $tablealias, $schema, $rightalias, 293 "$tablealias.$colname = $rightalias.pid AND $rightalias.latest = 1" 294 ); 295 $column->getType()->sort($QB, $rightalias, $field, $order); 296 } 297 298} 299