File: /home/lucendi/www/wp-content/themes/mesmerize/customizer/kirki/includes/class-kirki-field.php
<?php
/**
* Creates and validates field parameters.
*
* @package Kirki
* @category Core
* @author Aristeides Stathopoulos
* @copyright Copyright (c) 2016, Aristeides Stathopoulos
* @license http://opensource.org/licenses/https://opensource.org/licenses/MIT
* @since 1.0
*/
if ( ! class_exists('Kirki_Field')) {
#[AllowDynamicProperties]
/**
* Please do not use this class directly.
* You should instead extend it per-field-type.
*/
class Kirki_Field
{
/**
* The ID of the kirki_config we're using.
*
* @see Kirki_Config
* @access protected
* @var string
*/
protected $kirki_config = 'global';
/**
* Thje capability required so that users can edit this field.
*
* @access protected
* @var string
*/
protected $capability = 'edit_theme_options';
/**
* If we're using options instead of theme_mods
* and we want them serialized, this is the option that
* will saved in the db.
*
* @access protected
* @var string
*/
protected $option_name = '';
/**
* Vustom input attributes (defined as an array).
*
* @access protected
* @var array
*/
protected $input_attrs = array();
/**
* Use "theme_mod" or "option".
*
* @access protected
* @var string
*/
protected $option_type = 'theme_mod';
/**
* The name of this setting (id for the db).
*
* @access protected
* @var string|array
*/
protected $settings = '';
/**
* Set to true if you want to disable all CSS output for this field.
*
* @access protected
* @var bool
*/
protected $disable_output = false;
/**
* The field type.
*
* @access protected
* @var string
*/
protected $type = 'kirki-generic';
/**
* Some fields require options to be set.
* We're whitelisting the property here
* and suggest you validate this in a child class.
*
* @access protected
* @var array
*/
protected $choices = array();
/**
* Assign this field to a section.
* Fields not assigned to a section will not be displayed in the customizer.
*
* @access protected
* @var string
*/
protected $section = '';
/**
* The default value for this field.
*
* @access protected
* @var string|array
*/
protected $default = '';
/**
* Priority determines the position of a control inside a section.
* Lower priority numbers move the control to the top.
*
* @access protected
* @var int
*/
protected $priority = 10;
/**
* Unique ID for this field.
* This is auto-calculated from the $settings argument.
*
* @access protected
* @var string
*/
protected $id = '';
/**
* Use if you want to automatically generate CSS from this field's value.
*
* @see https://kirki.org/docs/arguments/output
* @access protected
* @var array
*/
protected $output = array();
/**
* Use to automatically generate postMessage scripts.
* Not necessary to use if you use 'transport' => 'auto'
* and have already set an array for the 'output' argument.
*
* @see https://kirki.org/docs/arguments/js_vars
* @access protected
* @var array
*/
protected $js_vars = array();
/**
* If you want to use a CSS compiler, then use this to set the variable names.
*
* @see https://kirki.org/docs/arguments/variables
* @access protected
* @var array
*/
protected $variables = array();
/**
* Text that will be used in a tooltip to provide extra info for this field.
*
* @access protected
* @var string
*/
protected $tooltip = '';
/**
* Whitelisting for backwards-compatibility.
*
* @access protected
* @var string
*/
protected $help = '';
/**
* Whitelisting for backwards-compatibility.
*
* @access protected
* @var string
*/
protected $mode = '';
/**
* A custom callback to determine if the field should be visible or not.
*
* @access protected
* @var string|array
*/
protected $active_callback = '__return_true';
/**
* A custom sanitize callback that will be used to properly save the values.
*
* @access protected
* @var string|array
*/
protected $sanitize_callback = '';
/**
* Use 'refresh', 'postMessage' or 'auto'.
* 'auto' will automatically geberate any 'js_vars' from the 'output' argument.
*
* @access protected
* @var string
*/
protected $transport = 'refresh';
/**
* Define dependencies to show/hide this field based on the values of other fields.
*
* @access protected
* @var array
*/
protected $required = array();
/**
* Use only on select controls.
* Defines if this is a multi-select or not.
* If value is > 1, then the maximum number of selectable options
* is the number defined here.
*
* @access protected
* @var int
*/
protected $multiple = 1;
/**
* Suggested width for cropped image.
*
* @access protected
* @var int
*/
protected $width = 150;
/**
* Suggested height for cropped image.
*
* @access protected
* @var int
*/
protected $height = 150;
/**
* Whether the width is flexible for cropped image.
*
* @access protected
* @var bool
*/
protected $flex_width = false;
/**
* Whether the height is flexible for cropped image.
*
* @access protected
* @var bool
*/
protected $flex_height = false;
/**
* Contain the settings for the repeater rows labels
*
* @access protected
* @var array
*/
protected $row_label = array();
protected $in_row_with = array();
protected $group = "";
/**
* Partial Refreshes array.
*
* @access protected
* @var array
*/
protected $partial_refresh = array();
/**
* Use only on image, cropped_image, upload controls.
* Limit the Media library to a specific mime type
*
* @access protected
* @var array
*/
protected $mime_type = '';
/**
* The class constructor.
* Parses and sanitizes all field arguments.
* Then it adds the field to Kirki::$fields.
*
* @access public
*
* @param string $config_id The ID of the config we want to use.
* Defaults to "global".
* Configs are handled by the Kirki_Config class.
* @param array $args The arguments of the field.
*/
public function __construct($config_id = 'global', $args = array())
{
if (isset($args['setting']) && ! empty($args['setting']) && ( ! isset($args['settings']) || empty($args['settings']))) {
$args['settings'] = $args['setting'];
unset($args['setting']);
error_log('Kirki: Typo found in field ' . $args['settings'] . ' ("setting" instead of "settings").');
}
if (is_string($config_id)) {
$args['kirki_config'] = $config_id;
}
// In case the user only provides 1 argument,
// assume that the provided argument is $args and set $config_id = 'global'.
if (is_array($config_id) && empty($args)) {
$args = $config_id;
$this->kirki_config = 'global';
}
$this->kirki_config = trim(esc_attr($config_id));
if ('' === $config_id) {
$this->kirki_config = 'global';
}
// Get defaults from the class.
$defaults = get_class_vars(__CLASS__);
// Get the config arguments, and merge them with the defaults.
$config_defaults = (isset(Kirki::$config['global'])) ? Kirki::$config['global'] : array();
if ('global' !== $this->kirki_config && isset(Kirki::$config[$this->kirki_config])) {
$config_defaults = Kirki::$config[$this->kirki_config];
}
$config_defaults = (is_array($config_defaults)) ? $config_defaults : array();
foreach ($config_defaults as $key => $value) {
if (isset($defaults[$key])) {
if ( ! empty($value) && $value != $defaults[$key]) {
$defaults[$key] = $value;
}
}
}
// Merge our args with the defaults.
$args = wp_parse_args($args, $defaults);
// Set the class properties using the parsed args.
foreach ($args as $key => $value) {
$this->$key = $value;
}
// An array of whitelisted properties that don't need to be sanitized here.
// Format: $key => $default_value.
$whitelisted = apply_filters('kirki/' . $this->kirki_config . '/fields/properties_whitelist', array(
'group' => '', // This is sanitized later in the controls themselves.
'in_row_with' => array(), // This is sanitized later in the controls themselves.
'label' => '', // This is sanitized later in the controls themselves.
'description' => '', // This is sanitized later in the controls themselves.
'mode' => '', // Only used for backwards-compatibility reasons.
'fields' => array(), // Used in repeater fields.
'row_label' => array(), // Used in repeater fields.
));
if (isset($args['active_callback'])) {
$whitelisted["active_callback_vars"] = $args['active_callback'];
}
if (isset($args['update'])) {
$whitelisted["update"] = $args['update'];
}
$this->set_field($whitelisted);
}
/**
* Processes the field arguments
*
* @param array $whitelisted_properties Defines an array of arguments that will skip validation at this point.
*/
protected function set_field($whitelisted_properties = array())
{
$properties = get_class_vars(__CLASS__);
// Remove any whitelisted properties from above.
// These will get a free pass, completely unfiltered.
foreach ($whitelisted_properties as $key => $default_value) {
if (isset($properties[$key])) {
unset($properties[$key]);
}
}
// Some things must run before the others.
$priorities = array(
'option_name',
'option_type',
'settings',
);
foreach ($priorities as $priority) {
if (method_exists($this, 'set_' . $priority)) {
$method_name = 'set_' . $priority;
$this->$method_name();
}
}
// Sanitize the properties, skipping the ones run from the $priorities.
foreach ($properties as $property => $value) {
if (in_array($property, $priorities, true)) {
continue;
}
if (method_exists($this, 'set_' . $property)) {
$method_name = 'set_' . $property;
$this->$method_name();
}
}
// Get all arguments with their values.
$args = get_class_vars(__CLASS__);
foreach ($args as $key => $default_value) {
$args[$key] = $this->$key;
}
// Add the whitelisted properties through the back door.
foreach ($whitelisted_properties as $key => $default_value) {
if ( ! isset($this->$key)) {
$this->$key = $default_value;
}
$args[$key] = $this->$key;
}
// Add the field to the static $fields variable properly indexed.
Kirki::$fields[$this->settings] = $args;
if ('background' === $this->type) {
// Build the background fields.
Kirki::$fields = Kirki_Explode_Background_Field::process_fields(Kirki::$fields);
}
}
/**
* This allows us to process this on a field-basis
* by using sub-classes which can override this method.
*
* @access protected
*/
protected function set_default()
{
}
/**
* Escape $kirki_config.
*
* @access protected
*/
protected function set_kirki_config()
{
$this->kirki_config = esc_attr($this->kirki_config);
}
/**
* Escape $option_name.
*
* @access protected
*/
protected function set_option_name()
{
$this->option_name = esc_attr($this->option_name);
}
/**
* Escape the $section.
*
* @access protected
*/
protected function set_section()
{
$this->section = sanitize_key($this->section);
}
/**
* Escape the $section.
*
* @access protected
*/
protected function set_input_attrs()
{
if ( ! is_array($this->input_attrs)) {
$this->input_attrs = array();
}
}
/**
* Checks the capability chosen is valid.
* If not, then falls back to 'edit_theme_options'
*
* @access protected
*/
protected function set_capability()
{
// Early exit if we're using 'edit_theme_options'.
if ('edit_theme_options' === $this->capability) {
return;
}
// Escape & trim the capability.
$this->capability = trim(esc_attr($this->capability));
}
/**
* Make sure we're using the correct option_type
*
* @access protected
*/
protected function set_option_type()
{
// Take care of common typos.
if ('options' === $this->option_type) {
$this->option_type = 'option';
}
// Take care of common typos.
if ('theme_mods' === $this->option_type) {
$this->option_type = 'theme_mod';
}
}
/**
* Modifications for partial refreshes.
*
* @access protected
*/
protected function set_partial_refresh()
{
if ( ! apply_filters('mesmerize_enable_kirki_selective_refresh', true)) {
return;
}
if ( ! is_array($this->partial_refresh)) {
$this->partial_refresh = array();
}
foreach ($this->partial_refresh as $id => $args) {
if ( ! is_array($args) || ! isset($args['selector']) || ! isset($args['render_callback']) || ! is_callable($args['render_callback'])) {
unset($this->partial_refresh[$id]);
continue;
}
}
if ( ! empty($this->partial_refresh)) {
$this->transport = 'postMessage';
}
}
/**
* Sets the settings.
* If we're using serialized options it makes sure that settings are properly formatted.
* We'll also be escaping all setting names here for consistency.
*
* @access protected
*/
protected function set_settings()
{
// If settings is not an array, temporarily convert it to an array.
// This is just to allow us to process everything the same way and avoid code duplication.
// if settings is not an array then it will not be set as an array in the end.
if ( ! is_array($this->settings)) {
$this->settings = array('kirki_placeholder_setting' => $this->settings);
}
$settings = array();
foreach ($this->settings as $setting_key => $setting_value) {
$settings[sanitize_key($setting_key)] = esc_attr($setting_value);
// If we're using serialized options then we need to spice this up.
if ('option' === $this->option_type && '' !== $this->option_name && (false === strpos($setting_key, '['))) {
$settings[sanitize_key($setting_key)] = esc_attr($this->option_name) . '[' . esc_attr($setting_value) . ']';
}
}
$this->settings = $settings;
if (isset($this->settings['kirki_placeholder_setting'])) {
$this->settings = $this->settings['kirki_placeholder_setting'];
}
}
/**
* Escapes the tooltip messages.
*
* @access protected
*/
protected function set_tooltip()
{
if ('' !== $this->tooltip) {
$this->tooltip = wp_strip_all_tags($this->tooltip);
return;
}
}
/**
* Sets the active_callback
* If we're using the $required argument,
* Then this is where the switch is made to our evaluation method.
*
* @access protected
*/
protected function set_active_callback()
{
if (is_array($this->active_callback) && ! is_callable($this->active_callback)) {
if (isset($this->active_callback[0])) {
$this->required = $this->active_callback;
}
}
if ( ! empty($this->required)) {
$this->active_callback = array('Kirki_Active_Callback', 'evaluate');
return;
}
// No need to proceed any further if we're using the default value.
if ('__return_true' === $this->active_callback) {
return;
}
// Make sure the function is callable, otherwise fallback to __return_true.
if ( ! is_callable($this->active_callback)) {
$this->active_callback = '__return_true';
}
}
/**
* Sets the control type.
*
* @access protected
*/
protected function set_type()
{
// Escape the control type (it doesn't hurt to be sure).
$this->type = esc_attr($this->type);
}
/**
* Sets the $id.
* Setting the ID should happen after the 'settings' sanitization.
* This way we can also properly handle cases where the option_type is set to 'option'
* and we're using an array instead of individual options.
*
* @access protected
*/
protected function set_id()
{
$this->id = sanitize_key(str_replace('[', '-', str_replace(']', '', $this->settings)));
}
/**
* Sets the $sanitize_callback
*
* @access protected
*/
protected function set_sanitize_callback()
{
// If a custom sanitize_callback has been defined,
// then we don't need to proceed any further.
if ( ! empty($this->sanitize_callback)) {
return;
}
$default_callbacks = array(
'kirki-multicheck' => array('Kirki_Sanitize_Values', 'multicheck'),
'kirki-sortable' => array('Kirki_Sanitize_Values', 'sortable'),
'kirki-typography' => array('Kirki_Sanitize_Values', 'typography'),
);
if (array_key_exists($this->type, $default_callbacks)) {
$this->sanitize_callback = $default_callbacks[$this->type];
}
}
/**
* Sets the $choices.
*
* @access protected
*/
protected function set_choices()
{
if ( ! is_array($this->choices)) {
$this->choices = array();
}
}
/**
* Escapes the $disable_output.
*
* @access protected
*/
protected function set_disable_output()
{
$this->disable_output = (bool)$this->disable_output;
}
/**
* Sets the $sanitize_callback
*
* @access protected
*/
protected function set_output()
{
if (empty($this->output)) {
return;
}
if ( ! empty($this->output) && ! is_array($this->output)) {
$this->output = array(array('element' => $this->output));
}
// Convert to array of arrays if needed.
if (isset($this->output['element'])) {
$this->output = array($this->output);
}
$outputs = array();
foreach ($this->output as $output) {
if ( ! isset($output['element'])) {
continue;
}
if ( ! isset($output['property']) && ! in_array($this->type, array('kirki-typography', 'background'), true)) {
continue;
}
if ( ! isset($output['sanitize_callback']) && isset($output['callback'])) {
$output['sanitize_callback'] = $output['callback'];
}
// Convert element arrays to strings.
if (is_array($output['element'])) {
$output['element'] = array_unique($output['element']);
sort($output['element']);
$output['element'] = implode(',', $output['element']);
}
$outputs[] = array(
'element' => $output['element'],
'property' => (isset($output['property'])) ? $output['property'] : '',
'media_query' => (isset($output['media_query'])) ? $output['media_query'] : 'global',
'sanitize_callback' => (isset($output['sanitize_callback'])) ? $output['sanitize_callback'] : '',
'units' => (isset($output['units'])) ? $output['units'] : '',
'prefix' => (isset($output['prefix'])) ? $output['prefix'] : '',
'suffix' => (isset($output['suffix'])) ? $output['suffix'] : '',
'exclude' => (isset($output['exclude'])) ? $output['exclude'] : false,
);
}
}
/**
* Sets the $js_vars
*
* @access protected
*/
protected function set_js_vars()
{
if ( ! is_array($this->js_vars)) {
$this->js_vars = array();
}
// Check if transport is set to auto.
// If not, then skip the auto-calculations and exit early.
if ('auto' !== $this->transport) {
return;
}
// Set transport to refresh initially.
// Serves as a fallback in case we failt to auto-calculate js_vars.
$this->transport = 'refresh';
$js_vars = array();
// Try to auto-generate js_vars.
// First we need to check if js_vars are empty, and that output is not empty.
if (empty($this->js_vars) && ! empty($this->output)) {
// Start going through each item in the $output array.
foreach ($this->output as $output) {
$output['function'] = 'css';
// If 'element' or 'property' are not defined, skip this.
if ( ! isset($output['element']) || ! isset($output['property'])) {
continue;
}
if (is_array($output['element'])) {
$output['element'] = implode(',', $output['element']);
}
if (false !== strpos($output['element'], ':')) {
$output['function'] = 'style';
}
// If there's a sanitize_callback defined, skip this.
if (isset($output['sanitize_callback']) && ! empty($output['sanitize_callback'])) {
continue;
}
// If we got this far, it's safe to add this.
$js_vars[] = $output;
}
// Did we manage to get all the items from 'output'?
// If not, then we're missing something so don't add this.
if (count($js_vars) !== count($this->output)) {
return;
}
$this->js_vars = $js_vars;
$this->transport = 'postMessage';
}
}
/**
* Sets the $variables
*
* @access protected
*/
protected function set_variables()
{
$variable = '';
if ( ! is_array($this->variables)) {
$variable = (is_string($this->variables) && ! empty($this->variables)) ? $this->variables : false;
$this->variables = array();
if ($variable && empty($this->variables)) {
$this->variables[0]['name'] = $variable;
}
}
}
/**
* This is a fallback method:
* $help has now become $tooltip, so this just migrates the data
*
* @access protected
*/
protected function set_help()
{
if ('' !== $this->tooltip) {
return;
}
if ('' !== $this->help) {
$this->tooltip = wp_strip_all_tags($this->help);
// $help has been deprecated
$this->help = '';
return;
}
}
/**
* Sets the $transport
*
* @access protected
*/
protected function set_transport()
{
if ('postmessage' === trim(strtolower($this->transport))) {
$this->transport = 'postMessage';
}
}
/**
* Sets the $required
*
* @access protected
*/
protected function set_required()
{
if ( ! is_array($this->required)) {
$this->required = array();
}
}
/**
* Sets the $multiple
*
* @access protected
*/
protected function set_multiple()
{
$this->multiple = absint($this->multiple);
}
/**
* Sets the $priority
*
* @access protected
*/
protected function set_priority()
{
$this->priority = absint($this->priority);
}
}
}