PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 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 All 37 releases
← All changes | libraries/adapter/form/form.php +549 -0 1.6.1 → 1.8.16 View file →
@@ -1,0 +1,549 @@
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 +}