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 * A Dropdown with a single value to pick 124 * 125 * @param string $name 126 * @param string $value 127 * @param bool $isRaw ignored 128 * @return string 129 */ 130 public function valueEditor($name, $value, $isRaw = false) { 131 $class = 'struct_' . strtolower($this->getClass()); 132 133 if(!$isRaw) $value = $this->rawValue($value); 134 135 $name = hsc($name); 136 $html = "<select name=\"$name\" class=\"$class\">"; 137 foreach($this->getOptions() as $opt => $val) { 138 if($opt == $value) { 139 $selected = 'selected="selected"'; 140 } else { 141 $selected = ''; 142 } 143 144 $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($val) . '</option>'; 145 } 146 $html .= '</select>'; 147 148 return $html; 149 } 150 151 /** 152 * A dropdown that allows to pick multiple values 153 * 154 * @param string $name 155 * @param \string[] $values 156 * @return string 157 */ 158 public function multiValueEditor($name, $values) { 159 $class = 'struct_' . strtolower($this->getClass()); 160 161 $values = array_map(array($this, 'rawValue'), $values); 162 163 $name = hsc($name); 164 $html = "<select name=\"{$name}[]\" class=\"$class\" multiple=\"multiple\" size=\"5\">"; 165 foreach($this->getOptions() as $opt) { 166 if(in_array($opt, $values)) { 167 $selected = 'selected="selected"'; 168 } else { 169 $selected = ''; 170 } 171 172 $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($opt) . '</option>'; 173 174 } 175 $html .= '</select> '; 176 $html .= '<small>' . $this->getLang('multidropdown') . '</small>'; 177 return $html; 178 } 179 180 /** 181 * @param string $value 182 * @return string 183 */ 184 public function rawValue($value) { 185 if($this->usesLookup()) { 186 list($value) = json_decode($value); 187 } 188 return $value; 189 } 190 191 /** 192 * @param string $value 193 * @return string 194 */ 195 public function displayValue($value) { 196 if($this->usesLookup()) { 197 list(, $value) = json_decode($value); 198 $value = $this->column->getType()->displayValue($value); 199 } 200 return $value; 201 } 202 203 /** 204 * Merge with lookup table 205 * 206 * @param QueryBuilder $QB 207 * @param string $tablealias 208 * @param string $colname 209 * @param string $alias 210 */ 211 public function select(QueryBuilder $QB, $tablealias, $colname, $alias) { 212 if(!$this->usesLookup()) { 213 parent::select($QB, $tablealias, $colname, $alias); 214 return; 215 } 216 217 $schema = 'data_'.$this->schema->getTable(); 218 $field = $this->column->getColName(); 219 220 $rightalias = $QB->generateTableAlias(); 221 $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid"); 222 $this->column->getType()->select($QB, $rightalias, $field, $alias); 223 $sql = $QB->getSelectStatement($alias); 224 $QB->addSelectStatement("JSON($tablealias.$colname, $sql)", $alias); 225 } 226 227 /** 228 * Compare against lookup table 229 * 230 * @param QueryBuilder $QB 231 * @param string $tablealias 232 * @param string $colname 233 * @param string $comp 234 * @param string|\string[] $value 235 * @param string $op 236 */ 237 public function filter(QueryBuilder $QB, $tablealias, $colname, $comp, $value, $op) { 238 if(!$this->usesLookup()) { 239 parent::filter($QB, $tablealias, $colname, $comp, $value, $op); 240 return; 241 } 242 243 $schema = 'data_'.$this->schema->getTable(); 244 $field = $this->column->getColName(); 245 246 // compare against lookup field 247 $rightalias = $QB->generateTableAlias(); 248 $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid"); 249 $this->column->getType()->filter($QB, $rightalias, $field, $comp, $value, $op); 250 } 251 252 /** 253 * Sort by lookup table 254 * 255 * @param QueryBuilder $QB 256 * @param string $tablealias 257 * @param string $colname 258 * @param string $order 259 */ 260 public function sort(QueryBuilder $QB, $tablealias, $colname, $order) { 261 if(!$this->usesLookup()) { 262 parent::sort($QB, $tablealias, $colname, $order); 263 return; 264 } 265 266 $schema = 'data_'.$this->schema->getTable(); 267 $field = $this->column->getColName(); 268 269 $rightalias = $QB->generateTableAlias(); 270 $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid"); 271 $this->column->getType()->sort($QB, $rightalias, $field, $order); 272 } 273 274} 275