1<?php 2namespace plugin\struct\types; 3use plugin\struct\meta\StructException; 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 * @var \DokuWiki_Plugin 46 */ 47 protected $hlp = null; 48 49 /** 50 * AbstractBaseType constructor. 51 * @param array|null $config The configuration, might be null if nothing saved, yet 52 * @param string $label The label for this field (empty for new definitions= 53 * @param bool $ismulti Should this field accept multiple values? 54 * @param int $tid The id of this type if it has been saved, yet 55 */ 56 public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) { 57 // initialize the configuration, ignoring all keys that are not supposed to be here 58 if(!is_null($config)) { 59 foreach($config as $key => $value) { 60 if(isset($this->config[$key]) || in_array($key, $this->keepconfig)) { 61 $this->config[$key] = $value; 62 } 63 } 64 } 65 66 $this->initTransConfig(); 67 $this->label = $label; 68 $this->ismulti = (bool) $ismulti; 69 $this->tid = $tid; 70 } 71 72 /** 73 * Add the translation keys to the configuration 74 * 75 * This checks if a configuration for the translation plugin exists and if so 76 * adds all configured languages to the config array. This ensures all types 77 * can have translatable labels. 78 */ 79 protected function initTransConfig() { 80 global $conf; 81 $lang = $conf['lang']; 82 if(isset($conf['plugin']['translation']['translations'])) { 83 $lang .= ' ' . $conf['plugin']['translation']['translations']; 84 } 85 $langs = explode(' ', $lang); 86 $langs = array_map('trim', $langs); 87 $langs = array_filter($langs); 88 $langs = array_unique($langs); 89 90 if(!isset($this->config['translation'])) $this->config['translation'] = array(); 91 foreach($langs as $lang) { 92 if(!isset($this->config['translation'][$lang])) $this->config['translation'][$lang] = ''; 93 } 94 } 95 96 /** 97 * Returns data as associative array 98 * 99 * @return array 100 */ 101 public function getAsEntry() { 102 return array( 103 'config' => json_encode($this->config), 104 'label' => $this->label, 105 'ismulti' => $this->ismulti, 106 'class' => $this->getClass() 107 ); 108 } 109 110 /** 111 * The class name of this type (no namespace) 112 * @return string 113 */ 114 public function getClass() { 115 return substr(get_class($this), 20); 116 } 117 118 /** 119 * Return the current configuration for this type 120 * 121 * @return array 122 */ 123 public function getConfig() { 124 return $this->config; 125 } 126 127 /** 128 * @return boolean 129 */ 130 public function isMulti() { 131 return $this->ismulti; 132 } 133 134 /** 135 * @return string 136 */ 137 public function getLabel() { 138 return $this->label; 139 } 140 141 /** 142 * Returns the translated label for this type 143 * 144 * Uses the current language as determined by $conf['lang']. Falls back to english 145 * and then to the Schema label 146 * 147 * @return string 148 */ 149 public function getTranslatedLabel() { 150 global $conf; 151 $lang = $conf['lang']; 152 if(!blank($this->config['translation'][$lang])) { 153 return $this->config['translation'][$lang]; 154 } 155 if(!blank($this->config['translation']['en'])) { 156 return $this->config['translation']['en']; 157 } 158 return $this->label; 159 } 160 161 /** 162 * @return int 163 */ 164 public function getTid() { 165 return $this->tid; 166 } 167 168 /** 169 * Split a single value into multiple values 170 * 171 * This function is called on saving data when only a single value instead of an array 172 * was submitted. 173 * 174 * Types implementing their own @see multiValueEditor() will probably want to override this 175 * 176 * @param string $value 177 * @return array 178 */ 179 public function splitValues($value) { 180 return array_map('trim', explode(',', $value)); 181 } 182 183 /** 184 * Return the editor to edit multiple values 185 * 186 * Types can override this to provide a better alternative than multiple entry fields 187 * 188 * @param string $name the form base name where this has to be stored 189 * @param string[] $values the current values 190 * @return string html 191 */ 192 public function multiValueEditor($name, $values) { 193 $html = ''; 194 foreach($values as $value) { 195 $html .= '<div class="multiwrap">'; 196 $html .= $this->valueEditor($name . '[]', $value); 197 $html .= '</div>'; 198 } 199 // empty field to add 200 $html .= '<div class="newtemplate">'; 201 $html .= '<div class="multiwrap">'; 202 $html .= $this->valueEditor($name . '[]', ''); 203 $html .= '</div>'; 204 $html .= '</div>'; 205 206 return $html; 207 } 208 209 /** 210 * Return the editor to edit a single value 211 * 212 * @param string $name the form name where this has to be stored 213 * @param string $value the current value 214 * @return string html 215 */ 216 public function valueEditor($name, $value) { 217 $class = 'struct_'.strtolower($this->getClass()); 218 $name = hsc($name); 219 $value = hsc($value); 220 $html = "<input name=\"$name\" value=\"$value\" class=\"$class\" />"; 221 return "$html"; 222 } 223 224 /** 225 * Output the stored data 226 * 227 * @param string|int $value the value stored in the database 228 * @param \Doku_Renderer $R the renderer currently used to render the data 229 * @param string $mode The mode the output is rendered in (eg. XHTML) 230 * @return bool true if $mode could be satisfied 231 */ 232 public function renderValue($value, \Doku_Renderer $R, $mode) { 233 $R->cdata($value); 234 return true; 235 } 236 237 /** 238 * format and return the data 239 * 240 * @param int[]|string[] $values the values stored in the database 241 * @param \Doku_Renderer $R the renderer currently used to render the data 242 * @param string $mode The mode the output is rendered in (eg. XHTML) 243 * @return bool true if $mode could be satisfied 244 */ 245 public function renderMultiValue($values, \Doku_Renderer $R, $mode) { 246 $len = count($values); 247 for($i = 0; $i < $len; $i++) { 248 $this->renderValue($values[$i], $R, $mode); 249 if($i < $len - 1) { 250 $R->cdata(', '); 251 } 252 } 253 return true; 254 } 255 256 /** 257 * This function builds a where clause for this column, comparing 258 * the current value stored in $column with $value. Types can use it to do 259 * clever things with the comparison. 260 * 261 * This default implementation is probably good enough for most basic types 262 * 263 * @param string $column The column name to us in the SQL 264 * @param string $comp The comparator @see Search::$COMPARATORS 265 * @param string $value 266 * @return array Tuple with the SQL and parameter array 267 */ 268 public function compare($column, $comp, $value) { 269 switch ($comp) { 270 case '~': 271 $sql = "$column LIKE ?"; 272 $opt = array($value); 273 break; 274 case '!~': 275 $sql = "$column NOT LIKE ?"; 276 $opt = array($value); 277 break; 278 default: 279 $sql = "$column $comp ?"; 280 $opt = array($value); 281 } 282 283 return array($sql, $opt); 284 } 285 286 /** 287 * Validate a single value 288 * 289 * This function needs to throw a validation exception when validation fails. 290 * The exception message will be prefixed by the appropriate field on output 291 * 292 * @param string|int $value 293 * @throws ValidationException 294 */ 295 public function validate($value) { 296 // nothing by default - we allow everything 297 } 298 299 /** 300 * Overwrite to handle Ajax requests 301 * 302 * A call to DOKU_BASE/lib/exe/ajax.php?call=plugin_struct&column=schema.name will 303 * be redirected to this function on a fully initialized type. The result is 304 * JSON encoded and returned to the caller. Access additional parameter via $INPUT 305 * as usual 306 * 307 * @throws StructException when something goes wrong 308 * @return mixed 309 */ 310 public function handleAjax() { 311 throw new StructException('not implemented'); 312 } 313 314 /** 315 * Convenience method to access plugin language strings 316 * 317 * @param string $string 318 * @return string 319 */ 320 public function getLang($string) { 321 if(is_null($this->hlp)) $this->hlp = plugin_load('action', 'struct_autoloader'); 322 return $this->hlp->getLang($string); 323 } 324} 325