Update to Kirki 4.0.22

This commit is contained in:
AlxMedia 2022-03-15 14:29:17 +01:00
parent 2b6ac38550
commit 78edeb1b25
492 changed files with 29668 additions and 39884 deletions

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 kirki-framework
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,2 @@
# module-css
CSS module for the Kirki Framework

View file

@ -0,0 +1,423 @@
<?php
/**
* Handles the CSS Output of fields.
*
* @package Kirki
* @category Modules
* @author Ari Stathopoulos (@aristath)
* @copyright Copyright (c) 2019, Ari Stathopoulos (@aristath)
* @license https://opensource.org/licenses/MIT
* @since 3.0.0
*/
namespace Kirki\Module;
use Kirki\Compatibility\Kirki;
use Kirki\Util\Helper;
use Kirki\Compatibility\Values;
use Kirki\Module\CSS\Generator;
/**
* The Module object.
*/
class CSS {
/**
* The CSS array
*
* @access public
* @var array
*/
public static $css_array = array();
/**
* An array of fields to be processed.
*
* @static
* @access protected
* @since 1.0
* @var array
*/
protected static $fields = array();
/**
* Field option types.
*
* @static
* @access protected
* @since 1.0
* @var array
*/
protected static $field_option_types = array();
/**
* The default handle for kirki's styles enqueue.
*
* @since 4.0
* @access private
* @static
*
* @var string
*/
private static $css_handle = 'kirki-styles';
/**
* Constructor
*
* @access public
*/
public function __construct() {
add_action( 'kirki_field_init', array( $this, 'field_init' ), 10, 2 );
add_action( 'init', array( $this, 'init' ) );
}
/**
* Init.
*
* @access public
*/
public function init() {
new \Kirki\Module\Webfonts();
add_action( 'wp', array( $this, 'print_styles_action' ) );
if ( ! apply_filters( 'kirki_output_inline_styles', true ) ) {
$config = apply_filters( 'kirki_config', array() );
$priority = 999;
if ( isset( $config['styles_priority'] ) ) {
$priority = absint( $config['styles_priority'] );
}
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ), $priority );
} else {
add_action( 'wp_head', array( $this, 'print_styles_inline' ), 999 );
}
}
/**
* Runs when a field gets added.
* Adds fields to this object so their styles can later be generated.
*
* @access public
* @since 1.0
* @param array $args The field args.
* @param Object $object The field object.
* @return void
*/
public function field_init( $args, $object ) {
if ( ! isset( $args['output'] ) ) {
$args['output'] = array();
}
self::$field_option_types[ $args['settings'] ] = isset( $args['option_type'] ) ? $args['option_type'] : 'theme_mod';
if ( ! is_array( $args['output'] ) ) {
/* translators: The field ID where the error occurs. */
_doing_it_wrong( __METHOD__, sprintf( esc_html__( '"output" invalid format in field %s. The "output" argument should be defined as an array of arrays.', 'kirki' ), esc_html( $args['settings'] ) ), '3.0.10' );
$args['output'] = array(
array(
'element' => $args['output'],
),
);
}
// Convert to array of arrays if needed.
if ( isset( $args['output']['element'] ) ) {
/* translators: The field ID where the error occurs. */
_doing_it_wrong( __METHOD__, sprintf( esc_html__( '"output" invalid format in field %s. The "output" argument should be defined as an array of arrays.', 'kirki' ), esc_html( $args['settings'] ) ), '3.0.10' );
$args['output'] = array( $args['output'] );
}
if ( empty( $args['output'] ) ) {
return;
}
foreach ( $args['output'] as $key => $output ) {
if ( empty( $output ) || ! isset( $output['element'] ) ) {
unset( $args['output'][ $key ] );
continue;
}
if ( ! isset( $output['sanitize_callback'] ) && isset( $output['callback'] ) ) {
$args['output'][ $key ]['sanitize_callback'] = $output['callback'];
}
// Convert element arrays to strings.
if ( isset( $output['element'] ) && is_array( $output['element'] ) ) {
$args['output'][ $key ]['element'] = array_unique( $args['output'][ $key ]['element'] );
sort( $args['output'][ $key ]['element'] );
// Trim each element in the array.
foreach ( $args['output'][ $key ]['element'] as $index => $element ) {
$args['output'][ $key ]['element'][ $index ] = trim( $element );
}
$args['output'][ $key ]['element'] = implode( ',', $args['output'][ $key ]['element'] );
}
// Fix for https://github.com/aristath/kirki/issues/1659#issuecomment-346229751.
$args['output'][ $key ]['element'] = str_replace( array( "\t", "\n", "\r", "\0", "\x0B" ), ' ', $args['output'][ $key ]['element'] );
$args['output'][ $key ]['element'] = trim( preg_replace( '/\s+/', ' ', $args['output'][ $key ]['element'] ) );
}
if ( ! isset( $args['type'] ) && isset( $object->type ) ) {
$args['type'] = $object->type;
}
self::$fields[] = $args;
}
/**
* Print styles inline.
*
* @access public
* @since 3.0.36
* @return void
*/
public function print_styles_inline() {
echo '<style id="kirki-inline-styles">';
$this->print_styles();
echo '</style>';
}
/**
* Enqueue the styles.
*
* @access public
* @since 3.0.36
* @return void
*/
public function enqueue_styles() {
$args = array(
'action' => apply_filters( 'kirki_styles_action_handle', self::$css_handle ),
);
if ( is_admin() ) {
global $current_screen;
/**
* This `enqueue_styles` method is also hooked into `enqueue_block_editor_assets`.
* It needs to be excluded from customize control page.
*
* Why not simply excluding all admin area except gutenberg editing interface?
* Because it would be nice to let the possibility open
* if a 3rd party plugin will output gutenberg syles somewhere in admin area.
*
* Example of possibility:
* In the future, Ultimate Dashboard Pro's admin page feature might supports Gutenberg.
*/
if ( is_object( $current_screen ) && property_exists( $current_screen, 'id' ) && 'customize' === $current_screen->id ) {
return;
}
if ( property_exists( $current_screen, 'is_block_editor' ) && 1 === (int) $current_screen->is_block_editor ) {
$args['editor'] = '1';
}
}
// Enqueue the dynamic stylesheet.
wp_enqueue_style(
self::$css_handle,
add_query_arg( $args, home_url() ),
array(),
'4.0'
);
}
/**
* Prints the styles as an enqueued file.
*
* @access public
* @since 3.0.36
* @return void
*/
public function print_styles_action() {
/**
* Note to code reviewers:
* There is no need for a nonce check here, we're only checking if this is a valid request or not.
*/
// phpcs:ignore WordPress.Security.NonceVerification
if ( empty( $_GET['action'] ) || apply_filters( 'kirki_styles_action_handle', self::$css_handle ) !== $_GET['action'] ) {
return;
}
// This is a stylesheet.
header( 'Content-type: text/css' );
$this->print_styles();
exit;
}
/**
* Prints the styles.
*
* @access public
*/
public function print_styles() {
// Go through all configs.
$configs = Kirki::$config;
foreach ( $configs as $config_id => $args ) {
if ( isset( $args['disable_output'] ) && true === $args['disable_output'] ) {
continue;
}
$styles = self::loop_controls( $config_id );
$styles = apply_filters( "kirki_{$config_id}_dynamic_css", $styles );
if ( ! empty( $styles ) ) {
/**
* Note to code reviewers:
*
* Though all output should be run through an escaping function, this is pure CSS.
*
* When used in the print_styles_action() method the PHP header() call makes the browser interpret it as such.
* No code, script or anything else can be executed from inside a stylesheet.
*
* When using in the print_styles_inline() method the wp_strip_all_tags call we use below
* strips anything that has the possibility to be malicious, and since this is inslide a <style> tag
* it can only be interpreted by the browser as such.
* wp_strip_all_tags() excludes the possibility of someone closing the <style> tag and then opening something else.
*/
echo wp_strip_all_tags( $styles ); // phpcs:ignore WordPress.Security.EscapeOutput
}
}
do_action( 'kirki_dynamic_css' );
}
/**
* Loop through all fields and create an array of style definitions.
*
* @static
* @access public
* @param string $config_id The configuration ID.
*/
public static function loop_controls( $config_id ) {
// Get an instance of the Generator class.
// This will make sure google fonts and backup fonts are loaded.
Generator::get_instance();
$fields = self::get_fields_by_config( $config_id );
// Compatibility with v3 API.
if ( class_exists( '\Kirki\Compatibility\Kirki' ) ) {
$fields = array_merge( \Kirki\Compatibility\Kirki::$fields, $fields );
}
$css = array();
// Early exit if no fields are found.
if ( empty( $fields ) ) {
return;
}
foreach ( $fields as $field ) {
// Only process fields that belong to $config_id.
if ( isset( $field['kirki_config'] ) && $config_id !== $field['kirki_config'] ) {
continue;
}
if ( true === apply_filters( "kirki_{$config_id}_css_skip_hidden", true ) ) {
// Only continue if field dependencies are met.
if ( ( isset( $field['required'] ) && ! empty( $field['required'] ) ) || ( isset( $field['active_callback'] ) && ! empty( $field['active_callback'] ) ) ) {
$valid = true;
// If $field is using active_callback instead of required.
if ( ! isset( $field['required'] ) || empty( $field['required'] ) ) {
if ( isset( $field['active_callback'] ) && ! empty( $field['active_callback'] ) ) {
// The "active_callback" or "required" accepts array or callable as the value.
if ( is_array( $field['active_callback'] ) || is_callable( $field['active_callback'] ) ) {
$field['required'] = $field['active_callback'];
}
}
}
// At this point, we know that the "required" is set and is not empty.
if ( is_array( $field['required'] ) ) {
foreach ( $field['required'] as $requirement ) {
if ( isset( $requirement['setting'] ) && isset( $requirement['value'] ) && isset( $requirement['operator'] ) && isset( self::$field_option_types[ $requirement['setting'] ] ) ) {
$controller_value = Values::get_value( $config_id, $requirement['setting'] );
if ( ! Helper::compare_values( $controller_value, $requirement['value'], $requirement['operator'] ) ) {
$valid = false;
}
}
}
} elseif ( is_string( $field['required'] ) ) {
$valid = '__return_true' === $field['required'] ? true : false;
} elseif ( is_callable( $field['required'] ) ) {
$valid = call_user_func( $field['required'] );
}
if ( ! $valid ) {
continue;
}
}
}
// Only continue if $field['output'] is set.
if ( isset( $field['output'] ) && ! empty( $field['output'] ) ) {
$css = Helper::array_replace_recursive( $css, Generator::css( $field ) );
// Add the globals.
if ( isset( self::$css_array[ $config_id ] ) && ! empty( self::$css_array[ $config_id ] ) ) {
Helper::array_replace_recursive( $css, self::$css_array[ $config_id ] );
}
}
}
$css = apply_filters( "kirki_{$config_id}_styles", $css );
if ( is_array( $css ) ) {
return Generator::styles_parse( Generator::add_prefixes( $css ) );
}
}
/**
* Gets fields from self::$fields by config-id.
*
* @static
* @access private
* @since 1.0
* @param string $config_id The config-ID.
* @return array
*/
private static function get_fields_by_config( $config_id ) {
$fields = array();
foreach ( self::$fields as $field ) {
if (
( isset( $field['kirki_config'] ) && $config_id === $field['kirki_config'] ) ||
(
( 'global' === $config_id || ! $config_id ) &&
( ! isset( $field['kirki_config'] ) || 'global' === $field['kirki_config'] || ! $field['kirki_config'] )
)
) {
$fields[] = $field;
}
}
return $fields;
}
}

