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)) AND $rightalias.latest = 1" 252 ); 253 $column->getType()->select($QB, $rightalias, $field, $alias); 254 $sql = $QB->getSelectStatement($alias); 255 $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias); 256 } 257 258 /** 259 * Compare against lookup table 260 * 261 * @param QueryBuilderWhere $add 262 * @param string $tablealias 263 * @param string $colname 264 * @param string $comp 265 * @param string|\string[] $value 266 * @param string $op 267 */ 268 public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 269 { 270 $schema = 'data_' . $this->config['schema']; 271 $column = $this->getLookupColumn(); 272 if (!$column) { 273 parent::filter($add, $tablealias, $colname, $comp, $value, $op); 274 return; 275 } 276 $field = $column->getColName(); 277 278 // compare against lookup field 279 $QB = $add->getQB(); 280 $rightalias = $QB->generateTableAlias(); 281 $QB->addLeftJoin( 282 $tablealias, 283 $schema, 284 $rightalias, 285 "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND $rightalias.latest = 1" 286 ); 287 $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op); 288 } 289 290 /** 291 * Sort by lookup table 292 * 293 * @param QueryBuilder $QB 294 * @param string $tablealias 295 * @param string $colname 296 * @param string $order 297 */ 298 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 299 { 300 $schema = 'data_' . $this->config['schema']; 301 $column = $this->getLookupColumn(); 302 if (!$column) { 303 parent::sort($QB, $tablealias, $colname, $order); 304 return; 305 } 306 $field = $column->getColName(); 307 308 $rightalias = $QB->generateTableAlias(); 309 $QB->addLeftJoin( 310 $tablealias, 311 $schema, 312 $rightalias, 313 "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND $rightalias.latest = 1" 314 ); 315 $column->getType()->sort($QB, $rightalias, $field, $order); 316 } 317} 318