1<?php 2namespace plugin\struct\types; 3 4use plugin\struct\meta\ValidationException; 5 6/** 7 * Class AbstractBaseType 8 * 9 * This class represents a basic type that can be configured to be used in a Schema. It is the main 10 * part of a column definition as defined in meta\Column 11 * 12 * This defines also how the content of the coulmn will be entered and formatted. 13 * 14 * @package plugin\struct\types 15 * @see Column 16 */ 17abstract class AbstractBaseType { 18 19 /** 20 * @var array current config 21 */ 22 protected $config = array(); 23 24 /** 25 * @var array config keys that should not be cleaned despite not being in $config 26 */ 27 protected $keepconfig = array('translation'); 28 29 /** 30 * @var string label for the field 31 */ 32 protected $label = ''; 33 34 /** 35 * @var bool is this a multivalue field? 36 */ 37 protected $ismulti = false; 38 39 /** 40 * @var int the type ID 41 */ 42 protected $tid = 0; 43 44 /** 45 * AbstractBaseType constructor. 46 * @param array|null $config The configuration, might be null if nothing saved, yet 47 * @param string $label The label for this field (empty for new definitions= 48 * @param bool $ismulti Should this field accept multiple values? 49 * @param int $tid The id of this type if it has been saved, yet 50 */ 51 public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) { 52 // initialize the configuration, ignoring all keys that are not supposed to be here 53 if(!is_null($config)) { 54 foreach($config as $key => $value) { 55 if(isset($this->config[$key]) || in_array($key, $this->keepconfig)) { 56 $this->config[$key] = $value; 57 } 58 } 59 } 60 61 $this->initTransConfig(); 62 $this->label = $label; 63 $this->ismulti = (bool) $ismulti; 64 $this->tid = $tid; 65 } 66 67 /** 68 * Add the translation keys to the configuration 69 * 70 * This checks if a configuration for the translation plugin exists and if so 71 * adds all configured languages to the config array. This ensures all types 72 * can have translatable labels. 73 */ 74 protected function initTransConfig() { 75 global $conf; 76 $lang = $conf['lang']; 77 if(isset($conf['plugin']['translation']['translations'])) { 78 $lang .= ' ' . $conf['plugin']['translation']['translations']; 79 } 80 $langs = explode(' ', $lang); 81 $langs = array_map('trim', $langs); 82 $langs = array_filter($langs); 83 $langs = array_unique($langs); 84 85 if(!isset($this->config['translation'])) $this->config['translation'] = array(); 86 foreach($langs as $lang) { 87 if(!isset($this->config['translation'][$lang])) $this->config['translation'][$lang] = ''; 88 } 89 } 90 91 /** 92 * Returns data as associative array 93 * 94 * @return array 95 */ 96 public function getAsEntry() { 97 return array( 98 'config' => json_encode($this->config), 99 'label' => $this->label, 100 'ismulti' => $this->ismulti, 101 'class' => $this->getClass() 102 ); 103 } 104 105 /** 106 * The class name of this type (no namespace) 107 * @return string 108 */ 109 public function getClass() { 110 return substr(get_class($this), 20); 111 } 112 113 /** 114 * Return the current configuration for this type 115 * 116 * @return array 117 */ 118 public function getConfig() { 119 return $this->config; 120 } 121 122 /** 123 * @return boolean 124 */ 125 public function isMulti() { 126 return $this->ismulti; 127 } 128 129 /** 130 * @return string 131 */ 132 public function getLabel() { 133 return $this->label; 134 } 135 136 /** 137 * Returns the translated label for this type 138 * 139 * Uses the current language as determined by $conf['lang']. Falls back to english 140 * and then to the Schema label 141 * 142 * @return string 143 */ 144 public function getTranslatedLabel() { 145 global $conf; 146 $lang = $conf['lang']; 147 if(!blank($this->config['translation'][$lang])) { 148 return $this->config['translation'][$lang]; 149 } 150 if(!blank($this->config['translation']['en'])) { 151 return $this->config['translation']['en']; 152 } 153 return $this->label; 154 } 155 156 /** 157 * @return int 158 */ 159 public function getTid() { 160 return $this->tid; 161 } 162 163 /** 164 * Split a single value into multiple values 165 * 166 * This function is called on saving data when only a single value instead of an array 167 * was submitted. 168 * 169 * Types implementing their own @see multiValueEditor() will probably want to override this 170 * 171 * @param string $value 172 * @return array 173 */ 174 public function splitValues($value) { 175 return array_map('trim', explode(',', $value)); 176 } 177 178 /** 179 * Return the editor to edit multiple values 180 * 181 * Types can override this to provide a better alternative than multiple entry fields 182 * 183 * @param string $name the form base name where this has to be stored 184 * @param string[] $values the current values 185 * @return string html 186 */ 187 public function multiValueEditor($name, $values) { 188 $html = ''; 189 foreach($values as $value) { 190 $html .= '<div class="multiwrap">'; 191 $html .= $this->valueEditor($name . '[]', $value); 192 $html .= '</div>'; 193 } 194 // empty field to add 195 $html .= '<div class="newtemplate">'; 196 $html .= '<div class="multiwrap">'; 197 $html .= $this->valueEditor($name . '[]', ''); 198 $html .= '</div>'; 199 $html .= '</div>'; 200 201 return $html; 202 } 203 204 /** 205 * Return the editor to edit a single value 206 * 207 * @param string $name the form name where this has to be stored 208 * @param string $value the current value 209 * @return string html 210 */ 211 public function valueEditor($name, $value) { 212 $name = hsc($name); 213 $value = hsc($value); 214 $html = "<input name=\"$name\" value=\"$value\" />"; 215 return "$html"; 216 } 217 218 /** 219 * Output the stored data 220 * 221 * @param string|int $value the value stored in the database 222 * @param \Doku_Renderer $R the renderer currently used to render the data 223 * @param string $mode The mode the output is rendered in (eg. XHTML) 224 * @return bool true if $mode could be satisfied 225 */ 226 public function renderValue($value, \Doku_Renderer $R, $mode) { 227 $R->cdata($value); 228 return true; 229 } 230 231 /** 232 * format and return the data 233 * 234 * @param int[]|string[] $values the values stored in the database 235 * @param \Doku_Renderer $R the renderer currently used to render the data 236 * @param string $mode The mode the output is rendered in (eg. XHTML) 237 * @return bool true if $mode could be satisfied 238 */ 239 public function renderMultiValue($values, \Doku_Renderer $R, $mode) { 240 $len = count($values); 241 for($i = 0; $i < $len; $i++) { 242 $this->renderValue($values[$i], $R, $mode); 243 if($i < $len - 1) { 244 $R->cdata(', '); 245 } 246 } 247 return true; 248 } 249 250 /** 251 * This function builds a where clause for this column, comparing 252 * the current value stored in $column with $value. Types can use it to do 253 * clever things with the comparison. 254 * 255 * This default implementation is probably good enough for most basic types 256 * 257 * @param string $column The column name to us in the SQL 258 * @param string $comp The comparator @see Search::$COMPARATORS 259 * @param string $value 260 * @return array Tuple with the SQL and parameter array 261 */ 262 public function compare($column, $comp, $value) { 263 switch ($comp) { 264 case '~': 265 $sql = "$column LIKE ?"; 266 $opt = array($value); 267 break; 268 case '!~': 269 $sql = "$column NOT LIKE ?"; 270 $opt = array($value); 271 break; 272 default: 273 $sql = "$column $comp ?"; 274 $opt = array($value); 275 } 276 277 return array($sql, $opt); 278 } 279 280 /** 281 * Validate a single value 282 * 283 * This function needs to throw a validation exception when validation fails. 284 * The exception message will be prefixed by the appropriate field on output 285 * 286 * @param string|int $value 287 * @throws ValidationException 288 */ 289 public function validate($value) { 290 // nothing by default - we allow everything 291 } 292} 293