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\Schema; 7use dokuwiki\plugin\struct\meta\Search; 8use dokuwiki\plugin\struct\meta\Value; 9 10class Dropdown extends AbstractBaseType { 11 12 protected $config = array( 13 'values' => 'one, two, three', 14 'schema' => '', 15 'field' => '' 16 ); 17 18 /** @var Schema */ 19 protected $schema = null; 20 /** @var Column */ 21 protected $column = null; 22 23 /** 24 * Dropdown constructor. 25 * 26 * @param array|null $config 27 * @param string $label 28 * @param bool $ismulti 29 * @param int $tid 30 */ 31 public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) { 32 global $conf; 33 34 parent::__construct($config, $label, $ismulti, $tid); 35 $this->config['schema'] = Schema::cleanTableName($this->config['schema']); 36 37 if($this->usesLookup()) { 38 $this->schema = new Schema($this->config['schema']); 39 if(!$this->schema->getId()) { 40 // schema does not exist 41 msg(sprintf('Schema %s does not exist', $this->config['schema']), -1); 42 $this->schema = null; 43 $this->config['schema'] = ''; 44 return; 45 } 46 47 // apply language replacement 48 $field = str_replace('$LANG', $conf['lang'], $this->config['field']); 49 $this->column = $this->schema->findColumn($field); 50 if(!$this->column) { 51 $field = str_replace('$LANG', 'en', $this->config['field']); // fallback to en 52 $this->column = $this->schema->findColumn($field); 53 } 54 if(!$this->column) { 55 // field does not exist 56 msg(sprintf('Field %s.%s does not exist', $this->config['schema'], $this->config['field']), -1); 57 $this->column = null; 58 $this->config['field'] = ''; 59 return; 60 } 61 62 if($this->column->isMulti()) { 63 // field is multi 64 msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $this->config['schema'], $this->config['field']), -1); 65 $this->column = null; 66 $this->config['field'] = ''; 67 return; 68 } 69 } 70 } 71 72 /** 73 * @return bool is this dropdown configured to use a lookup? 74 */ 75 protected function usesLookup() { 76 return !blank($this->config['schema']) && !blank($this->config['field']); 77 } 78 79 /** 80 * Creates the options array 81 * 82 * @return array 83 */ 84 protected function getOptions() { 85 if($this->usesLookup()) { 86 $options = $this->loadLookupData(); 87 } else { 88 $options = explode(',', $this->config['values']); 89 $options = array_map('trim', $options); 90 $options = array_filter($options); 91 array_unshift($options, ''); 92 $options = array_combine($options, $options); 93 } 94 return $options; 95 } 96 97 /** 98 * Loads all available lookup values 99 * 100 * @return array 101 */ 102 protected function loadLookupData() { 103 $schema = $this->schema->getTable(); 104 $field = $this->column->getLabel(); 105 106 $search = new Search(); 107 $search->addSchema($schema); 108 $search->addColumn($field); 109 $search->addSort($field); 110 $result = $search->execute(); 111 $pids = $search->getPids(); 112 $len = count($result); 113 114 /** @var Value[][] $result */ 115 $options = array('' => ''); 116 for($i = 0; $i < $len; $i++) { 117 $options[$pids[$i]] = $result[$i][0]->getDisplayValue(); 118 } 119 return $options; 120 } 121 122 /** 123 * Render using linked field 124 * 125 * @param int|string $value 126 * @param \Doku_Renderer $R 127 * @param string $mode 128 * @return bool 129 */ 130 public function renderValue($value, \Doku_Renderer $R, $mode) { 131 if(!$this->usesLookup()) { 132 return parent::renderValue($value, $R, $mode); 133 } else { 134 list(, $value) = json_decode($value); 135 return $this->column->getType()->renderValue($value, $R, $mode); 136 } 137 } 138 139 /** 140 * Render using linked field 141 * 142 * @param \int[]|\string[] $values 143 * @param \Doku_Renderer $R 144 * @param string $mode 145 * @return bool 146 */ 147 public function renderMultiValue($values, \Doku_Renderer $R, $mode) { 148 if(!$this->usesLookup()) { 149 return parent::renderMultiValue($values, $R, $mode); 150 } else { 151 $values = array_map( 152 function ($val) { 153 list(, $val) = json_decode($val); 154 return $val; 155 }, $values 156 ); 157 return $this->column->getType()->renderMultiValue($values, $R, $mode); 158 } 159 } 160 161 /** 162 * A Dropdown with a single value to pick 163 * 164 * @param string $name 165 * @param string $rawvalue 166 * @return string 167 */ 168 public function valueEditor($name, $rawvalue) { 169 $class = 'struct_' . strtolower($this->getClass()); 170 171 $name = hsc($name); 172 $html = "<select name=\"$name\" class=\"$class\">"; 173 foreach($this->getOptions() as $opt => $val) { 174 if($opt == $rawvalue) { 175 $selected = 'selected="selected"'; 176 } else { 177 $selected = ''; 178 } 179 180 $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($val) . '</option>'; 181 } 182 $html .= '</select>'; 183 184 return $html; 185 } 186 187 /** 188 * A dropdown that allows to pick multiple values 189 * 190 * @param string $name 191 * @param \string[] $rawvalues 192 * @return string 193 */ 194 public function multiValueEditor($name, $rawvalues) { 195 $class = 'struct_' . strtolower($this->getClass()); 196 197 $name = hsc($name); 198 $html = "<select name=\"{$name}[]\" class=\"$class\" multiple=\"multiple\" size=\"5\">"; 199 foreach($this->getOptions() as $opt) { 200 if(in_array($opt, $rawvalues)) { 201 $selected = 'selected="selected"'; 202 } else { 203 $selected = ''; 204 } 205 206 $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($opt) . '</option>'; 207 208 } 209 $html .= '</select> '; 210 $html .= '<small>' . $this->getLang('multidropdown') . '</small>'; 211 return $html; 212 } 213 214 /** 215 * @param string $value 216 * @return string 217 */ 218 public function rawValue($value) { 219 if($this->usesLookup()) { 220 list($value) = json_decode($value); 221 } 222 return $value; 223 } 224 225 /** 226 * @param string $value 227 * @return string 228 */ 229 public function displayValue($value) { 230 if($this->usesLookup()) { 231 list(, $value) = json_decode($value); 232 $value = $this->column->getType()->displayValue($value); 233 } 234 return $value; 235 } 236 237 /** 238 * Merge with lookup table 239 * 240 * @param QueryBuilder $QB 241 * @param string $tablealias 242 * @param string $colname 243 * @param string $alias 244 */ 245 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) { 246 if(!$this->usesLookup()) { 247 parent::select($QB, $tablealias, $colname, $alias); 248 return; 249 } 250 251 $schema = 'data_' . $this->schema->getTable(); 252 $field = $this->column->getColName(); 253 254 $rightalias = $QB->generateTableAlias(); 255 $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid"); 256 $this->column->getType()->select($QB, $rightalias, $field, $alias); 257 $sql = $QB->getSelectStatement($alias); 258 $QB->addSelectStatement("JSON($tablealias.$colname, $sql)", $alias); 259 } 260 261 /** 262 * Compare against lookup table 263 * 264 * @param QueryBuilder $QB 265 * @param string $tablealias 266 * @param string $colname 267 * @param string $comp 268 * @param string|\string[] $value 269 * @param string $op 270 */ 271 public function filter(QueryBuilder $QB, $tablealias, $colname, $comp, $value, $op) { 272 if(!$this->usesLookup()) { 273 parent::filter($QB, $tablealias, $colname, $comp, $value, $op); 274 return; 275 } 276 277 $schema = 'data_' . $this->schema->getTable(); 278 $field = $this->column->getColName(); 279 280 // compare against lookup field 281 $rightalias = $QB->generateTableAlias(); 282 $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid"); 283 $this->column->getType()->filter($QB, $rightalias, $field, $comp, $value, $op); 284 } 285 286 /** 287 * Sort by lookup table 288 * 289 * @param QueryBuilder $QB 290 * @param string $tablealias 291 * @param string $colname 292 * @param string $order 293 */ 294 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) { 295 if(!$this->usesLookup()) { 296 parent::sort($QB, $tablealias, $colname, $order); 297 return; 298 } 299 300 $schema = 'data_' . $this->schema->getTable(); 301 $field = $this->column->getColName(); 302 303 $rightalias = $QB->generateTableAlias(); 304 $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid"); 305 $this->column->getType()->sort($QB, $rightalias, $field, $order); 306 } 307 308} 309