PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / libraries / adapter / form / form.php

form.php in VikBooking Hotel Booking Engine & PMS trunk, at libraries/adapter/form/form.php

550 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.form
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 JLoader::import('adapter.form.field');
15
16 /**
17 * Form class to handle XML forms.
18 *
19 * This class implements a robust API for constructing, populating, filtering, and validating forms.
20 * It uses XML definitions to construct form fields and a variety of field and rule classes to
21 * render and validate the form.
22 *
23 * @since 10.0
24 */
25 class JForm
26 {
27 /**
28 * A list of JForm instances
29 *
30 * @var array
31 */
32 protected static $forms = array();
33
34 /**
35 * The form XML definition.
36 *
37 * @var SimpleXMLElement
38 */
39 protected $xml;
40
41 /**
42 * The form name.
43 *
44 * @var string
45 * @since 10.1.20
46 */
47 protected $name;
48
49 /**
50 * The form options.
51 *
52 * @var array
53 * @since 10.1.20
54 */
55 protected $options;
56
57 /**
58 * Method to get an instance of a form.
59 *
60 * @param string $name The name of the form.
61 * @param string $data The name of an XML file or string to load as the form definition.
62 * @param array $options An array of form options.
63 *
64 * @return JForm A new JForm instance.
65 *
66 * @throws InvalidArgumentException if no data provided.
67 * @throws RuntimeException if the form could not be loaded.
68 */
69 public static function getInstance($name, $data = null, $options = array())
70 {
71 // only instantiate the form if it does not already exist
72 if (!isset(static::$forms[$name]))
73 {
74 if (is_string($data))
75 {
76 $data = trim($data);
77 }
78
79 if (empty($data))
80 {
81 // no provided data, throw an exception
82 throw new InvalidArgumentException(
83 sprintf('JForm::getInstance(%s, *%s*)',
84 $name,
85 gettype($data)
86 ),
87 400
88 );
89 }
90
91 // instantiate the form.
92 static::$forms[$name] = new static($name, $options);
93
94 // if the string starts with '<' load the XML as string
95 if ($data instanceof SimpleXMLElement || substr($data, 0, 1) == '<')
96 {
97 if (static::$forms[$name]->load($data) == false)
98 {
99 throw new RuntimeException('JForm::getInstance() could not load form.', 500);
100 }
101 }
102 else
103 {
104 if (static::$forms[$name]->loadFile($data) == false)
105 {
106 throw new RuntimeException(sprintf('JForm::getInstance() could not load file [%s].', $data), 500);
107 }
108 }
109 }
110
111 return static::$forms[$name];
112 }
113
114 /**
115 * Class constructor.
116 *
117 * @param string $name The name of the form.
118 * @param array $options An array of form options.
119 *
120 * @since 10.1.20
121 */
122 public function __construct($name, $options = array())
123 {
124 $this->name = $name;
125 $this->options = (array) $options;
126 }
127
128 /**
129 * Returns a list of fieldsets.
130 * If the name is provided, returns only the match.
131 *
132 * @param string $set The fieldset name.
133 *
134 * @return array A list of fieldsets.
135 */
136 public function getFieldset($set = null)
137 {
138 if (is_null($set))
139 {
140 // return all fieldsets
141 return $this->xml->xpath('//fieldset');
142 }
143
144 return $this->xml->xpath('//fieldset[@name="' . $set . '"]');
145 }
146
147 /**
148 * Returns a list of fields that match the query.
149 *
150 * @param string $val The field key value.
151 * @param string $key The field key in which to search (name by default).
152 *
153 * @return array The matching XML elements.
154 */
155 public function getFields($val = null, $key = 'name')
156 {
157 if (is_null($val))
158 {
159 // do not filter fields
160 return $this->xml->xpath('//field');
161 }
162
163 return $this->xml->xpath('//field[@' . $key . '="' . $val . '"]');
164 }
165
166 /**
167 * Returns the specified field.
168 *
169 * @param string $val The field key value.
170 * @param string $key The field key in which to search (name by default).
171 *
172 * @return mixed The field XML element on success, otherwise null.
173 *
174 * @uses getFields()
175 */
176 public function getField($val, $key = 'name')
177 {
178 $fields = $this->getFields($val, $key);
179
180 // return first element if any, otherwise null
181 return array_shift($fields);
182 }
183
184 /**
185 * Returns the loaded XML object.
186 *
187 * @return SimpleXMLElement
188 */
189 public function getXml()
190 {
191 return $this->xml;
192 }
193
194 /**
195 * Method to load the form description from an XML string or object.
196 *
197 * @param string $data The name of an XML string or object.
198 *
199 * @return boolean True on success, otherwise false.
200 */
201 public function load($data)
202 {
203 // if the data to load isn't already an XML element or string return false
204 if (!($data instanceof SimpleXMLElement) && !is_string($data))
205 {
206 return false;
207 }
208
209 // attempt to load the XML if a string
210 if (is_string($data))
211 {
212 $data = new SimpleXMLElement($data);
213
214 // make sure the XML loaded correctly
215 if (!$data)
216 {
217 return false;
218 }
219 }
220
221 // if we have no XML definition at this point let's make sure we get one
222 if (empty($this->xml))
223 {
224 $this->xml = $data;
225 }
226
227 // search for any fieldset that mentions "addfieldpath"
228 $nodes = $this->xml->xpath('//fieldset[@addfieldpath!=""]');
229
230 // iterate the nodes found
231 foreach ($nodes as $node)
232 {
233 $path = (string) $node->attributes()->addfieldpath;
234
235 /**
236 * Check if the [addfieldpath] attribute is defined using a Joomla path.
237 * For example:
238 * - /administrator/components/com_[option]/[path]
239 * - /components/com_[option]/[path]
240 *
241 * @since 10.1.16
242 */
243 if (preg_match("/^\/(administrator)?\/components\/com_([a-z0-9_]+)\/?(.*)/i", $path, $parts))
244 {
245 // starts with option name
246 $path = $parts[2] . '/';
247
248 if ($parts[1] === 'administrator')
249 {
250 // use admin folder
251 $path .= 'admin';
252 }
253 else
254 {
255 // use site folder
256 $path .= 'site';
257 }
258
259 // concat remaining path
260 $path .= '/' . $parts[3];
261 }
262 else if (!empty($this->options['client']))
263 {
264 /**
265 * Try to check if we have a caller within the options array.
266 *
267 * @since 10.1.20
268 */
269 $path = $this->options['client'] . '/' . ltrim($path, '/');
270 }
271
272 $path = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, ltrim($path, '/'));
273
274 // update form fields paths with a new path in which to search for custom handlers
275 JFormField::addIncludePath($path);
276 }
277
278 return true;
279 }
280
281 /**
282 * Method to load the form description from an XML file.
283 *
284 * @param string $file The filesystem path of an XML file.
285 *
286 * @return boolean True on success, otherwise false.
287 *
288 * @uses load()
289 */
290 public function loadFile($file)
291 {
292 // make sure the file exists
293 if (!is_file($file))
294 {
295 return false;
296 }
297
298 // attempt to load the XML file
299 $xml = simplexml_load_file($file);
300
301 return $this->load($xml);
302 }
303
304 /**
305 * Renders the form layout.
306 *
307 * @param object $data The object data to bind:
308 * Property name = Field name;
309 * Property value = Field value.
310 *
311 * @return string The HTML form layout.
312 *
313 * @uses renderFieldset()
314 */
315 public function renderForm($data = null)
316 {
317 return $this->renderFieldset(null, $data);
318 }
319
320 /**
321 * Renders the layout of the given form fieldset.
322 * If fieldset is not given, renders all the fieldsets.
323 *
324 * @param string $set The fielset name.
325 * @param object $data The object data to bind:
326 * Property name = Field name;
327 * Property value = Field value.
328 *
329 * @return string The HTML fieldset(s) layout.
330 *
331 * @uses getFieldset()
332 * @uses renderField()
333 */
334 public function renderFieldset($set = null, $data = null)
335 {
336 // get the fieldsets
337 $fieldsets = $this->getFieldset($set);
338
339 $html = '';
340
341 // iterate the fieldsets
342 foreach ($fieldsets as $fieldset)
343 {
344 $setname = (string) $fieldset->attributes()->name;
345 $setname = 'COM_MENUS_' . strtoupper($setname) . '_FIELDSET_LABEL';
346
347 /**
348 * Do not use a fieldset name in case the title should not be
349 * displayed or in case the translation is missing.
350 *
351 * @since 10.1.29
352 */
353 if ($fieldset->attributes()->hidden || JText::translate($setname) == $setname)
354 {
355 $setname = '';
356 }
357
358 // render fieldset opening
359 $html .= JHtml::fetch('layoutfile', 'html.form.fieldset.open')->render(array('name' => $setname));
360
361 // iterate the fieldset children
362 foreach ($fieldset->field as $field)
363 {
364 $attrs = $field->attributes();
365 $name = (string) $attrs->name;
366
367 $args = array();
368 $args['id'] = (string) $attrs->id;
369 $args['label'] = (string) $attrs->label;
370 $args['description'] = (string) $attrs->description;
371 $args['required'] = ((string) $attrs->required) === 'true';
372
373 /**
374 * Open control only in case the input shouldn't be hidden.
375 *
376 * @since 10.1.21
377 */
378 if ($attrs->type != 'hidden' && $attrs->type != 'spacer' && empty($attrs->hidden))
379 {
380 // open control
381 $html .= JHtml::fetch('layoutfile', 'html.form.control.open')->render($args);
382 }
383
384 // try to check if the value should be bound
385 $val = isset($data->{$name}) ? $data->{$name} : null;
386
387 // render field
388 $html .= $this->renderField($field, array('value' => $val));
389
390 /**
391 * Close control only in case the input shouldn't be hidden.
392 *
393 * @since 10.1.21
394 */
395 if ($attrs->type != 'hidden' && $attrs->type != 'spacer' && empty($attrs->hidden))
396 {
397 // close control
398 $html .= JHtml::fetch('layoutfile', 'html.form.control.close')->render($args);
399 }
400 }
401
402 // render fieldset closing
403 $html .= JHtml::fetch('layoutfile', 'html.form.fieldset.close')->render();
404 }
405
406 return $html;
407 }
408
409 /**
410 * Renders the specified field.
411 *
412 * @param mixed $field The field (or its name) to render.
413 * @param mixed $data The value (or a list of data) to bind.
414 *
415 * @return string The rendered field.
416 *
417 * @uses getField()
418 */
419 public function renderField($field, $data = null)
420 {
421 // get field XML element if the name was provided
422 if (is_string($field))
423 {
424 $field = $this->getField($field);
425 }
426
427 // get form field
428 $field = JFormField::getInstance($field);
429
430 /**
431 * Assign field to this form.
432 *
433 * @since 10.1.31
434 */
435 $field->setForm($this);
436
437 // bind data if set
438 if ($data)
439 {
440 // if scalar value, setup value array
441 if (is_scalar($data))
442 {
443 $data = array('value' => $data);
444 }
445
446 // iterate the data to bind
447 foreach ($data as $k => $v)
448 {
449 // bind attribute only if NOT NULL
450 if (!is_null($v))
451 {
452 $field->bind($v, $k);
453 }
454 }
455 }
456
457 /**
458 * When specified, instruct the field that the layout
459 * should be drawn from the given client (plugin name).
460 *
461 * @since 10.1.31
462 */
463 if (isset($this->options['client']))
464 {
465 $field->modowner = $this->options['client'];
466 }
467
468 // get the field class and do the rendering
469 return $field->render();
470 }
471
472 /**
473 * Method used to bind data to the form.
474 *
475 * @param mixed $data An array or object of data to bind to the form.
476 *
477 * @return boolean True on success.
478 *
479 * @since 10.1.27
480 */
481 public function bind($data)
482 {
483 // Make sure there is a valid JForm XML document.
484 if (!($this->xml instanceof \SimpleXMLElement))
485 {
486 return false;
487 }
488
489 // The data must be an object or array.
490 if (!is_object($data) && !is_array($data))
491 {
492 return false;
493 }
494
495 // iterate field by field
496 foreach ($data as $name => $value)
497 {
498 /**
499 * Update only in case the value is NOT NULL.
500 *
501 * @since 10.1.29
502 */
503 if (!is_null($value))
504 {
505 // find field by name
506 $field = $this->getField($name);
507
508 if ($field)
509 {
510 // field found, update XML element by injecting
511 // the specified value
512 $field['value'] = $value;
513 }
514 }
515 }
516
517 return true;
518 }
519
520 /**
521 * Method to get the form control. This string serves as a container for all form fields. For
522 * example, if there is a field named 'foo' and a field named 'bar' and the form control is
523 * empty the fields will be rendered like: `<input name="foo" />` and `<input name="bar" />`. If
524 * the form control is set to 'jform' however, the fields would be rendered like:
525 * `<input name="jform[foo]" />` and `<input name="jform[bar]" />`.
526 *
527 * @return string The form control string.
528 *
529 * @since 10.1.31
530 */
531 public function getFormControl()
532 {
533 return (string) isset($this->options['control']) ? $this->options['control'] : '';
534 }
535
536 /**
537 * Method to set the form control.
538 *
539 * @param string $control The form control.
540 *
541 * @return void
542 *
543 * @since 10.1.31
544 */
545 public function setFormControl($control)
546 {
547 $this->options['control'] = $control;
548 }
549 }
550