wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.FormUtil.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.FormUtil.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/util/class.Message.php");
21 require_once(BASE."wcmf/lib/util/class.StringUtil.php");
22 require_once(BASE."wcmf/lib/util/class.InifileParser.php");
23 require_once(BASE."wcmf/lib/presentation/class.DefaultControlRenderer.php");
24 require_once(BASE."wcmf/lib/util/class.SessionData.php");
25 require_once(BASE."wcmf/lib/i18n/class.Localization.php");
26 
27 /**
28  * @class FormUtil
29  * @ingroup Util
30  * @brief FormUtil provides basic support for HTML forms.
31  * It's mainly for creating input controls from definition strings.
32  *
33  * @author ingo herwig <ingo@wemove.com>
34  */
35 class FormUtil
36 {
37  var $_language = null;
38  var $_controlRenderer = null;
39  var $_lists = array();
40 
41  /**
42  * Constructor
43  * @param language The lanugage if FormUtil should be localization aware. Optional,
44  * default is Localization::getDefaultLanguage()
45  */
46  public function FormUtil($language=null)
47  {
48  $this->_language = $language;
49 
50  // create the control renderer instance
51  $objectFactory = &ObjectFactory::getInstance();
52  $this->_controlRenderer = &$objectFactory->createInstanceFromConfig('implementation', 'ControlRenderer');
53  if ($this->_controlRenderer == null) {
54  WCMFException::throwEx('ControlRenderer not defined in section implementation.', __FILE__, __LINE__);
55  }
56  }
57 
58  /**
59  * Get the delimiter for HTML input control names to be used if a control name
60  * consists of different parts.
61  * @return The delimitor
62  * @note If 'inputFieldNameDelimiter' is given in the configuration file (section 'htmlform')
63  * it will be taken (else it defaults to '-').
64  */
65  public static function getInputFieldDelimiter()
66  {
67  $FIELD_DELIMITER = '-';
68  // try to get default field delimiter
69  $parser = InifileParser::getInstance();
70  if(($fieldDelimiter = $parser->getValue('inputFieldNameDelimiter', 'htmlform')) === false) {
71  $fieldDelimiter = $FIELD_DELIMITER;
72  }
73  return $fieldDelimiter;
74  }
75  /**
76  * Get a HTML input control for a given description.
77  * @param name The name of the control (HTML name attribute)
78  * @param inputType The description of the control as given in the input_type property of a value
79  * The description is of the form @code type @endcode or @code type[attributes]#list @endcode
80  * where list must be given for the types select, radio and checkbox
81  * - type: text|password|textarea|select|radio|checkbox|file|fileex(= file with delete checkbox)
82  * - attributes: a string of attributes for the input control as used in the HTML definition (e.g. 'cols="50" rows="4"')
83  * - list: one of the following:
84  * - fix:key1[val1]|key2[val2]|... or fix:$global_array_variable
85  * - db:key[val]|table
86  * - fkt:name|param1,param2,... global function
87  * - config:section
88  * @param value The predefined value of the control (maybe comma separated list for list controls)
89  * @param editable True/False if this is set false the function returns only the translated value (processed by translateValue()) [default: true]
90  * @param addEmptyValue True/False if this is set true, an additional empty value is added to the input control (if not given no empty value will be added) [default: false]
91  * @return The HTML control string or the translated value string depending in the editable parameter
92  * @see DefaultControlRenderer::renderControl
93  */
94  public function getInputControl($name, $inputType, $value, $editable=true, $addEmptyValue=false)
95  {
96  $value = strval($value);
97  $htmlString = '';
98  // get definition and list from description
99  if (strPos($inputType, '#'))
100  {
101  list($def, $list) = split('#', $inputType, 2);
102  $listMap = $this->getListMap($list, $value, null, $addEmptyValue);
103  }
104  else {
105  $def = $inputType;
106  }
107 
108  // if editable, make the value a list if we have a list type and the value contains comma separators
109  // if not editable, translate the value (using the translateValue() method)
110  if ($editable)
111  {
112  if ($list != '' && strPos($value, ',')) {
113  $value = split(",", $value);
114  }
115  else {
116  $value = htmlspecialchars($value);
117  }
118  }
119  else {
120  $value = $this->translateValue($value, $inputType);
121  }
122 
123  // get type and attributes from definition
124  preg_match_all("/[\w][^\[\]]+/", $def, $matches);
125  if (sizeOf($matches[0]) > 0) {
126  list($type, $attributes) = $matches[0];
127  }
128  if (!$type || $type == '') {
129  $type = 'text';
130  }
131 
132  // add '[]' to name if 'multiple' selection is given in attributes
133  if (strPos($attributes, 'multiple')) {
134  $name .= '[]';
135  }
136  // get error from session
137  $session = &SessionData::getInstance();
138 
139  // build input control
140  return $this->_controlRenderer->renderControl($type, $editable, $name, $value, $session->getError($name), $attributes, $listMap, $inputType);
141  }
142  /**
143  * Get a list of key/value pairs defined by description.
144  * @param description One of the following strings:
145  * - fix:key1[val1]|key2[val2]|... or fix:$global_array_variable
146  * - db:key[val]|table
147  * - fkt:name|param1,param2,... global function
148  * - config:section
149  * @param value The selected value (maybe null, default: null)
150  * @param nodeOid Oid of the node containing this value (for determining remote oids) [default: null]
151  * @param addEmptyValue True/False if this is set true, an additional empty value is added to the input control (if not given no empty value will be added) [default: false]
152  * @return An assoziative array containing the key/value pairs
153  * @note The method will try to translate values with Message::get().
154  * Keys and values are encoded using htmlentities(string, ENT_QUOTES, 'UTF-8').
155  */
156  private function getListMap($description, $value=null, $nodeOid=null, $addEmptyValue=false)
157  {
158  // check for cached result (only available if nodeOid == null)
159  $mapKey = $description.$value.$addEmptyValue;
160  if (isset($this->_lists[$mapKey])) {
161  return $this->_lists[$mapKey];
162  }
163 
164  $map = array();
165  // get type and list from description
166  if (!strPos($description, ':')) {
167  WCMFException::throwEx("No type found in list definition: ".$description, __FILE__, __LINE__);
168  }
169  else {
170  list($type, $list) = split(':', $description, 2);
171  }
172 
173  // build list
174  switch ($type)
175  {
176  case 'fix':
177  // see if we have an array variable or a list definition
178  if (strPos($list, '$') === 0) {
179  $entries = $GLOBALS[subStr($list,1)];
180  }
181  else {
182  $entries = split('\|', $list);
183  }
184  if (!is_array($entries))
185  {
186  WCMFException::throwEx($list." is no array.", __FILE__, __LINE__);
187  return array();
188  }
189  // process list
190  foreach($entries as $curEntry)
191  {
192  preg_match_all("/([^\[]*)\[*([^\]]*)\]*/", $curEntry, $matches);
193  if (sizeOf($matches) > 0)
194  {
195  $val1 = htmlentities($matches[1][0], ENT_QUOTES, 'UTF-8');
196  $val2 = htmlentities($matches[2][0], ENT_QUOTES, 'UTF-8');
197 
198  if (!function_exists('html_entity_decode')) {
199  $val1 = html_entity_decode($val1, ENT_QUOTES, 'UTF-8');
200  }
201  if ($val2 != '')
202  {
203  // value given
204  $map[$val1] = $val2;
205  }
206  else
207  {
208  // only key given
209  $map[$val1] = $val1;
210  }
211  }
212  }
213  break;
214  case 'db':
215  WCMFException::throwEx('db list type is not implemented yet!', __FILE__, __LINE__);
216  break;
217  case 'fkt':
218  // maybe there are '|' chars in parameters
219  $parts = split('\|', $list);
220  $name = array_shift($parts);
221  $params = join('|', $parts);
222  if (function_exists($name)) {
223  $map = call_user_func_array($name, split(',', $params));
224  }
225  else {
226  WCMFException::throwEx('Function '.$name.' is not defined globally!', __FILE__, __LINE__);
227  }
228  break;
229  case 'config':
230  $parser = &InifileParser::getInstance();
231  $map = $parser->getSection($list);
232  if (($map = $parser->getSection($list, false)) === false) {
233  WCMFException::throwEx($parser->getErrorMsg(), __FILE__, __LINE__);
234  }
235  break;
236  case 'async':
237  // load the translated value only
238  $parts = split('\|', $list);
239  $entityType = array_shift($parts);
240  //check for (remote) oid
241  if (PersistenceFacade::isValidOID($nodeOid)) {
242  $oidParts = PersistenceFacade::decomposeOID($nodeOid);
243  $oidParts['type'] = $entityType;
244  $oidParts['id'] = $value;
245 
246  $typeOid = PersistenceFacade::composeOID($oidParts);
247 
248  if (PersistenceFacade::isValidOID($typeOid)) {
249  $map[$value] = $this->resolveByOid($typeOid);
250  }
251  } else {
252  // since this may be a multivalue field, the ids may be separated by commas
253  $ids = split(',', $value);
254  foreach ($ids as $id)
255  {
256  //$oid may be pre-set
257  $oid = PersistenceFacade::composeOID(array('type' => $entityType, 'id' => $id));
258  $resolvedValue = $this->resolveByOid($oid);
259  if ($resolvedValue) {
260  $map[$id] = $resolvedValue;
261  }
262  }
263  }
264  // fallback if the value can not be interpreted as an oid or the object does not exist
265  if (sizeof($map) == 0) {
266  $map = array($value => $value);
267  }
268  break;
269  case 'asyncmult':
270  // load the translated value only
271  $parts = split('\|', $list);
272  $persistenceFacade = &PersistenceFacade::getInstance();
273  foreach($parts as $key=>$entityType)
274  {
275  // since this may be a multivalue field, the ids may be separated by commas
276  $ids = split(',', $value);
277  foreach ($ids as $id)
278  {
279  $oid = PersistenceFacade::composeOID(array('type' => $entityType, 'id' => $id));
281  {
282  $localization = &Localization::getInstance();
283  $obj = &$persistenceFacade->load($oid, BUILDDEPTH_SINGLE);
284  if ($obj != null) {
285  // localize object if requested
286  if ($this->_language != null) {
287  $localization->loadTranslation($obj, $this->_language);
288  }
289  $map[$id] = $obj->getDisplayValue();
290  }
291  }
292  }
293  }
294  // fallback if the value can not be interpreted as an oid or the object does not exist
295  if (sizeof($map) == 0) {
296  $map = array($value => $value);
297  }
298  break;
299  default:
300  WCMFException::throwEx('Unknown list type: '.$type, __FILE__, __LINE__);
301  break;
302  }
303 
304  // translate
305  $result = array();
306  foreach($map as $key => $value) {
307  $result[strval($key)] = strval(Message::get($value));
308  }
309 
310  // add an additional empty option if desired and not already existing
311  if ($addEmptyValue == true && !array_key_exists('', $result)) {
312  $result = array('' => Message::get('keine Angabe')) + $result;
313  }
314 
315  // cache result for later access
316  if ($nodeOid == null) {
317  $this->_lists[$mapKey] = $result;
318  }
319 
320  return $result;
321  }
322 
323  /**
324  * Resolves the DisplayValue of the given oid.
325  * @param String $oid The oid of the requested object.
326  * @return String The DisplayValue of $oid, or null if $oid is invalid.
327  */
328  private function resolveByOid($oid) {
329  $result = null;
330 
332  {
333  $persistenceFacade = &PersistenceFacade::getInstance();
334  $localization = &Localization::getInstance();
335  try {
336  $obj = &$persistenceFacade->load($oid, BUILDDEPTH_SINGLE);
337  if ($obj != null) {
338  // localize object if requested
339  if ($this->_language != null) {
340  $localization->loadTranslation($obj, $this->_language);
341  }
342  $result = $obj->getDisplayValue();
343  }
344  } catch (Exception $ex) {
345  //do nothing, $result stays null
346  }
347  }
348 
349  return $result;
350  }
351 
352  /**
353  * Translate a value with use of it's assoziated input type e.g get the location string from a location id.
354  * (this is only done when the input type has a list definition).
355  * @param value The value to translate (maybe comma separated list for list controls)
356  * @param inputType The description of the control as given in the input_type property of a value (see CMS getInputControl())
357  * @param replaceBR True/False wether to replace html line breaks with spaces or not [default:false]
358  * @param nodeOid Oid of the node containing this value (for determining remote oids) [default: null]
359  * @return The translated value
360  */
361  public function translateValue($value, $inputType, $replaceBR=false, $nodeOid = null)
362  {
363  // get definition and list from description
364  $translated = '';
365  if (strPos($inputType, '#') && $value != '')
366  {
367  list(,$list) = split('#', $inputType, 2);
368  $map = $this->getListMap($list, $value, $nodeOid);
369  if ($list != '' && strPos($value, ',')) {
370  $value = split(",", $value);
371  }
372  if (is_array($value))
373  {
374  foreach($value as $curValue) {
375  $translated .= $map[$curValue].", ";
376  }
377  $translated = StringUtil::removeTrailingComma($translated);
378  }
379  else {
380  $translated = $map[$value];
381  }
382  return $translated;
383  }
384  $value = nl2br($value);
385  if ($replaceBR) {
386  $value = str_replace('<br />', ' ', $value);
387  }
388  return $value;
389  }
390 }
391 ?>
removeTrailingComma($string)
getListMap($description, $value=null, $nodeOid=null, $addEmptyValue=false)
translateValue($value, $inputType, $replaceBR=false, $nodeOid=null)
FormUtil provides basic support for HTML forms. It's mainly for creating input controls from definiti...
get($message, $parameters=null, $domain='', $lang='')
throwEx($message, $file='', $line='')
getInputControl($name, $inputType, $value, $editable=true, $addEmptyValue=false)
resolveByOid($oid)
decomposeOID($oid, $validate=true)
static getInputFieldDelimiter()
FormUtil($language=null)
$GLOBALS['gJSONData']
const BUILDDEPTH_SINGLE