balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-redux-output.php (15110B)


      1 <?php
      2 /**
      3  * Redux Output Class
      4  *
      5  * @class   Redux_Output
      6  * @version 3.0.0
      7  * @package Redux Framework/Classes
      8  */
      9 
     10 defined( 'ABSPATH' ) || exit;
     11 
     12 if ( ! class_exists( 'Redux_Output', false ) ) {
     13 
     14 	/**
     15 	 * Class Redux_Output
     16 	 */
     17 	class Redux_Output extends Redux_Class {
     18 
     19 		/**
     20 		 * Redux_Output constructor.
     21 		 *
     22 		 * @param object $parent ReduxFramework pointer.
     23 		 */
     24 		public function __construct( $parent ) {
     25 			parent::__construct( $parent );
     26 
     27 			// Output dynamic CSS.
     28 			// Frontend: Maybe enqueue dynamic CSS and Google fonts.
     29 			if ( empty( $this->args['output_location'] ) || in_array( 'frontend', $this->args['output_location'], true ) ) {
     30 				add_action( 'wp_head', array( $this, 'output_css' ), 150 );
     31 				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ), 150 );
     32 			}
     33 
     34 			// Login page: Maybe enqueue dynamic CSS and Google fonts.
     35 			if ( in_array( 'login', $this->args['output_location'], true ) ) {
     36 				add_action( 'login_head', array( $this, 'output_css' ), 150 );
     37 				add_action( 'login_enqueue_scripts', array( $this, 'enqueue' ), 150 );
     38 			}
     39 
     40 			// Admin area: Maybe enqueue dynamic CSS and Google fonts.
     41 			if ( in_array( 'admin', $this->args['output_location'], true ) ) {
     42 				add_action( 'admin_head', array( $this, 'output_css' ), 150 );
     43 				add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ), 150 );
     44 			}
     45 
     46 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
     47 			do_action( "redux/output/{$this->parent->args['opt_name']}/construct", $this );
     48 			// Useful for adding different locations for CSS output.
     49 		}
     50 
     51 		/**
     52 		 * Enqueue CSS and Google fonts for front end
     53 		 *
     54 		 * @return      void
     55 		 * @since       1.0.0
     56 		 * @access      public
     57 		 */
     58 		public function enqueue() {
     59 			$core = $this->core();
     60 
     61 			if ( false === $core->args['output'] && false === $core->args['compiler'] ) {
     62 				return;
     63 			}
     64 
     65 			foreach ( $core->sections as $k => $section ) {
     66 				if ( isset( $section['type'] ) && ( 'divide' === $section['type'] ) ) {
     67 					continue;
     68 				}
     69 
     70 				if ( isset( $section['fields'] ) ) {
     71 					foreach ( $section['fields'] as $fieldk => $field ) {
     72 						if ( isset( $field['type'] ) && 'callback' !== $field['type'] ) {
     73 							$field_classes = array( 'Redux_' . $field['type'], 'ReduxFramework_' . $field['type'] );
     74 
     75 							$field_class = Redux_Functions::class_exists_ex( $field_classes );
     76 
     77 							if ( false === $field_class ) {
     78 								if ( ! isset( $field['compiler'] ) ) {
     79 									$field['compiler'] = '';
     80 								}
     81 
     82 								/**
     83 								 * Field class file
     84 								 * filter 'redux/{opt_name}/field/class/{field.type}
     85 								 *
     86 								 * @param string        field class file
     87 								 * @param array $field field config data
     88 								 */
     89 								$field_type = str_replace( '_', '-', $field['type'] );
     90 								$core_path  = Redux_Core::$dir . "inc/fields/{$field['type']}/class-redux-$field_type.php";
     91 
     92 								if ( ! file_exists( $core_path ) ) {
     93 									$core_path = Redux_Core::$dir . "inc/fields/{$field['type']}/field_{$field['type']}.php";
     94 								}
     95 
     96 								if ( Redux_Core::$pro_loaded ) {
     97 									$pro_path = '';
     98 
     99 									if ( class_exists( 'Redux_Pro' ) ) {
    100 										$pro_path = Redux_Pro::$dir . "core/inc/fields/{$field['type']}/class-redux-$field_type.php";
    101 									}
    102 
    103 									if ( file_exists( $pro_path ) ) {
    104 										$filter_path = $pro_path;
    105 									} else {
    106 										$filter_path = $core_path;
    107 									}
    108 								} else {
    109 									$filter_path = $core_path;
    110 								}
    111 
    112 								// phpcs:ignore WordPress.NamingConventions.ValidHookName
    113 								$class_file = apply_filters( "redux/{$core->args['opt_name']}/field/class/{$field['type']}", $filter_path, $field );
    114 
    115 								if ( $class_file && file_exists( $class_file ) && ( ! class_exists( $field_class ) ) ) {
    116 									require_once $class_file;
    117 
    118 									$field_class = Redux_Functions::class_exists_ex( $field_classes );
    119 								}
    120 							}
    121 
    122 							$field['default'] = $field['default'] ?? '';
    123 							$value            = $core->options[ $field['id'] ] ?? $field['default'];
    124 							$style_data       = '';
    125 							$data             = array(
    126 								'field' => $field,
    127 								'value' => $value,
    128 								'core'  => $core,
    129 								'mode'  => 'output',
    130 							);
    131 
    132 							Redux_Functions::load_pro_field( $data );
    133 
    134 							if ( empty( $field_class ) ) {
    135 								continue;
    136 							}
    137 
    138 							$field_object = new $field_class( $field, $value, $core );
    139 
    140 							if ( ! empty( $core->options[ $field['id'] ] ) && class_exists( $field_class ) && method_exists( $field_class, 'output' ) && $this->can_output_css( $core, $field ) ) {
    141 
    142 								// phpcs:ignore WordPress.NamingConventions.ValidHookName
    143 								$field = apply_filters( "redux/field/{$core->args['opt_name']}/output_css", $field );
    144 
    145 								if ( ! empty( $field['output'] ) && ! is_array( $field['output'] ) ) {
    146 									$field['output'] = array( $field['output'] );
    147 								}
    148 
    149 								if ( ( ( isset( $field['output'] ) && ! empty( $field['output'] ) ) || ( isset( $field['compiler'] ) && ! empty( $field['compiler'] ) ) || isset( $field['media_query'] ) && ! empty( $field['media_query'] ) || 'typography' === $field['type'] || 'icon_select' === $field['type'] ) ) {
    150 									if ( method_exists( $field_class, 'css_style' ) ) {
    151 										$style_data = $field_object->css_style( $field_object->value );
    152 									}
    153 								}
    154 
    155 								if ( null !== $style_data ) {
    156 									if ( ( ( isset( $field['output'] ) && ! empty( $field['output'] ) ) || ( isset( $field['compiler'] ) && ! empty( $field['compiler'] ) ) || 'typography' === $field['type'] || 'icon_select' === $field['type'] ) ) {
    157 										$field_object->output( $style_data );
    158 									}
    159 
    160 									if ( isset( $field['media_query'] ) && ! empty( $field['media_query'] ) ) {
    161 										$field_object->media_query( $style_data );
    162 									}
    163 								}
    164 							}
    165 
    166 							// phpcs:ignore WordPress.NamingConventions.ValidHookName
    167 							do_action( "redux/field/{$core->args['opt_name']}/output_loop", $core, $field, $value, $style_data );
    168 
    169 							// phpcs:ignore WordPress.NamingConventions.ValidHookName
    170 							do_action( "redux/field/{$core->args['opt_name']}/output_loop/{$field['type']}", $core, $field, $value, $style_data );
    171 
    172 							if ( method_exists( $field_class, 'output_variables' ) && $this->can_output_css( $core, $field ) ) {
    173 								$passed_style_data = $field_object->output_variables( $style_data );
    174 								$this->output_variables( $core, $section, $field, $value, $passed_style_data );
    175 							}
    176 						}
    177 					}
    178 
    179 					if ( ! empty( $core->outputCSS ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
    180 						// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
    181 						$core->outputCSS = html_entity_decode( $core->outputCSS, ENT_QUOTES, 'UTF-8' );
    182 					}
    183 				}
    184 			}
    185 
    186 			// For use like in the customizer. Stops the output, but passes the CSS in the variable for the compiler.
    187 			if ( isset( $core->no_output ) ) {
    188 				return;
    189 			}
    190 
    191 			if ( ! empty( $core->typography ) && filter_var( $core->args['output'], FILTER_VALIDATE_BOOLEAN ) ) {
    192 				$version = ! empty( $core->transients['last_save'] ) ? $core->transients['last_save'] : '';
    193 				if ( ! class_exists( 'Redux_Typography' ) ) {
    194 					require_once Redux_Core::$dir . '/inc/fields/typography/class-redux-typography.php';
    195 				}
    196 				$typography = new Redux_Typography( null, null, $core );
    197 
    198 				if ( ! $core->args['disable_google_fonts_link'] ) {
    199 					$url = $typography->make_google_web_font_link( $core->typography );
    200 					wp_enqueue_style( 'redux-google-fonts-' . $core->args['opt_name'], $url, array(), $version );
    201 					add_filter( 'style_loader_tag', array( $this, 'add_style_attributes' ), 10, 4 );
    202 					add_filter( 'wp_resource_hints', array( $this, 'google_fonts_preconnect' ), 10, 2 );
    203 				}
    204 			}
    205 		}
    206 
    207 		/**
    208 		 * Add Google Fonts preconnect link.
    209 		 *
    210 		 * @param array  $urls              HTML to be added.
    211 		 * @param string $relationship_type Handle name.
    212 		 *
    213 		 * @return      array
    214 		 * @since       4.1.15
    215 		 * @access      public
    216 		 */
    217 		public function google_fonts_preconnect( array $urls, string $relationship_type ): array {
    218 			if ( 'preconnect' !== $relationship_type ) {
    219 				return $urls;
    220 			}
    221 			$urls[] = array(
    222 				'rel'  => 'preconnect',
    223 				'href' => 'https://fonts.gstatic.com',
    224 				'crossorigin',
    225 			);
    226 			return $urls;
    227 		}
    228 
    229 		/**
    230 		 * Filter to enhance the google fonts enqueue.
    231 		 *
    232 		 * @param string $html   HTML to be added.
    233 		 * @param string $handle Handle name.
    234 		 * @param string $href   HREF URL of script.
    235 		 * @param string $media  Media type.
    236 		 *
    237 		 * @return      string
    238 		 * @since       4.1.15
    239 		 * @access      public
    240 		 */
    241 		public function add_style_attributes( string $html = '', string $handle = '', string $href = '', string $media = '' ): string {
    242 			if ( Redux_Functions_Ex::string_starts_with( $handle, 'redux-google-fonts-' ) ) {
    243 				// Revamp thanks to Harry: https://csswizardry.com/2020/05/the-fastest-google-fonts/.
    244 				$href      = str_replace( array( '|', ' ' ), array( '%7C', '%20' ), urldecode( $href ) );
    245 				$new_html  = '<link rel="preload" as="style" href="' . esc_attr( $href ) . '" />';
    246 				$new_html .= '<link rel="stylesheet" href="' . esc_attr( $href ) . '" media="print" onload="this.media=\'all\'">';  // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
    247 				$new_html .= '<noscript><link rel="stylesheet" href="' . esc_attr( $href ) . '" /></noscript>'; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
    248 				$html      = $new_html;
    249 			}
    250 
    251 			return $html;
    252 		}
    253 
    254 		/**
    255 		 * Function to output output_variables to the dynamic output.
    256 		 *
    257 		 * @param object       $core       ReduxFramework core pointer.
    258 		 * @param array        $section    Section containing this field.
    259 		 * @param array        $field      Field object.
    260 		 * @param array|string $value      Current value of field.
    261 		 * @param string|null  $style_data CSS output string to append to the root output variable.
    262 		 *
    263 		 * @return      void
    264 		 * @since       4.0.3
    265 		 * @access      public
    266 		 */
    267 		private function output_variables( $core, array $section = array(), array $field = array(), $value = array(), ?string $style_data = '' ) {
    268 			// Let's allow section overrides please.
    269 			if ( isset( $section['output_variables'] ) && ! isset( $field['output_variables'] ) ) {
    270 				$field['output_variables'] = $section['output_variables'];
    271 			}
    272 			if ( isset( $section['output_variables_prefix'] ) && ! isset( $field['output_variables_prefix'] ) ) {
    273 				$field['output_variables_prefix'] = $section['output_variables_prefix'];
    274 			}
    275 			if ( isset( $field['output_variables'] ) && $field['output_variables'] ) {
    276 				$output_variables_prefix = $core->args['output_variables_prefix'];
    277 				if ( isset( $field['output_variables_prefix'] ) && ! empty( $field['output_variables_prefix'] ) ) {
    278 					$output_variables_prefix = $field['output_variables_prefix'];
    279 				} elseif ( isset( $section['output_variables_prefix'] ) && ! empty( $section['output_variables_prefix'] ) ) {
    280 					$output_variables_prefix = $section['output_variables_prefix'];
    281 				}
    282 
    283 				if ( is_array( $value ) ) {
    284 					$val_pieces = array_filter( $value, 'strlen' );
    285 					// We don't need to show the Google boolean.
    286 					if ( 'typography' === $field['type'] && isset( $val_pieces['google'] ) ) {
    287 						unset( $val_pieces['google'] );
    288 					}
    289 
    290 					foreach ( $val_pieces as $val_key => $val_val ) {
    291 						$val_key                            = $output_variables_prefix . sanitize_title_with_dashes( $field['id'] ) . '-' . $val_key;
    292 						$core->output_variables[ $val_key ] = $val_val;
    293 						if ( ! empty( $style_data ) ) {
    294 							$val_key                            = $output_variables_prefix . sanitize_title_with_dashes( $field['id'] );
    295 							$core->output_variables[ $val_key ] = $style_data;
    296 						}
    297 					}
    298 				} else {
    299 					$val_key = $output_variables_prefix . sanitize_title_with_dashes( $field['id'] );
    300 
    301 					if ( ! empty( $style_data ) ) {
    302 						$core->output_variables[ $val_key ] = $style_data;
    303 					} else {
    304 						$core->output_variables[ $val_key ] = $value;
    305 					}
    306 				}
    307 			}
    308 		}
    309 
    310 		/**
    311 		 * Output dynamic CSS at bottom of HEAD
    312 		 *
    313 		 * @return      void
    314 		 * @since       3.2.8
    315 		 * @access      public
    316 		 */
    317 		public function output_css() {
    318 			$core = $this->core();
    319 
    320 			if ( false === $core->args['output'] && false === $core->args['compiler'] && empty( $core->output_variables ) ) {
    321 				return;
    322 			}
    323 
    324 			if ( isset( $core->no_output ) ) {
    325 				return;
    326 			}
    327 
    328 			if ( ! empty( $core->output_variables ) ) {
    329 				$root_css = ':root{';
    330 				foreach ( $core->output_variables as $key => $value ) {
    331 					$root_css .= "$key:$value;";
    332 				}
    333 				$root_css .= '}';
    334 				// phpcs:ignore WordPress.NamingConventions.ValidVariableName, WordPress.Security.EscapeOutput
    335 				$core->outputCSS = $root_css . $core->outputCSS;
    336 			}
    337 
    338 			// phpcs:ignore WordPress.NamingConventions.ValidVariableName
    339 			if ( ! empty( $core->outputCSS ) && ( true === $core->args['output_tag'] || ( isset( $_POST['customized'] ) && isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'preview-customize_' . wp_get_theme()->get_stylesheet() ) ) ) ) {
    340 				// phpcs:ignore WordPress.NamingConventions.ValidVariableName, WordPress.Security.EscapeOutput
    341 				echo '<style id="' . esc_attr( $core->args['opt_name'] ) . '-dynamic-css" title="dynamic-css" class="redux-options-output">' . $core->outputCSS . '</style>';
    342 			}
    343 		}
    344 
    345 		/**
    346 		 * Can Output CSS
    347 		 * Check if a field meets its requirements before outputting to CSS
    348 		 *
    349 		 * @param object $core  ReduxFramework core pointer.
    350 		 * @param array  $field Field array.
    351 		 *
    352 		 * @return bool
    353 		 */
    354 		private function can_output_css( $core, array $field ): ?bool {
    355 			$return = true;
    356 
    357 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    358 			$field = apply_filters( "redux/field/{$core->args['opt_name']}/_can_output_css", $field );
    359 
    360 			if ( isset( $field['force_output'] ) && true === $field['force_output'] ) {
    361 				return $return;
    362 			}
    363 
    364 			if ( ! empty( $field['required'] ) ) {
    365 				if ( isset( $field['required'][0] ) ) {
    366 					if ( ! is_array( $field['required'][0] ) && 3 === count( $field['required'] ) ) {
    367 						$parent_value = $GLOBALS[ $core->args['global_variable'] ][ $field['required'][0] ] ?? '';
    368 						$check_value  = $field['required'][2];
    369 						$operation    = $field['required'][1];
    370 						$return       = $core->required_class->compare_value_dependencies( $parent_value, $check_value, $operation );
    371 					} elseif ( is_array( $field['required'][0] ) ) {
    372 						foreach ( $field['required'] as $required ) {
    373 							if ( isset( $required[0] ) && ! is_array( $required[0] ) && 3 === count( $required ) ) {
    374 								$parent_value = $GLOBALS[ $core->args['global_variable'] ][ $required[0] ] ?? '';
    375 								$check_value  = $required[2];
    376 								$operation    = $required[1];
    377 								$return       = $core->required_class->compare_value_dependencies( $parent_value, $check_value, $operation );
    378 							}
    379 							if ( ! $return ) {
    380 								return $return;
    381 							}
    382 						}
    383 					}
    384 				}
    385 			}
    386 
    387 			return $return;
    388 		}
    389 
    390 	}
    391 
    392 }