View file

@ -0,0 +1,286 @@
<?php
/**
* Generates the styles for the frontend.
* Handles the 'output' argument of fields
*
* @package Kirki
* @category Core
* @author Ari Stathopoulos (@aristath)
* @copyright Copyright (c) 2019, Ari Stathopoulos (@aristath)
* @license https://opensource.org/licenses/MIT
* @since 1.0
*/
namespace Kirki\Module\CSS;
use Kirki\Module\Webfonts\Fonts;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handles CSS output.
*/
final class Generator {
/**
* The instance of this class (singleton pattern).
*
* @static
* @access public
* @var null|object
*/
public static $instance = null;
/**
* Settings.
*
* @static
* @access public
* @var null|string|array
*/
public static $settings = null;
/**
* Output.
*
* @static
* @access public
* @var array
*/
public static $output = [];
/**
* Callback.
*
* @static
* @access public
* @var null|string|array
*/
public static $callback = null;
/**
* Option Name.
*
* @static
* @access public
* @var null|string
*/
public static $option_name = null;
/**
* Field Type.
*
* @static
* @access public
* @var string
*/
public static $field_type = null;
/**
* Google Fonts
*
* @static
* @access public
* @var array
*/
public static $google_fonts = null;
/**
* CSS
*
* @static
* @access public
* @var string
*/
public static $css;
/**
* Value
*
* @static
* @access public
* @var mixed
*/
public static $value = null;
/**
* The class constructor.
*/
private function __construct() {
if ( is_null( self::$google_fonts ) ) {
self::$google_fonts = Fonts::get_google_fonts();
}
}
/**
* Get a single instance of this class
*
* @return object
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Get the CSS for a field.
*
* @static
* @access public
* @param array $field The field.
* @return array
*/
public static function css( $field ) {
// Set class vars.
self::$settings = $field['settings'];
self::$callback = isset( $field['sanitize_callback'] ) ? $field['sanitize_callback'] : '';
self::$field_type = $field['type'];
self::$field_type = ( isset( $field['choices'] ) && isset( $field['choices']['parent_type'] ) ) ? $field['choices']['parent_type'] : self::$field_type;
self::$output = $field['output'];
$field['kirki_config'] = isset( $field['kirki_config'] ) ? $field['kirki_config'] : 'global';
if ( ! is_array( self::$output ) ) {
self::$output = [
[
'element' => self::$output,
'sanitize_callback' => null,
],
];
}
// Get the value of this field.
$option_type = ( isset( $field['option_type'] ) ) ? $field['option_type'] : 'theme_mod';
$default = ( isset( $field['default'] ) ) ? $field['default'] : '';
$setting_name = $field['settings'];
if ( 'option' === $option_type ) {
if ( ! empty( $field['option_name'] ) && 0 !== stripos( $setting_name, $field['option_name'] . '[' ) ) {
$setting_name = $field['option_name'] . '[' . $field['settings'] . ']';
}
}
self::$value = apply_filters( 'kirki_get_value', get_theme_mod( $field['settings'], $default ), $setting_name, $default, $option_type );
// Find the class that will handle the outpout for this field.
$classname = '\Kirki\Module\CSS\Output';
$field_output_classes = apply_filters( 'kirki_output_control_classnames', [] );
$field_output_classes = apply_filters( "kirki_{$field['kirki_config']}_output_control_classnames", $field_output_classes );
if ( array_key_exists( self::$field_type, $field_output_classes ) ) {
$classname = $field_output_classes[ self::$field_type ];
}
$obj = new $classname( $field['kirki_config'], self::$output, self::$value, $field );
return $obj->get_styles();
}
/**
* Gets the array of generated styles and creates the minimized, inline CSS.
*
* @static
* @access public
* @param array $css The CSS definitions array.
* @return string The generated CSS.
*/
public static function styles_parse( $css = [] ) {
// Pass our styles from the kirki_styles_array filter.
$css = apply_filters( 'kirki_styles_array', $css );
// Process the array of CSS properties and produce the final CSS.
$final_css = '';
if ( ! is_array( $css ) || empty( $css ) ) {
return '';
}
foreach ( $css as $media_query => $styles ) {
$final_css .= ( 'global' !== $media_query ) ? $media_query . '{' : '';
foreach ( $styles as $style => $style_array ) {
$css_for_style = '';
foreach ( $style_array as $property => $value ) {
if ( is_string( $value ) && '' !== $value ) {
$css_for_style .= $property . ':' . $value . ';';
} elseif ( is_array( $value ) ) {
foreach ( $value as $subvalue ) {
if ( is_string( $subvalue ) && '' !== $subvalue ) {
$css_for_style .= $property . ':' . $subvalue . ';';
}
}
}
$value = ( is_string( $value ) ) ? $value : '';
}
if ( '' !== $css_for_style ) {
$final_css .= $style . '{' . $css_for_style . '}';
}
}
$final_css .= ( 'global' !== $media_query ) ? '}' : '';
}
return $final_css;
}
/**
* Add prefixes if necessary.
*
* @param array $css The CSS definitions array.
* @return array
*/
public static function add_prefixes( $css ) {
if ( is_array( $css ) ) {
foreach ( $css as $media_query => $elements ) {
foreach ( $elements as $element => $style_array ) {
foreach ( $style_array as $property => $value ) {
// Add -webkit-* and -moz-*.
if ( is_string( $property ) && in_array(
$property,
[
'border-radius',
'box-shadow',
'box-sizing',
'text-shadow',
'transform',
'background-size',
'transition',
'transition-property',
],
true
) ) {
unset( $css[ $media_query ][ $element ][ $property ] );
$css[ $media_query ][ $element ][ '-webkit-' . $property ] = $value;
$css[ $media_query ][ $element ][ '-moz-' . $property ] = $value;
$css[ $media_query ][ $element ][ $property ] = $value;
}
// Add -ms-* and -o-*.
if ( is_string( $property ) && in_array(
$property,
[
'transform',
'background-size',
'transition',
'transition-property',
],
true
) ) {
unset( $css[ $media_query ][ $element ][ $property ] );
$css[ $media_query ][ $element ][ '-ms-' . $property ] = $value;
$css[ $media_query ][ $element ][ '-o-' . $property ] = $value;
$css[ $media_query ][ $element ][ $property ] = $value;
}
}
}
}
}
return $css;
}
}

