PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.1.13
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.1.13
3.1.14 3.1.13 3.1.12 3.1.11 3.1.10 3.1.9 3.1.8 3.1.7 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 All 123 releases
firebox / Inc / Framework / Inc / Base / Field.php

Field.php in FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment 3.1.13, at Inc/Framework/Inc/Base/Field.php

596 lines 14.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package FirePlugins Framework
4 * @version 1.2.5
5 *
6 * @author FirePlugins <[email protected]>
7 * @link https://www.fireplugins.com
8 * @copyright Copyright © 2026 FirePlugins All Rights Reserved
9 * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
10 */
11
12 namespace FPFramework\Base;
13
14 if (!defined('ABSPATH'))
15 {
16 exit; // Exit if accessed directly.
17 }
18
19 use FPFramework\Libs\Registry;
20 use FPFramework\Helpers\StringHelper;
21 use FPFramework\Helpers\ArrayHelper;
22 use FPFramework\Helpers\FieldsHelper;
23
24 abstract class Field
25 {
26 /**
27 * WordPress page for creating new posts
28 * @var string
29 */
30 const PAGE_NEW_POST = 'post-new.php';
31
32 /**
33 * WordPress page for editing posts
34 * @var string
35 */
36 const PAGE_EDIT_POST = 'post.php';
37
38 /**
39 * All field options.
40 *
41 * @var array
42 */
43 protected $options = [];
44
45 /**
46 * Options specific for this field
47 *
48 * @var array
49 */
50 protected $field_options = [];
51
52 /**
53 * The filter of the field
54 *
55 * @var Filter
56 */
57 protected $filter;
58
59 public function __construct($options = [])
60 {
61 $this->options = $options;
62
63 if (method_exists($this, 'setFieldOptions'))
64 {
65 $this->setFieldOptions($this->options);
66 }
67
68 // merge default and given field options
69 $this->combineOptions();
70
71 // prepare field early
72 $this->prePrepareData();
73
74 // set value
75 $this->options['value'] = $this->getValue();
76
77 // prepare field after value has been set
78 $this->postPrepareData();
79 }
80
81 /**
82 * Prepares the field data after field default settings and any field specifc settings have been combined
83 *
84 * @return void
85 */
86 protected function prePrepareData()
87 {
88 $options = new Registry($this->options);
89
90 $this->options['value_raw'] = $this->options['value'];
91
92 $name = $options->get('name', '');
93
94 // prepare name
95 if (!empty($name) && !$options->get('name_clean', false))
96 {
97 $name = $this->prepareName($options);
98 }
99
100 // add [value] to the name of the field if it has units
101 // (cast: count() throws a TypeError on non-countables since PHP 8.0)
102 if (count((array) $options->get('units', [])) && !in_array($options->get('type'), ['Units', 'ResponsiveControl', 'Dimensions']))
103 {
104 $name .= '[value]';
105 }
106 $this->options['name'] = $name;
107
108 // set input class
109 if (is_array($options->get('input_class')) && count($options->get('input_class')))
110 {
111 $this->options['input_class'] = ' ' . implode(' ', $options->get('input_class', []));
112 }
113
114 // set required attribute
115 if ($options->get('required'))
116 {
117 $this->options['required_attribute'] = ' required="required"';
118 }
119 }
120
121 /**
122 * Prepares the field data after value has been set
123 *
124 * @return void
125 */
126 protected function postPrepareData()
127 {
128 // add the "disable" class to the input if the selected unit was "auto" or if no unit value is set and the default unit is set to "auto"
129 if (isset($this->options['unit']) && $this->options['unit'] == 'auto' ||
130 (!isset($this->options['unit']) && isset($this->options['units_default']) && $this->options['units_default'] == 'auto'))
131 {
132 $this->options['extra_atts']['readonly'] = 'readonly';
133 }
134
135 // generate extra attributes
136 $this->options['extra_atts'] = \FPFramework\Helpers\FieldsHelper::getHTMLAttributes($this->options['extra_atts']);
137 }
138
139 /**
140 * General filter value
141 *
142 * @param string $filter
143 *
144 * @return void
145 */
146 public function filterValue($filter = \FPFramework\Base\Filter::defaultFilter)
147 {
148 $filter = empty($filter) ? $this->options['filter'] : $filter;
149
150 // set filter
151 $this->filter = Filter::getInstance();
152
153 $this->options['value'] = $this->filter->clean($this->options['value'], $filter);
154 }
155
156 /**
157 * Renders a field
158 *
159 * @return void
160 */
161 public function render()
162 {
163 /**
164 * Runs before the field renders
165 */
166 if (method_exists($this, 'onBeforeRender'))
167 {
168 $this->onBeforeRender();
169 }
170
171 $options = $this->getOptions();
172
173 // check whether we manually set to skip loading of the field
174 if ($this->options['skip_render'])
175 {
176 return;
177 }
178
179 // render top section only if needed by field
180 if ($options['render_top'])
181 {
182 // get field top
183 $options['field_top'] = $this->getFieldTop();
184 }
185
186 // get field body
187 $options['field_body'] = $this->getFieldBody();
188
189 // check if we need to render the whole group or only the field
190 $render_file = !$options['render_group'] ? 'field_control' : 'tmpl';
191
192 // render field
193 fpframework()->renderer->field->render($render_file, $options);
194
195 /**
196 * Runs after the field renders
197 */
198 if (method_exists($this, 'onAfterRender'))
199 {
200 $this->onAfterRender();
201 }
202 }
203
204 /**
205 * Returns the field data (plugin and type).
206 *
207 * @return string
208 */
209 private function getFieldData()
210 {
211 $options = $this->getOptions();
212
213 $framework = 'fpframework';
214
215 if (!isset($options['field_path']) || empty($options['field_path']))
216 {
217 return $framework;
218 }
219
220 // check if we have a layout override and use it instead of the given field path
221 $field_path = isset($this->layout_override) ? $this->layout_override : $options['field_path'];
222
223 $plugin = '';
224
225 // retrieve the name of the plugin from the given field source
226 $path_data = explode('\\', $field_path);
227 $path_data = array_filter($path_data);
228 $path_data = array_values($path_data);
229
230 // get plugin (fpframework or plugin name);
231 $plugin = ltrim($path_data[0], '\\');
232 $plugin = strtolower($plugin);
233
234 // ensure plugin(framework or plugin) function exists
235 if (!function_exists($plugin))
236 {
237 return $framework;
238 }
239
240 // if a layout override exists, show the field from the override, otherwise show the given field
241 $type = isset($this->layout_override) ? $path_data[count($path_data) - 1] : $options['type'];
242
243 return [
244 'plugin' => $plugin,
245 'type' => $type
246 ];
247 }
248
249 /**
250 * Extracts the value from a unit-enabled field value
251 *
252 * @param mixed $value The value to process
253 *
254 * @return mixed The processed value
255 */
256 protected function extractUnitValue($value)
257 {
258 if ($this->hasUnits() && !$this instanceof \FPFramework\Base\Fields\Units && isset($value->value))
259 {
260 return $value->value;
261 }
262 return $value;
263 }
264
265 /**
266 * Gets the field value. The value resolution sequence is:
267 * 1. Directly set value in options
268 * 2. Value based on current page context (new post, edit post)
269 * 3. Value from WordPress options
270 *
271 * For fields with units, the value is extracted from the unit structure.
272 *
273 * @return mixed The field value
274 */
275 public function getValue()
276 {
277 global $pagenow;
278
279 $options = $this->getOptions();
280 $value = null;
281
282 // First try to get value directly from options
283 if (!empty($options['value']) || $options['value'] === '0')
284 {
285 return $this->extractUnitValue($options['value']);
286 }
287
288 // Then try to get value based on current page context
289 switch ($pagenow)
290 {
291 case self::PAGE_NEW_POST:
292 $value = $this->extractUnitValue($options['default']);
293 break;
294
295 case self::PAGE_EDIT_POST:
296 $value = $this->extractUnitValue($this->getValueFromPostMeta());
297 break;
298
299 default:
300 $value = $this->extractUnitValue($this->getValueFromOptions());
301 }
302
303 return $value;
304 }
305
306 /**
307 * Get value from post meta
308 *
309 * @param array $meta
310 *
311 * @return string
312 */
313 private function getValueFromPostMeta($meta = [])
314 {
315 $options = $this->getOptions();
316
317 $default = isset($options['default']) ? $options['default'] : null;
318
319 $name = $options['name'];
320
321 if (empty($name))
322 {
323 return $default;
324 }
325
326 if (!$meta)
327 {
328 global $post;
329 $meta = $post ? get_post_meta($post->ID, 'fpframework_meta_settings', true) : [];
330 }
331
332 $val = FieldsHelper::findFieldValueInArray($name, $default, $meta);
333
334 return $val;
335 }
336
337 /**
338 * Searching for value in the get_option() function
339 *
340 * @return string
341 */
342 private function getValueFromOptions()
343 {
344 $options = $this->getOptions();
345
346 // get the setting name
347 $name_initial = $options['name'];
348 $name = explode('[', $name_initial);
349
350 // setting data
351 $data = get_option($name[0]);
352
353 $default = isset($options['default']) ? $options['default'] : null;
354
355 // no results, return default
356 if (!is_array($data))
357 {
358 return $default;
359 }
360
361 return FieldsHelper::findFieldValueInArray($name_initial, $default, $data);
362 }
363
364 /**
365 * Replaces dots with [].
366 *
367 * @param string $name
368 *
369 * @return mixed
370 */
371 private function prepareName($options)
372 {
373 $name = $options->get('name', null);
374 if (!$name)
375 {
376 return;
377 }
378
379 $prefix = $suffix = '';
380
381 $name_prefix = $options->get('name_prefix', null);
382
383 // If we were given a name prefix and the name does not start with it, add prefix
384 if ($name_prefix && !StringHelper::startsWith($name, $name_prefix))
385 {
386 $prefix = $name_prefix . '[';
387 }
388 // if no name prefix was given and if the first character of the name
389 // does not start with open bracket then add prefix
390 else if (!$name_prefix && $name[0] != '[')
391 {
392 $prefix = '[';
393 }
394
395 $name = str_replace('.', '][', $name);
396 $suffix = $name[strlen($name) - 1] == ']' ? '' : ']';
397
398 // Final Name
399 return $prefix . $name . $suffix;
400 }
401
402 /**
403 * Sets field's value
404 *
405 * @param string $value
406 *
407 * @return void
408 */
409 public function setValue($value)
410 {
411 $this->options['value'] = $value;
412 }
413
414 /**
415 * Set options value
416 *
417 * @param string $key
418 * @param string $value
419 *
420 * @return void
421 */
422 public function setOptionsValue($key, $value)
423 {
424 $this->options[$key] = $value;
425 }
426
427 /**
428 * Checks whether the field contains units.
429 *
430 * @return bool
431 */
432 protected function hasUnits()
433 {
434 return is_array($this->options['units']) && count($this->options['units']);
435 }
436
437 /**
438 * Renders field top
439 * Contains the Responsive Controls and the Units
440 *
441 * @return string
442 */
443 public function getFieldTop()
444 {
445 $options = $this->getOptions();
446
447 // units
448 if (count($options['units']))
449 {
450 $default_units_value = isset($options['units_default']) ? $options['units_default'] : null;
451
452 // First check if unit is directly set in options
453 $unit_value = isset($options['unit']) && !empty($options['unit']) ? $options['unit'] : null;
454
455 // Check if unit exists in value_raw
456 if (!$unit_value && isset($options['value_raw']) && is_object($options['value_raw']) && isset($options['value_raw']->unit))
457 {
458 $unit_value = $options['value_raw']->unit;
459 }
460
461 // If no unit found, check in default.unit if it exists
462 if (!$unit_value && isset($options['default']) && is_object($options['default']) && isset($options['default']->unit))
463 {
464 $unit_value = $options['default']->unit;
465 }
466
467 // If still no unit found, use the default units value
468 $unit_value = $unit_value ?: $default_units_value;
469
470 $units_field_data = [
471 'name' => rtrim($options['name_key'], ']') . '][unit]',
472 'units' => (array) $options['units'],
473 'value' => $unit_value,
474 'units_relative_position' => $options['units_relative_position'],
475 'render_top' => false,
476 'render_group' => false
477 ];
478 $units = new \FPFramework\Base\Fields\Units($units_field_data);
479
480 ob_start();
481 $units->render();
482 $render = ob_get_contents();
483 ob_end_clean();
484 $options['field_top_units'] = $render;
485 }
486
487 return fpframework()->renderer->field->render('field_top', $options, true);
488 }
489
490 /**
491 * Renders field body
492 *
493 * @return string
494 */
495 public function getFieldBody()
496 {
497 // determine who will render the field (plugin or framework)
498 $field_data = $this->getFieldData();
499
500 // render a specific field
501 $plugin = isset($field_data['plugin']) ? $field_data['plugin'] : $field_data;
502 $type = isset($field_data['type']) ? $field_data['type'] : $this->options['type'];
503
504 return $plugin()->renderer->fields->render(strtolower($type), $this->getOptions(), true);
505 }
506
507 /**
508 * Combines all options
509 *
510 * @return array
511 */
512 protected function combineOptions()
513 {
514 $this->options = $this->getDefaultOptions($this->options);
515
516 // merge both the base field options as well as the specific field options
517 $this->options = ArrayHelper::arrayMerge($this->options, $this->field_options);
518 }
519
520 /**
521 * Returns all options
522 *
523 * @return array
524 */
525 protected function getOptions()
526 {
527 return $this->options;
528 }
529
530 /**
531 * Return specific option value
532 *
533 * @param string $option
534 *
535 * @return array
536 */
537 public function getOption($option = null)
538 {
539 if (!$option || !is_string($option) || !isset($this->options[$option]))
540 {
541 return null;
542 }
543
544 return $this->options[$option];
545 }
546
547 /**
548 * Gets the default options for a Field.
549 *
550 * @param array $options
551 *
552 * @return void
553 */
554 private function getDefaultOptions($options)
555 {
556 $options = new Registry($options);
557
558 $initial_name = $options->get('name', '');
559
560 $name_prefix = $options->get('name_prefix', \FPFramework\Helpers\FieldsHelper::$fields_prefix);
561
562 return [
563 'name' => $initial_name,
564 'name_key' => $options->get('name_key', $initial_name),
565 'id' => $options->get('id', ''),
566 'type' => $options->get('type', ''),
567 'name_clean' => $options->get('name_clean', false),
568 'name_prefix' => $name_prefix,
569 'field_path' => $options->get('field_path', ''),
570 'label' => fpframework()->_($options->get('label', '')),
571 'description' => fpframework()->_($options->get('description', '')),
572 'description_class' => $options->get('description_class', ['top']),
573 'value_raw' => $options->get('value_raw', null),
574 'value' => $options->get('value', null),
575 'placeholder' => fpframework()->_($options->get('placeholder', '')),
576 'default' => $options->get('default', null),
577 'required' => $options->get('required', false),
578 'required_attribute' => null,
579 'class' => $options->get('class', null),
580 'input_class' => $options->get('input_class', ''),
581 'control_inner_class' => $options->get('control_inner_class', []),
582 'input_parent_class' => $options->get('input_parent_class', []),
583 'showon' => \FPFramework\Helpers\FormHelper::parseShowOnConditions($options->get('showon', null), $name_prefix),
584 'filter' => $options->get('filter', 'sanitize_text_field'),
585 'extra_atts' => $options->get('extra_atts', []),
586 'render_group' => $options->get('render_group', true),
587 'tooltip' => fpframework()->_($options->get('tooltip', '')),
588 'unit' => $options->get('unit', null),
589 'units' => $options->get('units', []),
590 'units_relative_position' => $options->get('units_relative_position', true),
591 'units_default' => $options->get('units_default', null),
592 'render_top' => $options->get('render_top', true),
593 'skip_render' => $options->get('skip_render', false)
594 ];
595 }
596 }