Plugin rewritten from ground up

Version 2.0
This commit is contained in:
LebCit
2018-11-22 20:05:59 +02:00
parent 9c68e0dd59
commit 97d20e9a81
277 changed files with 61349 additions and 2 deletions
@@ -0,0 +1,151 @@
<?php
/**
* Writes compiled CSS to a file.
*
* @package Kirki
* @subpackage CSS Module
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 3.0.0
*/
/**
* Handles writing CSS to a file.
*/
class Kirki_CSS_To_File {
/**
* Fallback to inline CSS?
*
* @access protected
* @since 3.0.0
* @var bool
*/
protected $fallback = false;
/**
* Constructor.
*
* @access public
* @since 3.0.0
*/
public function __construct() {
// If the file doesn't exist, create it.
if ( ! file_exists( $this->get_path( 'file' ) ) ) {
// If the file-write fails, fallback to inline
// and cache the failure so we don't try again immediately.
$this->write_file();
}
add_action( 'customize_save_after', array( $this, 'write_file' ) );
}
/**
* Gets the path of the CSS file and folder in the filesystem.
*
* @access protected
* @since 3.0.0
* @param string $context Can be "file" or "folder". If empty, returns both as array.
* @return string|array
*/
protected function get_path( $context = '' ) {
$upload_dir = wp_upload_dir();
$paths = array(
'file' => wp_normalize_path( $upload_dir['basedir'] . '/kirki-css/styles.css' ),
'folder' => wp_normalize_path( $upload_dir['basedir'] . '/kirki-css' ),
);
if ( 'file' === $context ) {
return $paths['file'];
}
if ( 'folder' === $context ) {
return $paths['folder'];
}
return $paths;
}
/**
* Gets the URL of the CSS file in the filesystem.
*
* @access public
* @since 3.0.0
* @return string
*/
public function get_url() {
$upload_dir = wp_upload_dir();
return esc_url_raw( $upload_dir['baseurl'] . '/kirki-css/styles.css' );
}
/**
* Gets the timestamp of the file.
* This will be used as "version" for cache-busting purposes.
*
* @access public
* @since 3.0.0
* @return integer|false
*/
public function get_timestamp() {
if ( file_exists( $this->get_path( 'file' ) ) ) {
return filemtime( $this->get_path( 'file' ) );
}
return false;
}
/**
* Writes the file to disk.
*
* @access public
* @since 3.0.0
* @return bool
*/
public function write_file() {
$css = array();
$configs = Kirki::$config;
foreach ( $configs as $config_id => $args ) {
// Get the CSS we want to write.
$css[ $config_id ] = apply_filters( "kirki_{$config_id}_dynamic_css", Kirki_Modules_CSS::loop_controls( $config_id ) );
}
$css = implode( $css, '' );
// If the folder doesn't exist, create it.
if ( ! file_exists( $this->get_path( 'folder' ) ) ) {
wp_mkdir_p( $this->get_path( 'folder' ) );
}
$filesystem = $this->get_filesystem();
$write_file = (bool) $filesystem->put_contents( $this->get_path( 'file' ), $css );
if ( ! $write_file ) {
$this->fallback = true;
set_transient( 'kirki_css_write_to_file_failed', true, HOUR_IN_SECONDS );
}
return $write_file;
}
/**
* Gets the WP_Filesystem object.
*
* @access protected
* @since 3.0.0
* @return object
*/
protected function get_filesystem() {
// The WordPress filesystem.
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
WP_Filesystem();
}
return $wp_filesystem;
}
}
@@ -0,0 +1,280 @@
<?php
/**
* Generates the styles for the frontend.
* Handles the 'output' argument of fields
*
* @package Kirki
* @category Core
* @author Aristeides Stathopoulos
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 1.0
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handles CSS output.
*/
final class Kirki_Modules_CSS_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 = array();
/**
* 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;
/**
* Standard Fonts
*
* @static
* @access public
* @var array
*/
public static $backup_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 = Kirki_Fonts::get_google_fonts();
}
if ( is_null( self::$backup_fonts ) ) {
self::$backup_fonts = Kirki_Fonts::get_backup_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 = $field['sanitize_callback'];
self::$field_type = $field['type'];
self::$output = $field['output'];
if ( ! is_array( self::$output ) ) {
self::$output = array(
array(
'element' => self::$output,
'sanitize_callback' => null,
),
);
}
// Get the value of this field.
self::$value = Kirki_Values::get_sanitized_field_value( $field );
// Find the class that will handle the outpout for this field.
$classname = 'Kirki_Output';
$field_output_classes = apply_filters(
"kirki_{$field['kirki_config']}_output_control_classnames", array(
'kirki-background' => 'Kirki_Output_Field_Background',
'kirki-dimensions' => 'Kirki_Output_Field_Dimensions',
'kirki-image' => 'Kirki_Output_Field_Image',
'kirki-typography' => 'Kirki_Output_Field_Typography',
'kirki-multicolor' => 'Kirki_Output_Field_Multicolor',
)
);
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 = array() ) {
// 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, array(
'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, array(
'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;
}
}
@@ -0,0 +1,319 @@
<?php
/**
* Handles the CSS Output of fields.
*
* @package Kirki
* @category Modules
* @author Aristeides Stathopoulos
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 3.0.0
*/
/**
* The Kirki_Modules_CSS object.
*/
class Kirki_Modules_CSS {
/**
* The object instance.
*
* @static
* @access private
* @since 3.0.0
* @var object
*/
private static $instance;
/**
* Whether we've already processed this or not.
*
* @access public
* @var bool
*/
public $processed = false;
/**
* The CSS array
*
* @access public
* @var array
*/
public static $css_array = array();
/**
* Set to true if you want to use the AJAX method.
*
* @access public
* @var bool
*/
public static $ajax = false;
/**
* The Kirki_CSS_To_File object.
*
* @access protected
* @since 3.0.0
* @var object
*/
protected $css_to_file;
/**
* Should we enqueue font-awesome?
*
* @static
* @access protected
* @since 3.0.26
* @var bool
*/
protected static $enqueue_fa = false;
/**
* Constructor
*
* @access protected
*/
protected function __construct() {
$class_files = array(
'Kirki_CSS_To_File' => '/class-kirki-css-to-file.php',
'Kirki_Modules_CSS_Generator' => '/class-kirki-modules-css-generator.php',
'Kirki_Output' => '/class-kirki-output.php',
'Kirki_Output_Field_Background' => '/field/class-kirki-output-field-background.php',
'Kirki_Output_Field_Image' => '/field/class-kirki-output-field-image.php',
'Kirki_Output_Field_Multicolor' => '/field/class-kirki-output-field-multicolor.php',
'Kirki_Output_Field_Dimensions' => '/field/class-kirki-output-field-dimensions.php',
'Kirki_Output_Field_Typography' => '/field/class-kirki-output-field-typography.php',
'Kirki_Output_Property' => '/property/class-kirki-output-property.php',
'Kirki_Output_Property_Background_Image' => '/property/class-kirki-output-property-background-image.php',
'Kirki_Output_Property_Background_Position' => '/property/class-kirki-output-property-background-position.php',
'Kirki_Output_Property_Font_Family' => '/property/class-kirki-output-property-font-family.php',
);
foreach ( $class_files as $class_name => $file ) {
if ( ! class_exists( $class_name ) ) {
include_once wp_normalize_path( dirname( __FILE__ ) . $file );
}
}
add_action( 'init', array( $this, 'init' ) );
}
/**
* Gets an instance of this object.
* Prevents duplicate instances which avoid artefacts and improves performance.
*
* @static
* @access public
* @since 3.0.0
* @return object
*/
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Init.
*
* @access public
*/
public function init() {
Kirki_Modules_Webfonts::get_instance();
global $wp_customize;
$config = apply_filters( 'kirki_config', array() );
$priority = 999;
if ( isset( $config['styles_priority'] ) ) {
$priority = absint( $config['styles_priority'] );
}
// Allow completely disabling Kirki CSS output.
if ( ( defined( 'KIRKI_NO_OUTPUT' ) && true === KIRKI_NO_OUTPUT ) || ( isset( $config['disable_output'] ) && true === $config['disable_output'] ) ) {
return;
}
$method = apply_filters( 'kirki_dynamic_css_method', 'inline' );
if ( $wp_customize ) {
// If we're in the customizer, load inline no matter what.
add_action( 'wp_enqueue_scripts', array( $this, 'inline_dynamic_css' ), $priority );
// If we're using file method, on save write the new styles.
if ( 'file' === $method ) {
$this->css_to_file = new Kirki_CSS_To_File();
add_action( 'customize_save_after', array( $this->css_to_file, 'write_file' ) );
}
return;
}
if ( 'file' === $method ) {
// Attempt to write the CSS to file.
$this->css_to_file = new Kirki_CSS_To_File();
// If we succesd, load this file.
$failed = get_transient( 'kirki_css_write_to_file_failed' );
// If writing CSS to file hasn't failed, just enqueue this file.
if ( ! $failed ) {
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_compiled_file' ), $priority );
return;
}
}
// If we are in the customizer, load CSS using inline-styles.
// If we are in the frontend AND self::$ajax is true, then load dynamic CSS using AJAX.
if ( ( true === self::$ajax ) || ( isset( $config['inline_css'] ) && false === $config['inline_css'] ) ) {
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_styles' ), $priority );
add_action( 'wp_ajax_kirki_dynamic_css', array( $this, 'ajax_dynamic_css' ) );
add_action( 'wp_ajax_nopriv_kirki_dynamic_css', array( $this, 'ajax_dynamic_css' ) );
return;
}
// If we got this far then add styles inline.
add_action( 'wp_enqueue_scripts', array( $this, 'inline_dynamic_css' ), $priority );
}
/**
* Enqueues compiled CSS file.
*
* @access public
* @since 3.0.0
*/
public function enqueue_compiled_file() {
wp_enqueue_style( 'kirki-styles', $this->css_to_file->get_url(), array(), $this->css_to_file->get_timestamp() );
}
/**
* Adds inline styles.
*
* @access public
*/
public function inline_dynamic_css() {
$configs = Kirki::$config;
if ( ! $this->processed ) {
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 ) ) {
$stylesheet = apply_filters( "kirki_{$config_id}_stylesheet", false );
if ( $stylesheet ) {
wp_add_inline_style( $stylesheet, $styles );
continue;
}
wp_enqueue_style( 'kirki-styles-' . $config_id, trailingslashit( Kirki::$url ) . 'assets/css/kirki-styles.css', array(), KIRKI_VERSION );
wp_add_inline_style( 'kirki-styles-' . $config_id, $styles );
}
}
$this->processed = true;
}
if ( self::$enqueue_fa && apply_filters( 'kirki_load_fontawesome', true ) ) {
wp_enqueue_script( 'kirki-fontawesome-font', 'https://use.fontawesome.com/30858dc40a.js', array(), '4.0.7', true );
}
}
/**
* Get the dynamic-css.php file
*
* @access public
*/
public function ajax_dynamic_css() {
require wp_normalize_path( Kirki::$path . '/modules/css/dynamic-css.php' );
exit;
}
/**
* Enqueues the ajax stylesheet.
*
* @access public
*/
public function frontend_styles() {
wp_enqueue_style( 'kirki-styles-php', admin_url( 'admin-ajax.php' ) . '?action=kirki_dynamic_css', array(), KIRKI_VERSION );
}
/**
* 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 Kirki_Modules_CSS_Generator class.
// This will make sure google fonts and backup fonts are loaded.
Kirki_Modules_CSS_Generator::get_instance();
$fields = Kirki::$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 ( $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 ( ! empty( $field['required'] ) ) {
$valid = true;
foreach ( $field['required'] as $requirement ) {
if ( isset( $requirement['setting'] ) && isset( $requirement['value'] ) && isset( $requirement['operator'] ) ) {
$controller_value = Kirki_Values::get_value( $config_id, $requirement['setting'] );
if ( ! Kirki_Helper::compare_values( $controller_value, $requirement['value'], $requirement['operator'] ) ) {
$valid = false;
}
}
}
if ( ! $valid ) {
continue;
}
}
}
// Only continue if $field['output'] is set.
if ( isset( $field['output'] ) && ! empty( $field['output'] ) ) {
$css = Kirki_Helper::array_replace_recursive( $css, Kirki_Modules_CSS_Generator::css( $field ) );
// Add the globals.
if ( isset( self::$css_array[ $config_id ] ) && ! empty( self::$css_array[ $config_id ] ) ) {
Kirki_Helper::array_replace_recursive( $css, self::$css_array[ $config_id ] );
}
}
}
$css = apply_filters( "kirki_{$config_id}_styles", $css );
if ( is_array( $css ) ) {
return Kirki_Modules_CSS_Generator::styles_parse( Kirki_Modules_CSS_Generator::add_prefixes( $css ) );
}
}
/**
* Runs when we're adding a font-awesome field to allow enqueueing the
* fontawesome script on the frontend.
*
* @static
* @since 3.0.26
* @access public
* @return void
*/
public static function add_fontawesome_script() {
self::$enqueue_fa = true;
}
}
@@ -0,0 +1,338 @@
<?php
/**
* Handles CSS output for fields.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Handles field CSS output.
*/
class Kirki_Output {
/**
* The Kirki configuration used in the field.
*
* @access protected
* @var string
*/
protected $config_id = 'global';
/**
* The field's `output` argument.
*
* @access protected
* @var array
*/
protected $output = array();
/**
* An array of the generated styles.
*
* @access protected
* @var array
*/
protected $styles = array();
/**
* The field.
*
* @access protected
* @var array
*/
protected $field = array();
/**
* 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->config_id = $config_id;
$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'] ) && ! empty( $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_string( $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 = ( '' !== Kirki::get_config_param( $this->config_id, 'option_type' ) ) ? Kirki::get_config_param( $this->config_id, 'option_type' ) : 'theme_mod';
$option_name = Kirki::get_config_param( $this->config_id, 'option_name' );
$options = array();
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( array( $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 );
}
$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 );
$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 ) {
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 = array(
'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_{$this->config_id}_output_property_classnames", array(
'font-family' => 'Kirki_Output_Property_Font_Family',
'background-image' => 'Kirki_Output_Property_Background_Image',
'background-position' => 'Kirki_Output_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;
}
}
@@ -0,0 +1,48 @@
<?php
/**
* Handles CSS output for background fields.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 3.0.0
*/
/**
* Output overrides.
*/
class Kirki_Output_Field_Background extends Kirki_Output {
/**
* Processes a single item from the `output` array.
*
* @access protected
* @param array $output The `output` item.
* @param array $value The field's value.
*/
protected function process_output( $output, $value ) {
$output = wp_parse_args(
$output,
array(
'media_query' => 'global',
'element' => 'body',
'prefix' => '',
'suffix' => '',
)
);
foreach ( array( 'background-image', 'background-color', 'background-repeat', 'background-position', 'background-size', 'background-attachment' ) as $property ) {
// See https://github.com/aristath/kirki/issues/1808.
if ( 'background-color' === $property && isset( $value['background-color'] ) && $value['background-color'] && ( ! isset( $value['background-image'] ) || empty( $value['background-image'] ) ) ) {
$this->styles[ $output['media_query'] ][ $output['element'] ]['background'] = $output['prefix'] . $this->process_property_value( $property, $value[ $property ] ) . $output['suffix'];
}
if ( isset( $value[ $property ] ) && ! empty( $value[ $property ] ) ) {
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $output['prefix'] . $this->process_property_value( $property, $value[ $property ] ) . $output['suffix'];
}
}
}
}
@@ -0,0 +1,56 @@
<?php
/**
* Handles CSS output for dimensions fields.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Output overrides.
*/
class Kirki_Output_Field_Dimensions extends Kirki_Output {
/**
* Processes a single item from the `output` array.
*
* @access protected
* @param array $output The `output` item.
* @param array $value The field's value.
*/
protected function process_output( $output, $value ) {
$output = wp_parse_args(
$output, array(
'element' => '',
'property' => '',
'media_query' => 'global',
'prefix' => '',
'suffix' => '',
)
);
if ( ! is_array( $value ) ) {
return;
}
foreach ( array_keys( $value ) as $key ) {
$property = ( empty( $output['property'] ) ) ? $key : $output['property'] . '-' . $key;
if ( isset( $output['choice'] ) && $output['property'] ) {
if ( $key === $output['choice'] ) {
$property = $output['property'];
} else {
continue;
}
}
if ( false !== strpos( $output['property'], '%%' ) ) {
$property = str_replace( '%%', $key, $output['property'] );
}
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $output['prefix'] . $this->process_property_value( $property, $value[ $key ] ) . $output['suffix'];
}
}
}
@@ -0,0 +1,50 @@
<?php
/**
* Handles CSS output for image fields.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 3.0.10
*/
/**
* Output overrides.
*/
class Kirki_Output_Field_Image extends Kirki_Output {
/**
* Processes a single item from the `output` array.
*
* @access protected
* @param array $output The `output` item.
* @param array $value The field's value.
*/
protected function process_output( $output, $value ) {
if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
return;
}
$output = wp_parse_args(
$output, array(
'media_query' => 'global',
'prefix' => '',
'units' => '',
'suffix' => '',
)
);
if ( is_array( $value ) ) {
if ( isset( $output['choice'] ) && $output['choice'] ) {
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value[ $output['choice'] ] ) . $output['units'] . $output['suffix'];
return;
}
if ( isset( $value['url'] ) ) {
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value['url'] ) . $output['units'] . $output['suffix'];
return;
}
return;
}
$this->styles[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $this->process_property_value( $output['property'], $value ) . $output['units'] . $output['suffix'];
}
}
@@ -0,0 +1,54 @@
<?php
/**
* Handles CSS output for multicolor fields.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Output overrides.
*/
class Kirki_Output_Field_Multicolor extends Kirki_Output {
/**
* Processes a single item from the `output` array.
*
* @access protected
* @param array $output The `output` item.
* @param array $value The field's value.
*/
protected function process_output( $output, $value ) {
foreach ( $value as $key => $sub_value ) {
// If "element" is not defined, there's no reason to continue.
if ( ! isset( $output['element'] ) ) {
continue;
}
// If the "choice" is not the same as the $key in our loop, there's no reason to proceed.
if ( isset( $output['choice'] ) && $key !== $output['choice'] ) {
continue;
}
// If "property" is not defined, fallback to the $key.
$property = ( ! isset( $output['property'] ) || empty( $output['property'] ) ) ? $key : $output['property'];
// If "media_query" is not defined, use "global".
if ( ! isset( $output['media_query'] ) || empty( $output['media_query'] ) ) {
$output['media_query'] = 'global';
}
// If "suffix" is defined, add it to the value.
$output['suffix'] = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
// Create the styles.
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $sub_value . $output['suffix'];
}
}
}
@@ -0,0 +1,96 @@
<?php
/**
* Handles CSS output for typography fields.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Output overrides.
*/
class Kirki_Output_Field_Typography extends Kirki_Output {
/**
* Processes a single item from the `output` array.
*
* @access protected
* @param array $output The `output` item.
* @param array $value The field's value.
*/
protected function process_output( $output, $value ) {
$output['media_query'] = ( isset( $output['media_query'] ) ) ? $output['media_query'] : 'global';
$output['element'] = ( isset( $output['element'] ) ) ? $output['element'] : 'body';
$output['prefix'] = ( isset( $output['prefix'] ) ) ? $output['prefix'] : '';
$output['suffix'] = ( isset( $output['suffix'] ) ) ? $output['suffix'] : '';
$value = Kirki_Field_Typography::sanitize( $value );
$properties = array(
'font-family',
'font-size',
'variant',
'font-weight',
'font-style',
'letter-spacing',
'word-spacing',
'line-height',
'text-align',
'text-transform',
'text-decoration',
'color',
);
foreach ( $properties as $property ) {
// Early exit if the value is not in the defaults.
if ( ! isset( $this->field['default'][ $property ] ) ) {
continue;
}
// Early exit if the value is not saved in the values.
if ( ! isset( $value[ $property ] ) || ! $value[ $property ] ) {
continue;
}
// Early exit if we use "choice" but not for this property.
if ( isset( $output['choice'] ) && $output['choice'] !== $property ) {
continue;
}
// Take care of variants.
if ( 'variant' === $property && isset( $value['variant'] ) && ! empty( $value['variant'] ) ) {
// Get the font_weight.
$font_weight = str_replace( 'italic', '', $value['variant'] );
$font_weight = ( in_array( $font_weight, array( '', 'regular' ), true ) ) ? '400' : $font_weight;
// Is this italic?
$is_italic = ( false !== strpos( $value['variant'], 'italic' ) );
$this->styles[ $output['media_query'] ][ $output['element'] ]['font-weight'] = $font_weight;
if ( $is_italic ) {
$this->styles[ $output['media_query'] ][ $output['element'] ]['font-style'] = 'italic';
}
continue;
}
$property_value = $this->process_property_value( $property, $value[ $property ] );
if ( 'font-family' === $property ) {
$value['font-backup'] = ( isset( $value['font-backup'] ) ) ? $value['font-backup'] : '';
$property_value = $this->process_property_value(
$property, array(
$value['font-family'],
$value['font-backup'],
)
);
}
$property = ( isset( $output['choice'] ) && isset( $output['property'] ) ) ? $output['property'] : $property;
$property_value = ( is_array( $property_value ) && isset( $property_value[0] ) ) ? $property_value[0] : $property_value;
$this->styles[ $output['media_query'] ][ $output['element'] ][ $property ] = $output['prefix'] . $property_value . $output['suffix'];
}
}
}
@@ -0,0 +1,40 @@
<?php
/**
* Handles CSS output for background-image.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Output overrides.
*/
class Kirki_Output_Property_Background_Image extends Kirki_Output_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 ) . '")';
}
}
}
}
@@ -0,0 +1,72 @@
<?php
/**
* Handles CSS output for background-position.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Output overrides.
*/
class Kirki_Output_Property_Background_Position extends Kirki_Output_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 = array( 'left', 'center', 'right' );
$y_dimensions = array( '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;
}
}
@@ -0,0 +1,65 @@
<?php
/**
* Handles CSS output for font-family.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Output overrides.
*/
class Kirki_Output_Property_Font_Family extends Kirki_Output_Property {
/**
* Modifies the value.
*
* @access protected
*/
protected function process_value() {
$google_fonts_array = Kirki_Fonts::get_google_fonts();
$backup_fonts = Kirki_Fonts::get_backup_fonts();
$family = $this->value;
$backup = '';
if ( is_array( $this->value ) && isset( $this->value[0] ) && isset( $this->value[1] ) ) {
$family = $this->value[0];
$backup = $this->value[1];
}
// 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 backup font.
if ( Kirki_Fonts::is_google_font( $family ) ) {
if ( '' === $backup && isset( $google_fonts_array[ $family ] ) && isset( $backup_fonts[ $google_fonts_array[ $family ]['category'] ] ) ) {
$backup = $backup_fonts[ $google_fonts_array[ $family ]['category'] ];
}
// Add double quotes if needed.
if ( false !== strpos( $family, ' ' ) && false === strpos( $family, '"' ) ) {
$this->value = '"' . $family . '", ' . $backup;
return;
}
$this->value = $family . ', ' . $backup;
return;
}
// Add double quotes if needed.
if ( false !== strpos( $family, ' ' ) && false === strpos( $family, '"' ) ) {
$this->value = '"' . $family . '"';
}
$this->value = html_entity_decode( $family, ENT_QUOTES );
}
}
@@ -0,0 +1,64 @@
<?php
/**
* Handles CSS properties.
* Extend this class in order to handle exceptions.
*
* @package Kirki
* @subpackage Controls
* @copyright Copyright (c) 2017, Aristeides Stathopoulos
* @license https://opensource.org/licenses/MIT
* @since 2.2.0
*/
/**
* Output for CSS properties.
*/
class Kirki_Output_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;
}
}