View file

@ -0,0 +1,377 @@
<?php
/**
* Handles CSS output for fields.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2019, Ari Stathopoulos (@aristath)
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
namespace Kirki\Module\CSS;
use Kirki\Compatibility\Values;
use Kirki\Compatibility\Kirki;
/**
* Handles field CSS output.
*/
class Output {
/**
* The field's `output` argument.
*
* @access protected
* @var array
*/
protected $output = [];
/**
* An array of the generated styles.
*
* @access protected
* @var array
*/
protected $styles = [];
/**
* The field.
*
* @access protected
* @var array
*/
protected $field = [];
/**
* The value.
*
* @access protected
* @var string|array
*/
protected $value;
/**
* The class constructor.
*
* @access public
* @param string $config_id The config ID.
* @param array $output The output argument.
* @param string|array $value The value.
* @param array $field The field.
*/
public function __construct( $config_id, $output, $value, $field ) {
$this->value = $value;
$this->output = $output;
$this->field = $field;
$this->parse_output();
}
/**
* If we have a sanitize_callback defined, apply it to the value.
*
* @param array $output The output args.
* @param string|array $value The value.
*
* @return string|array
*/
protected function apply_sanitize_callback( $output, $value ) {
if ( isset( $output['sanitize_callback'] ) && null !== $output['sanitize_callback'] ) {
// If the sanitize_callback is invalid, return the value.
if ( ! is_callable( $output['sanitize_callback'] ) ) {
return $value;
}
return call_user_func( $output['sanitize_callback'], $this->value );
}
return $value;
}
/**
* If we have a value_pattern defined, apply it to the value.
*
* @param array $output The output args.
* @param string|array $value The value.
* @return string|array
*/
protected function apply_value_pattern( $output, $value ) {
if ( isset( $output['value_pattern'] ) && is_string( $output['value_pattern'] ) ) {
if ( ! is_array( $value ) ) {
$value = str_replace( '$', $value, $output['value_pattern'] );
}
if ( is_array( $value ) ) {
foreach ( array_keys( $value ) as $value_k ) {
if ( is_array( $value[ $value_k ] ) ) {
continue;
}
if ( isset( $output['choice'] ) ) {
if ( $output['choice'] === $value_k ) {
$value[ $output['choice'] ] = str_replace( '$', $value[ $output['choice'] ], $output['value_pattern'] );
}
continue;
}
$value[ $value_k ] = str_replace( '$', $value[ $value_k ], $output['value_pattern'] );
}
}
$value = $this->apply_pattern_replace( $output, $value );
}
return $value;
}
/**
* If we have a value_pattern defined, apply it to the value.
*
* @param array $output The output args.
* @param string|array $value The value.
* @return string|array
*/
protected function apply_pattern_replace( $output, $value ) {
if ( isset( $output['pattern_replace'] ) && is_array( $output['pattern_replace'] ) ) {
$option_type = ( isset( $this->field['option_type'] ) ) ? $this->field['option_type'] : 'theme_mod';
$option_name = ( isset( $this->field['option_name'] ) ) ? $this->field['option_name'] : '';
$options = [];
if ( $option_name ) {
$options = ( 'site_option' === $option_type ) ? get_site_option( $option_name ) : get_option( $option_name );
}
foreach ( $output['pattern_replace'] as $search => $replace ) {
$replacement = '';
switch ( $option_type ) {
case 'option':
if ( is_array( $options ) ) {
if ( $option_name ) {
$subkey = str_replace( [ $option_name, '[', ']' ], '', $replace );
$replacement = ( isset( $options[ $subkey ] ) ) ? $options[ $subkey ] : '';
break;
}
$replacement = ( isset( $options[ $replace ] ) ) ? $options[ $replace ] : '';
break;
}
$replacement = get_option( $replace );
break;
case 'site_option':
$replacement = ( is_array( $options ) && isset( $options[ $replace ] ) ) ? $options[ $replace ] : get_site_option( $replace );
break;
case 'user_meta':
$user_id = get_current_user_id();
if ( $user_id ) {
$replacement = get_user_meta( $user_id, $replace, true );
}
break;
default:
$replacement = get_theme_mod( $replace );
if ( ! $replacement ) {
$replacement = Values::get_value( $this->field['kirki_config'], $replace );
}
}
$replacement = ( false === $replacement ) ? '' : $replacement;
if ( is_array( $value ) ) {
foreach ( $value as $k => $v ) {
$_val = ( isset( $value[ $v ] ) ) ? $value[ $v ] : $v;
$value[ $k ] = str_replace( $search, $replacement, $_val );
}
return $value;
}
$value = str_replace( $search, $replacement, $value );
}
}
return $value;
}
/**
* Parses the output arguments.
* Calls the process_output method for each of them.
*
* @access protected
*/
protected function parse_output() {
foreach ( $this->output as $output ) {
$skip = false;
// Apply any sanitization callbacks defined.
$value = $this->apply_sanitize_callback( $output, $this->value );
// Skip if value is empty.
if ( '' === $this->value ) {
$skip = true;
}
// No need to proceed this if the current value is the same as in the "exclude" value.
if ( isset( $output['exclude'] ) && is_array( $output['exclude'] ) ) {
foreach ( $output['exclude'] as $exclude ) {
if ( is_array( $value ) ) {
if ( is_array( $exclude ) ) {
$diff1 = array_diff( $value, $exclude );
$diff2 = array_diff( $exclude, $value );
if ( empty( $diff1 ) && empty( $diff2 ) ) {
$skip = true;
}
}
// If 'choice' is defined check for sub-values too.
// Fixes https://github.com/aristath/kirki/issues/1416.
if ( isset( $output['choice'] ) && isset( $value[ $output['choice'] ] ) && $exclude == $value[ $output['choice'] ] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
$skip = true;
}
}
if ( $skip ) {
continue;
}
// Skip if value is defined as excluded.
if ( $exclude === $value || ( '' === $exclude && empty( $value ) ) ) {
$skip = true;
}
}
}
if ( $skip ) {
continue;
}
// Apply any value patterns defined.
$value = $this->apply_value_pattern( $output, $value );
if ( isset( $output['element'] ) && is_array( $output['element'] ) ) {
$output['element'] = array_unique( $output['element'] );
sort( $output['element'] );
$output['element'] = implode( ',', $output['element'] );
}
$value = $this->process_value( $value, $output );
if ( is_admin() ) {
// In admin area, only output kirki-styles/kirki-inline-styles inside editing screen.
if ( ! isset( $_GET['editor'] ) || 1 !== (int) $_GET['editor'] ) { // phpcs:ignore WordPress.Security.NonceVerification
continue;
}
} else {
// Check if this is a frontend style.
if ( isset( $output['context'] ) && ! in_array( 'front', $output['context'], true ) ) {
continue;
}
}
/**
* Inside gutenberg editing screen, prepend `.editor-styles-wrapper` to the element
* so that it doesn't polute elements other than inside the editing content.
*/
if ( isset( $_GET['editor'] ) && 1 === (int) $_GET['editor'] ) {
if ( isset( $output['element'] ) && ! empty( $output['element'] ) ) {
if ( -1 < strpos( $output['element'], ',' ) ) {
$elms = explode( ',', $output['element'] );
foreach ( $elms as $index => $elm ) {
if ( ! empty( $elm ) ) {
$elms[ $index ] = '.editor-styles-wrapper ' . $elm;
$elms[ $index ] = str_ireplace( '.editor-styles-wrapper :root', '.editor-styles-wrapper', $elms[ $index ] );
}
}
$output['element'] = implode( ',', $elms );
} else {
$output['element'] = '.editor-styles-wrapper ' . $output['element'];
$output['element'] = str_ireplace( '.editor-styles-wrapper :root', '.editor-styles-wrapper', $output['element'] );
}
}
}
$this->process_output( $output, $value );
}
}
/**
* Parses an output and creates the styles array for it.
*
* @access protected
* @param array $output The field output.
* @param string|array $value The value.
*
* @return null
*/
protected function process_output( $output, $value ) {
$output = apply_filters( 'kirki_output_item_args', $output, $value, $this->output, $this->field );
if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
return;
}
$output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
$output['prefix'] = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
$output['units'] = ( isset( $output['units'] ) ) ? $output['units'] : '';
$output['suffix'] = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
// Properties that can accept multiple values.
// Useful for example for gradients where all browsers use the "background-image" property
// and the browser prefixes go in the value_pattern arg.
$accepts_multiple = [
'background-image',
'background',
];
if ( in_array( $output['property'], $accepts_multiple, true ) ) {
if ( isset( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) && ! is_array( $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] ) ) {
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = (array) $this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ];
}
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ][] = $output['prefix'] . $value . $output['units'] . $output['suffix'];
return;
}
if ( is_string( $value ) || is_numeric( $value ) ) {
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
}
}
/**
* Some CSS properties are unique.
* We need to tweak the value to make everything works as expected.
*
* @access protected
* @param string $property The CSS property.
* @param string|array $value The value.
*
* @return array
*/
protected function process_property_value( $property, $value ) {
$properties = apply_filters(
'kirki_output_property_classnames',
[
'font-family' => '\Kirki\Module\CSS\Property\Font_Family',
'background-image' => '\Kirki\Module\CSS\Property\Background_Image',
'background-position' => '\Kirki\Module\CSS\Property\Background_Position',
]
);
if ( array_key_exists( $property, $properties ) ) {
$classname = $properties[ $property ];
$obj = new $classname( $property, $value );
return $obj->get_value();
}
return $value;
}
/**
* Returns the value.
*
* @access protected
* @param string|array $value The value.
* @param array $output The field "output".
* @return string|array
*/
protected function process_value( $value, $output ) {
if ( isset( $output['property'] ) ) {
return $this->process_property_value( $output['property'], $value );
}
return $value;
}
/**
* Exploses the private $styles property to the world
*
* @access protected
* @return array
*/
public function get_styles() {
return $this->styles;
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* Handles CSS properties.
* Extend this class in order to handle exceptions.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2019, Ari Stathopoulos (@aristath)
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
namespace Kirki\Module\CSS;
/**
* Output for CSS properties.
*/
class Property {
/**
* The property we're modifying.
*
* @access protected
* @var string
*/
protected $property;
/**
* The value
*
* @access protected
* @var string|array
*/
protected $value;
/**
* Constructor.
*
* @access public
* @param string $property The CSS property we're modifying.
* @param mixed $value The value.
*/
public function __construct( $property, $value ) {
$this->property = $property;
$this->value = $value;
$this->process_value();
}
/**
* Modifies the value.
*
* @access protected
*/
protected function process_value() {
}
/**
* Gets the value.
*
* @access protected
*/
public function get_value() {
return $this->value;
}
}

View file

@ -0,0 +1,41 @@
<?php
/**
* Handles CSS output for background-image.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2019, Ari Stathopoulos (@aristath)
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
namespace Kirki\Module\CSS\Property;
use Kirki\Module\CSS\Property;
/**
* Output overrides.
*/
class Background_Image extends Property {
/**
* Modifies the value.
*
* @access protected
*/
protected function process_value() {
if ( is_array( $this->value ) && isset( $this->value['url'] ) ) {
$this->value = $this->value['url'];
}
if ( false === strpos( $this->value, 'gradient' ) && false === strpos( $this->value, 'url(' ) ) {
if ( empty( $this->value ) ) {
return;
}
if ( preg_match( '/^\d+$/', $this->value ) ) {
$this->value = 'url("' . set_url_scheme( wp_get_attachment_url( $this->value ) ) . '")';
} else {
$this->value = 'url("' . set_url_scheme( $this->value ) . '")';
}
}
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* Handles CSS output for background-position.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2019, Ari Stathopoulos (@aristath)
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
namespace Kirki\Module\CSS\Property;
use Kirki\Module\CSS\Property;
/**
* Output overrides.
*/
class Background_Position extends Property {
/**
* Modifies the value.
*
* @access protected
*/
protected function process_value() {
$this->value = trim( $this->value );
// If you use calc() there, I suppose you know what you're doing.
// No need to process this any further, just exit.
if ( false !== strpos( $this->value, 'calc' ) ) {
return;
}
// If the value is initial or inherit, we don't need to do anything.
// Just exit.
if ( 'initial' === $this->value || 'inherit' === $this->value ) {
return;
}
$x_dimensions = [ 'left', 'center', 'right' ];
$y_dimensions = [ 'top', 'center', 'bottom' ];
// If there's a space, we have an X and a Y value.
if ( false !== strpos( $this->value, ' ' ) ) {
$xy = explode( ' ', $this->value );
$x = trim( $xy[0] );
$y = trim( $xy[1] );
// If x is not left/center/right, we need to sanitize it.
if ( ! in_array( $x, $x_dimensions, true ) ) {
$x = sanitize_text_field( $x );
}
if ( ! in_array( $y, $y_dimensions, true ) ) {
$y = sanitize_text_field( $y );
}
$this->value = $x . ' ' . $y;
return;
}
$x = 'center';
foreach ( $x_dimensions as $x_dimension ) {
if ( false !== strpos( $this->value, $x_dimension ) ) {
$x = $x_dimension;
}
}
$y = 'center';
foreach ( $y_dimensions as $y_dimension ) {
if ( false !== strpos( $this->value, $y_dimension ) ) {
$y = $y_dimension;
}
}
$this->value = $x . ' ' . $y;
}
}

View file

@ -0,0 +1,47 @@
<?php
/**
* Handles CSS output for font-family.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2019, Ari Stathopoulos (@aristath)
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
namespace Kirki\Module\CSS\Property;
use Kirki\Module\CSS\Property;
use Kirki\Module\Webfonts\Fonts;
/**
* Output overrides.
*/
class Font_Family extends Property {
/**
* Modifies the value.
*
* @access protected
*/
protected function process_value() {
$google_fonts_array = Fonts::get_google_fonts();
$family = $this->value;
// Make sure the value is a string.
// If not, then early exit.
if ( ! is_string( $family ) ) {
return;
}
// Hack for standard fonts.
$family = str_replace( '&quot;', '"', $family );
// Add double quotes if needed.
if ( false !== strpos( $family, ' ' ) && false === strpos( $family, '"' ) ) {
$this->value = '"' . $family . '"';
}
$this->value = html_entity_decode( $family, ENT_QUOTES );
}
}