19b8dc0b2SAndreas Gohr<?php 2d6d97f60SAnna Dabrowska 39b8dc0b2SAndreas Gohrnamespace dokuwiki\plugin\struct\types; 49b8dc0b2SAndreas Gohr 59b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\Column; 60549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\PageColumn; 79b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\QueryBuilder; 8af993d55SMichael Grosseuse dokuwiki\plugin\struct\meta\QueryBuilderWhere; 90549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\RevisionColumn; 100549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\RowColumn; 119b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\Schema; 129b8dc0b2SAndreas Gohruse dokuwiki\plugin\struct\meta\Search; 1388b58a21SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\SummaryColumn; 1410b11cc5SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\UserColumn; 150549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\Value; 169b8dc0b2SAndreas Gohr 17d6d97f60SAnna Dabrowskaclass Lookup extends Dropdown 18d6d97f60SAnna Dabrowska{ 19*24eec657SAndreas Gohr protected $config = [ 20*24eec657SAndreas Gohr 'schema' => '', 21*24eec657SAndreas Gohr 'field' => '', 22*24eec657SAndreas Gohr 'combobox' => false, 23*24eec657SAndreas Gohr ]; 249b8dc0b2SAndreas Gohr 25f256f53fSAndreas Gohr /** @var Column caches the referenced column */ 267234bfb1Ssplitbrain protected $column; 27f256f53fSAndreas Gohr 289b8dc0b2SAndreas Gohr /** 299b8dc0b2SAndreas Gohr * Dropdown constructor. 309b8dc0b2SAndreas Gohr * 319b8dc0b2SAndreas Gohr * @param array|null $config 329b8dc0b2SAndreas Gohr * @param string $label 339b8dc0b2SAndreas Gohr * @param bool $ismulti 349b8dc0b2SAndreas Gohr * @param int $tid 359b8dc0b2SAndreas Gohr */ 36d6d97f60SAnna Dabrowska public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) 37d6d97f60SAnna Dabrowska { 389b8dc0b2SAndreas Gohr parent::__construct($config, $label, $ismulti, $tid); 399b8dc0b2SAndreas Gohr $this->config['schema'] = Schema::cleanTableName($this->config['schema']); 409b8dc0b2SAndreas Gohr } 419b8dc0b2SAndreas Gohr 429b8dc0b2SAndreas Gohr /** 43dd6f0fd7SAndreas Gohr * Get the configured loojup column 44dd6f0fd7SAndreas Gohr * 459b8dc0b2SAndreas Gohr * @return Column|false 469b8dc0b2SAndreas Gohr */ 47d6d97f60SAnna Dabrowska protected function getLookupColumn() 48d6d97f60SAnna Dabrowska { 497234bfb1Ssplitbrain if ($this->column instanceof Column) return $this->column; 50dd6f0fd7SAndreas Gohr $this->column = $this->getColumn($this->config['schema'], $this->config['field']); 51dd6f0fd7SAndreas Gohr return $this->column; 52dd6f0fd7SAndreas Gohr } 539b8dc0b2SAndreas Gohr 54dd6f0fd7SAndreas Gohr /** 55dd6f0fd7SAndreas Gohr * Gets the given column, applies language place holder 56dd6f0fd7SAndreas Gohr * 57dd6f0fd7SAndreas Gohr * @param string $table 58d329f04cSAndreas Gohr * @param string $infield 59dd6f0fd7SAndreas Gohr * @return Column|false 60dd6f0fd7SAndreas Gohr */ 61d6d97f60SAnna Dabrowska protected function getColumn($table, $infield) 62d6d97f60SAnna Dabrowska { 63dd6f0fd7SAndreas Gohr global $conf; 64dd6f0fd7SAndreas Gohr 65dd6f0fd7SAndreas Gohr $table = new Schema($table); 66dd6f0fd7SAndreas Gohr if (!$table->getId()) { 679b8dc0b2SAndreas Gohr // schema does not exist 68dd6f0fd7SAndreas Gohr msg(sprintf('Schema %s does not exist', $table), -1); 699b8dc0b2SAndreas Gohr return false; 709b8dc0b2SAndreas Gohr } 719b8dc0b2SAndreas Gohr 729b8dc0b2SAndreas Gohr // apply language replacement 73dd6f0fd7SAndreas Gohr $field = str_replace('$LANG', $conf['lang'], $infield); 74dd6f0fd7SAndreas Gohr $column = $table->findColumn($field); 759b8dc0b2SAndreas Gohr if (!$column) { 76dd6f0fd7SAndreas Gohr $field = str_replace('$LANG', 'en', $infield); // fallback to en 77dd6f0fd7SAndreas Gohr $column = $table->findColumn($field); 789b8dc0b2SAndreas Gohr } 799b8dc0b2SAndreas Gohr if (!$column) { 8010b11cc5SSzymon Olewniczak if ($infield == '%pageid%') { 8110b11cc5SSzymon Olewniczak $column = new PageColumn(0, new Page(), $table); 8210b11cc5SSzymon Olewniczak } 8310b11cc5SSzymon Olewniczak if ($infield == '%title%') { 847234bfb1Ssplitbrain $column = new PageColumn(0, new Page(['usetitles' => true]), $table); 8510b11cc5SSzymon Olewniczak } 8610b11cc5SSzymon Olewniczak if ($infield == '%lastupdate%') { 8710b11cc5SSzymon Olewniczak $column = new RevisionColumn(0, new DateTime(), $table); 8810b11cc5SSzymon Olewniczak } 8910b11cc5SSzymon Olewniczak if ($infield == '%lasteditor%') { 9010b11cc5SSzymon Olewniczak $column = new UserColumn(0, new User(), $table); 9110b11cc5SSzymon Olewniczak } 9288b58a21SSzymon Olewniczak if ($infield == '%lastsummary%') { 936781c68dSSzymon Olewniczak return new SummaryColumn(0, new AutoSummary(), $table); 9488b58a21SSzymon Olewniczak } 9510b11cc5SSzymon Olewniczak if ($infield == '%rowid%') { 9610b11cc5SSzymon Olewniczak $column = new RowColumn(0, new Decimal(), $table); 9710b11cc5SSzymon Olewniczak } 9810b11cc5SSzymon Olewniczak } 9910b11cc5SSzymon Olewniczak if (!$column) { 1009b8dc0b2SAndreas Gohr // field does not exist 101dd6f0fd7SAndreas Gohr msg(sprintf('Field %s.%s does not exist', $table, $infield), -1); 1029b8dc0b2SAndreas Gohr return false; 1039b8dc0b2SAndreas Gohr } 1049b8dc0b2SAndreas Gohr 1059b8dc0b2SAndreas Gohr if ($column->isMulti()) { 1069b8dc0b2SAndreas Gohr // field is multi 107dd6f0fd7SAndreas Gohr msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $table, $field), -1); 1089b8dc0b2SAndreas Gohr return false; 1099b8dc0b2SAndreas Gohr } 1109b8dc0b2SAndreas Gohr 1119b8dc0b2SAndreas Gohr return $column; 1129b8dc0b2SAndreas Gohr } 1139b8dc0b2SAndreas Gohr 1149b8dc0b2SAndreas Gohr /** 1159b8dc0b2SAndreas Gohr * Creates the options array 1169b8dc0b2SAndreas Gohr * 1179b8dc0b2SAndreas Gohr * @return array 1189b8dc0b2SAndreas Gohr */ 119d6d97f60SAnna Dabrowska protected function getOptions() 120d6d97f60SAnna Dabrowska { 1219b8dc0b2SAndreas Gohr $schema = $this->config['schema']; 1229b8dc0b2SAndreas Gohr $column = $this->getLookupColumn(); 1237234bfb1Ssplitbrain if (!$column) return []; 1249b8dc0b2SAndreas Gohr $field = $column->getLabel(); 1259b8dc0b2SAndreas Gohr 1269b8dc0b2SAndreas Gohr $search = new Search(); 1279b8dc0b2SAndreas Gohr $search->addSchema($schema); 1289b8dc0b2SAndreas Gohr $search->addColumn($field); 1299b8dc0b2SAndreas Gohr $search->addSort($field); 1307234bfb1Ssplitbrain 131ba7f5789SAnna Dabrowska $result = $search->getRows(); 132fbbae12aSAnna Dabrowska $pids = $search->getPids(); 1337f803aa8SAnna Dabrowska $rids = $search->getRids(); 1349b8dc0b2SAndreas Gohr $len = count($result); 1359b8dc0b2SAndreas Gohr 1367234bfb1Ssplitbrain $options = ['' => '']; 1379b8dc0b2SAndreas Gohr for ($i = 0; $i < $len; $i++) { 1385e29103aSannda $val = json_encode([$pids[$i], (int)$rids[$i]], JSON_THROW_ON_ERROR); 139fbbae12aSAnna Dabrowska $options[$val] = $result[$i][0]->getDisplayValue(); 1409b8dc0b2SAndreas Gohr } 1419b8dc0b2SAndreas Gohr return $options; 1429b8dc0b2SAndreas Gohr } 1439b8dc0b2SAndreas Gohr 144fbbae12aSAnna Dabrowska 1459b8dc0b2SAndreas Gohr /** 1469b8dc0b2SAndreas Gohr * Render using linked field 1479b8dc0b2SAndreas Gohr * 1489b8dc0b2SAndreas Gohr * @param int|string $value 1499b8dc0b2SAndreas Gohr * @param \Doku_Renderer $R 1509b8dc0b2SAndreas Gohr * @param string $mode 1519b8dc0b2SAndreas Gohr * @return bool 1529b8dc0b2SAndreas Gohr */ 153d6d97f60SAnna Dabrowska public function renderValue($value, \Doku_Renderer $R, $mode) 154d6d97f60SAnna Dabrowska { 1557234bfb1Ssplitbrain [, $value] = \helper_plugin_struct::decodeJson($value); 1569b8dc0b2SAndreas Gohr $column = $this->getLookupColumn(); 1579b8dc0b2SAndreas Gohr if (!$column) return false; 1589b8dc0b2SAndreas Gohr return $column->getType()->renderValue($value, $R, $mode); 1599b8dc0b2SAndreas Gohr } 1609b8dc0b2SAndreas Gohr 1619b8dc0b2SAndreas Gohr /** 1629b8dc0b2SAndreas Gohr * Render using linked field 1639b8dc0b2SAndreas Gohr * 1649b8dc0b2SAndreas Gohr * @param \int[]|\string[] $values 1659b8dc0b2SAndreas Gohr * @param \Doku_Renderer $R 1669b8dc0b2SAndreas Gohr * @param string $mode 1679b8dc0b2SAndreas Gohr * @return bool 1689b8dc0b2SAndreas Gohr */ 169d6d97f60SAnna Dabrowska public function renderMultiValue($values, \Doku_Renderer $R, $mode) 170d6d97f60SAnna Dabrowska { 1719b8dc0b2SAndreas Gohr $values = array_map( 1729b8dc0b2SAndreas Gohr function ($val) { 1737234bfb1Ssplitbrain [, $val] = \helper_plugin_struct::decodeJson($val); 1749b8dc0b2SAndreas Gohr return $val; 175d6d97f60SAnna Dabrowska }, 176d6d97f60SAnna Dabrowska $values 1779b8dc0b2SAndreas Gohr ); 1789b8dc0b2SAndreas Gohr $column = $this->getLookupColumn(); 1799b8dc0b2SAndreas Gohr if (!$column) return false; 1809b8dc0b2SAndreas Gohr return $column->getType()->renderMultiValue($values, $R, $mode); 1819b8dc0b2SAndreas Gohr } 1829b8dc0b2SAndreas Gohr 1839b8dc0b2SAndreas Gohr /** 1849b8dc0b2SAndreas Gohr * @param string $value 1859b8dc0b2SAndreas Gohr * @return string 1869b8dc0b2SAndreas Gohr */ 187d6d97f60SAnna Dabrowska public function rawValue($value) 188d6d97f60SAnna Dabrowska { 1897234bfb1Ssplitbrain [$value] = \helper_plugin_struct::decodeJson($value); 1909b8dc0b2SAndreas Gohr return $value; 1919b8dc0b2SAndreas Gohr } 1929b8dc0b2SAndreas Gohr 1939b8dc0b2SAndreas Gohr /** 1949b8dc0b2SAndreas Gohr * @param string $value 1959b8dc0b2SAndreas Gohr * @return string 1969b8dc0b2SAndreas Gohr */ 197d6d97f60SAnna Dabrowska public function displayValue($value) 198d6d97f60SAnna Dabrowska { 1997234bfb1Ssplitbrain [, $value] = \helper_plugin_struct::decodeJson($value); 2009b8dc0b2SAndreas Gohr $column = $this->getLookupColumn(); 2019b8dc0b2SAndreas Gohr if ($column) { 2029b8dc0b2SAndreas Gohr return $column->getType()->displayValue($value); 2039b8dc0b2SAndreas Gohr } else { 2049b8dc0b2SAndreas Gohr return ''; 2059b8dc0b2SAndreas Gohr } 2069b8dc0b2SAndreas Gohr } 2079b8dc0b2SAndreas Gohr 2089b8dc0b2SAndreas Gohr /** 2097717c082SMichael Große * This is the value to be used as argument to a filter for another column. 2107717c082SMichael Große * 2110549dcc5SAndreas Gohr * In a sense this is the counterpart to the @param string $value 2127717c082SMichael Große * 2137717c082SMichael Große * @return string 2140549dcc5SAndreas Gohr * @see filter() function 2150549dcc5SAndreas Gohr * 2167717c082SMichael Große */ 217d6d97f60SAnna Dabrowska public function compareValue($value) 218d6d97f60SAnna Dabrowska { 2197234bfb1Ssplitbrain [, $value] = \helper_plugin_struct::decodeJson($value); 2207717c082SMichael Große $column = $this->getLookupColumn(); 2217717c082SMichael Große if ($column) { 2227717c082SMichael Große return $column->getType()->rawValue($value); 2237717c082SMichael Große } else { 2247717c082SMichael Große return ''; 2257717c082SMichael Große } 2267717c082SMichael Große } 2277717c082SMichael Große 2287717c082SMichael Große 2297717c082SMichael Große /** 2309b8dc0b2SAndreas Gohr * Merge with lookup table 2319b8dc0b2SAndreas Gohr * 2329b8dc0b2SAndreas Gohr * @param QueryBuilder $QB 2339b8dc0b2SAndreas Gohr * @param string $tablealias 2349b8dc0b2SAndreas Gohr * @param string $colname 2359b8dc0b2SAndreas Gohr * @param string $alias 2369b8dc0b2SAndreas Gohr */ 237d6d97f60SAnna Dabrowska public function select(QueryBuilder $QB, $tablealias, $colname, $alias) 238d6d97f60SAnna Dabrowska { 2399b8dc0b2SAndreas Gohr $schema = 'data_' . $this->config['schema']; 2409b8dc0b2SAndreas Gohr $column = $this->getLookupColumn(); 2419b8dc0b2SAndreas Gohr if (!$column) { 2429b8dc0b2SAndreas Gohr parent::select($QB, $tablealias, $colname, $alias); 2439b8dc0b2SAndreas Gohr return; 2449b8dc0b2SAndreas Gohr } 2459b8dc0b2SAndreas Gohr 2469b8dc0b2SAndreas Gohr $field = $column->getColName(); 2479b8dc0b2SAndreas Gohr $rightalias = $QB->generateTableAlias(); 2489b8dc0b2SAndreas Gohr $QB->addLeftJoin( 249d6d97f60SAnna Dabrowska $tablealias, 250d6d97f60SAnna Dabrowska $schema, 251d6d97f60SAnna Dabrowska $rightalias, 252cdd16494SAnna Dabrowska "STRUCT_LOOKUP($tablealias.$colname, 0) = $rightalias.pid " . 253cdd16494SAnna Dabrowska "AND STRUCT_LOOKUP($tablealias.$colname, 1) = $rightalias.rid " . 25417a3a578SAndreas Gohr "AND $rightalias.latest = 1" 2559b8dc0b2SAndreas Gohr ); 2569b8dc0b2SAndreas Gohr $column->getType()->select($QB, $rightalias, $field, $alias); 2579b8dc0b2SAndreas Gohr $sql = $QB->getSelectStatement($alias); 2589b8dc0b2SAndreas Gohr $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $sql)", $alias); 2599b8dc0b2SAndreas Gohr } 2609b8dc0b2SAndreas Gohr 2619b8dc0b2SAndreas Gohr /** 2629b8dc0b2SAndreas Gohr * Compare against lookup table 2639b8dc0b2SAndreas Gohr * 264af993d55SMichael Grosse * @param QueryBuilderWhere $add 2659b8dc0b2SAndreas Gohr * @param string $tablealias 2669b8dc0b2SAndreas Gohr * @param string $colname 2679b8dc0b2SAndreas Gohr * @param string $comp 2689b8dc0b2SAndreas Gohr * @param string|\string[] $value 2699b8dc0b2SAndreas Gohr * @param string $op 2709b8dc0b2SAndreas Gohr */ 271d6d97f60SAnna Dabrowska public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op) 272d6d97f60SAnna Dabrowska { 2739b8dc0b2SAndreas Gohr $schema = 'data_' . $this->config['schema']; 2749b8dc0b2SAndreas Gohr $column = $this->getLookupColumn(); 2759b8dc0b2SAndreas Gohr if (!$column) { 276af993d55SMichael Grosse parent::filter($add, $tablealias, $colname, $comp, $value, $op); 2779b8dc0b2SAndreas Gohr return; 2789b8dc0b2SAndreas Gohr } 2799b8dc0b2SAndreas Gohr $field = $column->getColName(); 2809b8dc0b2SAndreas Gohr 2819b8dc0b2SAndreas Gohr // compare against lookup field 282af993d55SMichael Grosse $QB = $add->getQB(); 2839b8dc0b2SAndreas Gohr $rightalias = $QB->generateTableAlias(); 2849b8dc0b2SAndreas Gohr $QB->addLeftJoin( 285d6d97f60SAnna Dabrowska $tablealias, 286d6d97f60SAnna Dabrowska $schema, 287d6d97f60SAnna Dabrowska $rightalias, 28817a3a578SAndreas Gohr "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " . 28917a3a578SAndreas Gohr "$rightalias.latest = 1" 2909b8dc0b2SAndreas Gohr ); 291af993d55SMichael Grosse $column->getType()->filter($add, $rightalias, $field, $comp, $value, $op); 2929b8dc0b2SAndreas Gohr } 2939b8dc0b2SAndreas Gohr 2949b8dc0b2SAndreas Gohr /** 2959b8dc0b2SAndreas Gohr * Sort by lookup table 2969b8dc0b2SAndreas Gohr * 2979b8dc0b2SAndreas Gohr * @param QueryBuilder $QB 2989b8dc0b2SAndreas Gohr * @param string $tablealias 2999b8dc0b2SAndreas Gohr * @param string $colname 3009b8dc0b2SAndreas Gohr * @param string $order 3019b8dc0b2SAndreas Gohr */ 302d6d97f60SAnna Dabrowska public function sort(QueryBuilder $QB, $tablealias, $colname, $order) 303d6d97f60SAnna Dabrowska { 3049b8dc0b2SAndreas Gohr $schema = 'data_' . $this->config['schema']; 3059b8dc0b2SAndreas Gohr $column = $this->getLookupColumn(); 3069b8dc0b2SAndreas Gohr if (!$column) { 3079b8dc0b2SAndreas Gohr parent::sort($QB, $tablealias, $colname, $order); 3089b8dc0b2SAndreas Gohr return; 3099b8dc0b2SAndreas Gohr } 3109b8dc0b2SAndreas Gohr $field = $column->getColName(); 3119b8dc0b2SAndreas Gohr 3129b8dc0b2SAndreas Gohr $rightalias = $QB->generateTableAlias(); 3139b8dc0b2SAndreas Gohr $QB->addLeftJoin( 314d6d97f60SAnna Dabrowska $tablealias, 315d6d97f60SAnna Dabrowska $schema, 316d6d97f60SAnna Dabrowska $rightalias, 31717a3a578SAndreas Gohr "$tablealias.$colname = STRUCT_JSON($rightalias.pid, CAST($rightalias.rid AS DECIMAL)) AND " . 31817a3a578SAndreas Gohr "$rightalias.latest = 1" 3199b8dc0b2SAndreas Gohr ); 3209b8dc0b2SAndreas Gohr $column->getType()->sort($QB, $rightalias, $field, $order); 3219b8dc0b2SAndreas Gohr } 3229b8dc0b2SAndreas Gohr} 323