Files
wp-multisite-waas/inc/ui/class-field.php
2024-11-30 18:24:12 -07:00

504 lines
11 KiB
PHP

<?php
/**
* Describes a field and contains helper functions for sanitization and validation.
*
* @package WP_Ultimo
* @subpackage UI
* @since 2.0.0
*/
namespace WP_Ultimo\UI;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Describes a field and contains helper functions for sanitization and validation.
*
* @since 2.0.0
*/
class Field implements \JsonSerializable {
/**
* Holds the attributes of this field.
*
* @since 2.0.0
* @var array
*/
protected $atts = array();
/**
* Holds the value of the settings represented by this field.
*
* @since 2.0.0
* @var mixed
*/
protected $value = null;
/**
* Set and the attributes passed via the constructor.
*
* @since 2.0.0
*
* @param string $id Field id. This is going to be used to retrieve the value from the database later.
* @param array $atts Field attributes.
*/
public function __construct($id, $atts) {
$this->set_attributes($id, $atts);
} // end __construct;
/**
* Set and the attributes passed via the constructor.
*
* @since 2.0.0
*
* @param string $id Field id. This is going to be used to retrieve the value from the database later.
* @param array $atts Field attributes.
* @return void
*/
public function set_attributes($id, $atts) {
$this->atts = wp_parse_args($atts, array(
'id' => $id,
'type' => 'text',
'icon' => 'dashicons-wu-cog',
'action' => false,
'form' => false,
'title' => false,
'img' => false,
'desc' => false,
'content' => false,
'display_value' => false,
'default_value' => false,
'tooltip' => false,
'args' => false,
'sortable' => false,
'placeholder' => false,
'options' => false,
'options_template' => false,
'require' => false,
'button' => false,
'width' => false,
'rules' => false,
'min' => false,
'max' => false,
'allow_html' => false,
'append' => false,
'order' => false,
'dummy' => false,
'disabled' => false,
'capability' => false,
'edit' => false,
'copy' => false,
'validation' => false,
'meter' => false,
'href' => false,
'raw' => false,
'money' => false,
'stacked' => false, // If the field is inside a restricted container
'columns' => 1,
'classes' => '',
'wrapper_classes' => '',
'html_attr' => array(),
'wrapper_html_attr' => array(),
'sub_fields' => array(),
'prefix' => '',
'suffix' => '',
'prefix_html_attr' => array(),
'suffix_html_attr' => array(),
));
} // end set_attributes;
/**
* Set a particular attribute.
*
* @since 2.0.0
*
* @param string $att The attribute name.
* @param mixed $value The new attribute value.
* @return void
*/
public function set_attribute($att, $value) {
$this->atts[$att] = $value;
} // end set_attribute;
/**
* Returns the list of field attributes.
*
* @since 2.0.0
* @return array
*/
public function get_attributes() {
return $this->atts;
} // end get_attributes;
/**
* Makes sure old fields remain compatible.
*
* We are making some field type name changes in 2.0.
* This method lists an array with aliases in the following format:
*
* - old_type_name => new_type_name.
*
* We throw a deprecation notice to make sure developers update their code appropriately.
*
* @since 2.0.0
* @return string
*/
public function get_compat_template_name() {
$aliases = array(
'heading' => 'header',
'heading_collapsible' => 'header',
'select2' => 'select',
'checkbox' => 'toggle',
);
$deprecated = array(
'heading',
'heading_collapsible',
'select2',
);
if (array_key_exists($this->type, $aliases)) {
$new_type_name = $aliases[$this->type];
if (array_key_exists($this->type, $deprecated)) {
// translators: The %1$s placeholder is the old type name, the second, the new type name.
_doing_it_wrong('wu_add_field', sprintf(__('The field type "%1$s" is no longer supported, use "%2$s" instead.'), $this->type, $new_type_name), '2.0.0');
} // end if;
/*
* Back Compat for Select2 Fields
*/
if ($this->type === 'select2') {
$this->atts['html_attr']['data-selectize'] = 1;
$this->atts['html_attr']['multiple'] = 1;
} // end if;
return $new_type_name;
} // end if;
return false;
} // end get_compat_template_name;
/**
* Returns the template name for a field.
*
* We use this to go to the views folder and fetch the HTML template.
* The return here is not an absolute path, as the folder depends on the view the form is using.
*
* @see \WP_Ultimo\UI\Forms
*
* @since 2.0.0
* @return string
*/
public function get_template_name() {
$compat_name = $this->get_compat_template_name();
$view_name = $compat_name ? $compat_name : $this->type;
return str_replace('_', '-', (string) $view_name);
} // end get_template_name;
/**
* Returns attributes as class properties.
*
* @since 2.0.0
*
* @param string $att Attribute to retrieve.
* @return mixed
*/
public function __get($att) {
$allowed_callable = array(
'title',
'desc',
'content',
'display_value',
'default_value',
'tooltip',
'options',
'require',
'validation',
'value',
'html_attr',
'img',
);
$attr = isset($this->atts[$att]) ? $this->atts[$att] : false;
$allow_callable_prefix = is_string($attr) && strncmp($attr, 'wu_get_', strlen('wu_get_')) === 0 && is_callable($attr);
$allow_callable_method = is_array($attr) && is_callable($attr);
if (in_array($att, $allowed_callable, true) && ($allow_callable_prefix || $allow_callable_method || is_a($attr, \Closure::class))) {
$attr = call_user_func($attr, $this);
} // end if;
if ($att === 'wrapper_classes' && isset($this->atts['wrapper_html_attr']['v-show'])) {
$this->atts['wrapper_classes'] = $this->atts['wrapper_classes'] . ' wu-requires-other';
} // end if;
if ($att === 'type' && $this->atts[$att] === 'submit') {
$this->atts['wrapper_classes'] = $this->atts['wrapper_classes'] . ' wu-submit-field';
} // end if;
if ($att === 'type' && $this->atts[$att] === 'tab-select') {
$this->atts['wrapper_classes'] = $this->atts['wrapper_classes'] . ' wu-tab-field';
} // end if;
if ($att === 'wrapper_classes' && is_a($this->form, '\\WP_Ultimo\\UI\\Form')) {
return $this->form->field_wrapper_classes . ' ' . $this->atts['wrapper_classes'];
} // end if;
if ($att === 'classes' && is_a($this->form, '\\WP_Ultimo\\UI\\Form')) {
return $this->form->field_classes . ' ' . $this->atts['classes'];
} // end if;
if ($att === 'title' && $attr === false && isset($this->atts['name'])) {
$attr = $this->atts['name'];
} // end if;
return $attr;
} // end __get;
/**
* Returns the list of sanitization callbacks for each field type
*
* @since 2.0.0
* @return array
*/
protected function sanitization_rules() {
$rules = array(
'text' => 'sanitize_text_field',
'header' => '__return_null',
'number' => array($this, 'validate_number_field'),
'wp_editor' => array($this, 'validate_textarea_field'),
'textarea' => array($this, 'validate_textarea_field'),
'checkbox' => 'wu_string_to_bool',
'multi_checkbox' => false,
'select2' => false,
'multiselect' => false,
);
return apply_filters('wu_settings_fields_sanitization_rules', $rules);
} // end sanitization_rules;
/**
* Returns the value of the setting represented by this field.
*
* @since 2.0.0
* @return mixed
*/
public function get_value() {
return $this->value;
} // end get_value;
/**
* Sets the value of the settings represented by this field.
*
* This alone won't save the setting to the database. This method also invokes the
* sanitization callback, so we can be sure the data is ready for database insertion.
*
* @since 2.0.0
*
* @param mixed $value Value of the settings being represented by this field.
* @return WP_Ultimo\UI\Field
*/
public function set_value($value) {
$this->value = $value;
if (!$this->raw) {
$this->sanitize();
} // end if;
return $this;
} // end set_value;
/**
* Runs the value of the field through the sanitization callback.
*
* @since 2.0.0
* @return void
*/
public function sanitize() {
$rules = $this->sanitization_rules();
$sanitize_method = isset($rules[$this->type]) ? $rules[$this->type] : $rules['text'];
if ($sanitize_method) {
$this->value = call_user_func($sanitize_method, $this->value);
} // end if;
} // end sanitize;
/**
* Sanitization callback for fields of type number.
*
* Checks if the new value set is between the min and max boundaries.
*
* @since 2.0.0
*
* @param int|float $value Value of the settings being represented by this field.
* @return int|float
*/
protected function validate_number_field($value) {
/**
* Check if the value respects the min/max values.
*/
if ($this->min && $value < $this->min) {
return $this->min;
} // end if;
if ($this->max && $value > $this->max) {
return $this->max;
} // end if;
return $value;
} // end validate_number_field;
/**
* Cleans the value submitted via a textarea or wp_editor field for database insertion.
*
* @since 2.0.0
*
* @param string $value Value of the settings being represented by this field.
* @return string
*/
protected function validate_textarea_field($value) {
if ($this->allow_html) {
return stripslashes(wp_filter_post_kses(addslashes($value)));
} // end if;
return wp_strip_all_tags(stripslashes($value));
} // end validate_textarea_field;
/**
* Return HTML attributes for the field.
*
* @since 2.0.0
* @return string
*/
public function get_html_attributes() {
if (is_callable($this->atts['html_attr'])) {
$this->atts['html_attr'] = call_user_func($this->atts['html_attr']);
} // end if;
$attributes = $this->atts['html_attr'];
unset($this->atts['html_attr']['class']);
if ($this->type === 'number') {
if ($this->min !== false) {
$attributes['min'] = $this->min;
} // end if;
if ($this->max !== false) {
$attributes['max'] = $this->max;
} // end if;
} // end if;
/*
* Adds money formatting and masking
*/
if ($this->money !== false) {
$attributes['v-bind'] = 'money_settings';
} // end if;
return wu_array_to_html_attrs($attributes);
} // end get_html_attributes;
/**
* Return HTML attributes for the field.
*
* @since 2.0.0
* @return string
*/
public function get_wrapper_html_attributes() {
$attributes = $this->atts['wrapper_html_attr'];
unset($this->atts['wrapper_html_attr']['class']);
return wu_array_to_html_attrs($attributes);
} // end get_wrapper_html_attributes;
/**
* Implements our on json_decode version of this object. Useful for use in vue.js
*
* @since 2.0.0
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return $this->atts;
} // end jsonSerialize;
} // end class Field;