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 $rids = $search->getRids(); 132 $len = count($result); 133 134 /** @var Value[][] $result */ 135 $options = array('' => ''); 136 for ($i = 0; $i < $len; $i++) { 137 $options[$rids[$i]] = $result[$i][0]->getDisplayValue(); 138 } 139 return $options; 140 } 141 142 /** 143 * Render using linked field 144 * 145 * @param int|string $value 146 * @param \Doku_Renderer $R 147 * @param string $mode 148 * @return bool 149 */ 150 public function renderValue($value, \Doku_Renderer $R, $mode) 151 { 152 list(, $value) = json_decode($value); 153 $column = $this->getLookupColumn(); 154 if (!$column) return false; 155 return $column->getType()->renderValue($value, $R, $mode); 156 } 157 158 /** 159 * Render using linked field 160 * 161 * @param \int[]|\string[] $values 162 * @param \Doku_Renderer $R 163 * @param string $mode 164 * @return bool 165 */ 166 public function renderMultiValue($values, \Doku_Renderer $R, $mode) 167 { 168 $values = array_map( 169 function ($val) { 170 list(, $val) = json_decode($val); 171 return $val; 172 }, 173 $values 174 ); 175 $column = $this->getLookupColumn(); 176 if (!$column) return false; 177 return $column->getType()->renderMultiValue($values, $R, $mode); 178 } 179 180 /** 181 * @param string $value 182 * @return string 183 */ 184 public function rawValue($value) 185 { 186 list($value) = json_decode($value); 187 return $value; 188 } 189 190 /** 191 * @param string $value 192 * @return string 193 */ 194 public function displayValue($value) 195 { 196 list(, $value) = json_decode($value); 197 $column = $this->getLookupColumn(); 198 if ($column) { 199 return $column->getType()->displayValue($value); 200 } else { 201 return ''; 202 } 203 } 204 205 /** 206 * This is the value to be used as argument to a filter for another column. 207 * 208 * In a sense this is the counterpart to the @see filter() function 209 * 210 * @param string $value 211 * 212 * @return string 213 */ 214 public function compareValue($value) 215 { 216 list(, $value) = json_decode($value); 217 $column = $this->getLookupColumn(); 218 if ($column) { 219 return $column->getType()->rawValue($value); 220 } else { 221 return ''; 222 } 223 } 224 225 226 /** 227 * Merge with lookup table 228 * 229 * @param QueryBuilder $QB 230 * @param string $tablealias 231 * @param string $colname 232 * @param string $alias 233 */ 234 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) 235 { 236 $schema = 'data_' . $this->config['schema']; 237 $column = $this->getLookupColumn(); 238 if (!$column) { 239 parent::select($QB, $tablealias, $colname, $alias); 240 return; 241 } 242 243 $field = $column->getColName(); 244 $rightalias = $QB->generateTableAlias(); 245 $QB->addLeftJoin( 246 $tablealias, 247 $schema, 248 $rightalias, 249 "$tablealias.$colname = $rightalias.rid AND $rightalias.latest = 1" 250 ); 251 $column->getType()->select($QB, $rightalias, $field, $alias); 252 $sql = $QB->getSelectStatement($alias); 253 $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias); 254 } 255 256 /** 257 * Compare against lookup table 258 * 259 * @param QueryBuilderWhere $add 260 * @param string $tablealias 261 * @param string $colname 262 * @param string $comp 263 * @param string|\string[] $value 264 * @param string $op 265 */ 266 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 267 { 268 $schema = 'data_' . $this->config['schema']; 269 $column = $this->getLookupColumn(); 270 if (!$column) { 271 parent::filter($add, $tablealias, $colname, $comp, $value, $op); 272 return; 273 } 274 $field = $column->getColName(); 275 276 // compare against lookup field 277 $QB = $add->getQB(); 278 $rightalias = $QB->generateTableAlias(); 279 $QB->addLeftJoin( 280 $tablealias, 281 $schema, 282 $rightalias, 283 "$tablealias.$colname = $rightalias.rid AND $rightalias.latest = 1" 284 ); 285 $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op); 286 } 287 288 /** 289 * Sort by lookup table 290 * 291 * @param QueryBuilder $QB 292 * @param string $tablealias 293 * @param string $colname 294 * @param string $order 295 */ 296 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 297 { 298 $schema = 'data_' . $this->config['schema']; 299 $column = $this->getLookupColumn(); 300 if (!$column) { 301 parent::sort($QB, $tablealias, $colname, $order); 302 return; 303 } 304 $field = $column->getColName(); 305 306 $rightalias = $QB->generateTableAlias(); 307 $QB->addLeftJoin( 308 $tablealias, 309 $schema, 310 $rightalias, 311 "$tablealias.$colname = $rightalias.rid AND $rightalias.latest = 1" 312 ); 313 $column->getType()->sort($QB, $rightalias, $field, $order); 314 } 315} 316