balmet.com

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

class-redux-extension-metaboxes.php (51268B)


      1 <?php
      2 /**
      3  * Redux Metabox Extension Class
      4  *
      5  * @package Redux Extentions
      6  * @author  Dovy Paukstys <dovy@reduxframework.com> & Kevin Provance <kevin.provance@gmail.com>
      7  * @class   Redux_Extension_Metaboxes
      8  *
      9  * @version 4.0.0
     10  */
     11 
     12 defined( 'ABSPATH' ) || exit;
     13 
     14 // Don't duplicate me!
     15 if ( ! class_exists( 'Redux_Extension_Metaboxes', false ) ) {
     16 
     17 	/**
     18 	 * Main Redux_Extension_Metaboxes class
     19 	 *
     20 	 * @since       1.0.0
     21 	 */
     22 	class Redux_Extension_Metaboxes extends Redux_Extension_Abstract {
     23 
     24 		/**
     25 		 * Extension version.
     26 		 *
     27 		 * @var string
     28 		 */
     29 		public static $version = '4.2.0';
     30 
     31 		/**
     32 		 * Extension friendly name.
     33 		 *
     34 		 * @var string
     35 		 */
     36 		public $ext_name = 'Metaboxes';
     37 
     38 		/**
     39 		 * Boxes array.
     40 		 *
     41 		 * @var array
     42 		 */
     43 		public $boxes = array();
     44 
     45 		/**
     46 		 * Post types array.
     47 		 *
     48 		 * @var array
     49 		 */
     50 		public $post_types = array();
     51 
     52 		/**
     53 		 * Post type.
     54 		 *
     55 		 * @var string
     56 		 */
     57 		public $post_type;
     58 
     59 		/**
     60 		 * Sections array.
     61 		 *
     62 		 * @var array
     63 		 */
     64 		public $sections = array();
     65 
     66 		/**
     67 		 * CSS output array.
     68 		 *
     69 		 * @var array
     70 		 */
     71 		public $output = array();
     72 
     73 		/**
     74 		 * ReduxFramework object pointer.
     75 		 *
     76 		 * @var object
     77 		 */
     78 		public $parent = null;
     79 
     80 		/**
     81 		 * Options array.
     82 		 *
     83 		 * @var array
     84 		 */
     85 		public $options = array();
     86 
     87 		/**
     88 		 * Parent options array.
     89 		 *
     90 		 * @var array
     91 		 */
     92 		public $parent_options = array();
     93 
     94 		/**
     95 		 * Parent defaults array.
     96 		 *
     97 		 * @var array
     98 		 */
     99 		public $parent_defaults = array();
    100 
    101 		/**
    102 		 * Post type fields array.
    103 		 *
    104 		 * @var array
    105 		 */
    106 		public $post_type_fields = array();
    107 
    108 		/**
    109 		 * Options defaults array.
    110 		 *
    111 		 * @var array
    112 		 */
    113 		public $options_defaults = array();
    114 
    115 		/**
    116 		 * Replace array.
    117 		 *
    118 		 * @var array
    119 		 */
    120 		public $to_replace = array();
    121 
    122 		/**
    123 		 * Extension URI.
    124 		 *
    125 		 * @var string|void
    126 		 */
    127 		public $extension_url;
    128 
    129 		/**
    130 		 * Extension Directory.
    131 		 *
    132 		 * @var string
    133 		 */
    134 		public $extension_dir;
    135 
    136 		/**
    137 		 * Meta data array.
    138 		 *
    139 		 * @var array
    140 		 */
    141 		public $meta = array();
    142 
    143 		/**
    144 		 * Post ID.
    145 		 *
    146 		 * @var int
    147 		 */
    148 		public $post_id = 0;
    149 
    150 		/**
    151 		 * Base URI.
    152 		 *
    153 		 * @var string
    154 		 */
    155 		public $base_url;
    156 
    157 		/**
    158 		 * WP_Links array.
    159 		 *
    160 		 * @var array
    161 		 */
    162 		public $wp_links = array();
    163 
    164 		/**
    165 		 * ReduxFramework_extension_metaboxes constructor.
    166 		 *
    167 		 * @param object $parent ReduxFramework object.
    168 		 */
    169 		public function __construct( $parent ) {
    170 			global $pagenow;
    171 
    172 			$this->parent = $parent;
    173 
    174 			$this->parent->extensions['metaboxes'] = $this;
    175 
    176 			if ( empty( self::$extension_dir ) ) {
    177 				$this->extension_dir = trailingslashit( str_replace( '\\', '/', dirname( __FILE__ ) ) );
    178 				$this->extension_url = site_url( str_replace( trailingslashit( str_replace( '\\', '/', ABSPATH ) ), '', $this->extension_dir ) );
    179 			}
    180 
    181 			// Only run metaboxes on the pages/posts, not the front-end.
    182 			// The DOING_AJAX check allows for redux_post_meta to work inside
    183 			// AJAX calls. - kp.
    184 			if ( 'post-new.php' !== $pagenow && 'post.php' !== $pagenow ) {
    185 				if ( is_admin() ) {
    186 					if ( defined( 'DOING_AJAX' ) && ! DOING_AJAX ) {
    187 						return;
    188 					}
    189 
    190 					return;
    191 				}
    192 			}
    193 
    194 			if ( 'wp-cron.php' === $pagenow || 'wp-comments-post.php' === $pagenow ) {
    195 				return;
    196 			}
    197 
    198 			// Must not update the DB when just updating metaboxes. Sheesh.
    199 			if ( is_admin() && ( 'post-new.php' === $pagenow || 'post.php' === $pagenow ) ) {
    200 				$this->parent->never_save_to_db = true;
    201 			}
    202 
    203 			// phpcs:ignore Generic.Strings.UnnecessaryStringConcat
    204 			add_action( 'add_' . 'meta_' . 'boxes', array( $this, 'add' ) );
    205 			add_action( 'save_post', array( $this, 'meta_boxes_save' ), 1, 2 );
    206 			add_action( 'pre_post_update', array( $this, 'pre_post_update' ) );
    207 			add_action( 'admin_notices', array( $this, 'meta_boxes_show_errors' ), 0 );
    208 			add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ), 20 );
    209 
    210 			// Global variable overrides for within loops.
    211 			add_action( 'the_post', array( $this, 'loop_start' ), 0 );
    212 			add_action( 'loop_end', array( $this, 'loop_end' ), 0 );
    213 
    214 			$this->init();
    215 		}
    216 
    217 		/**
    218 		 * Added class names to metabox DIV.
    219 		 *
    220 		 * @param array $classes Class array.
    221 		 *
    222 		 * @return array
    223 		 */
    224 		public function add_box_classes( array $classes ): array {
    225 			$classes[] = 'redux-metabox';
    226 			$classes[] = 'redux-' . $this->parent->args['opt_name'];
    227 
    228 			if ( '' !== $this->parent->args['class'] ) {
    229 				$classes[] = $this->parent->args['class'];
    230 			}
    231 
    232 			return $classes;
    233 		}
    234 
    235 		/**
    236 		 * Class init.
    237 		 */
    238 		public function init() {
    239 			global $pagenow;
    240 
    241 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
    242 			$this->boxes = apply_filters( 'redux/metaboxes/' . $this->parent->args['opt_name'] . '/boxes', $this->boxes, $this->parent->args['opt_name'] );
    243 
    244 			if ( empty( $this->boxes ) && class_exists( 'Redux_Metaboxes' ) ) {
    245 				$this->boxes = Redux_Metaboxes::construct_boxes( $this->parent->args['opt_name'] );
    246 			}
    247 
    248 			if ( empty( $this->boxes ) || ! is_array( $this->boxes ) ) {
    249 				return;
    250 			}
    251 
    252 			if ( isset( Redux_Core::$server['HTTP_HOST'] ) && isset( Redux_Core::$server['REQUEST_URI'] ) ) {
    253 				$this->base_url = ( is_ssl() ? 'https://' : 'http://' ) . sanitize_text_field( wp_unslash( Redux_Core::$server['HTTP_HOST'] ) ) . sanitize_text_field( wp_unslash( Redux_Core::$server['REQUEST_URI'] ) ); // Safe & Reliable.
    254 				$this->post_id  = $this->url_to_postid( ( is_ssl() ? 'https://' : 'http://' ) . sanitize_text_field( wp_unslash( Redux_Core::$server['HTTP_HOST'] ) ) . sanitize_text_field( wp_unslash( Redux_Core::$server['REQUEST_URI'] ) ) );
    255 			}
    256 
    257 			if ( is_admin() && isset( $_GET['post_type'] ) && ! empty( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    258 				$this->post_type = sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
    259 			} else {
    260 				$this->post_type = get_post_type( $this->post_id );
    261 			}
    262 
    263 			foreach ( $this->boxes as $bk => $box ) {
    264 
    265 				// If the post ids for this box are set, we're limiting to the current post id.
    266 				if ( isset( $box['post_ids'] ) && ! empty( $box['post_ids'] ) ) {
    267 					if ( ! is_array( $box['post_ids'] ) ) {
    268 						$box['post_ids'] = array( $box['post_ids'] );
    269 					}
    270 					if ( ! in_array( $this->post_id, $box['post_ids'], true ) ) {
    271 						continue;
    272 					}
    273 				}
    274 
    275 				if ( ! empty( $box['sections'] ) ) {
    276 					$this->sections = $box['sections'];
    277 
    278 					array_merge( $this->parent->sections, $box['sections'] );
    279 
    280 					$this->post_types = wp_parse_args( $this->post_types, $box['post_types'] );
    281 
    282 					// Checking to overide the parent variables.
    283 					$add_field = false;
    284 
    285 					foreach ( $box['post_types'] as $type ) {
    286 						if ( $this->post_type === $type ) {
    287 							$add_field = true;
    288 						}
    289 					}
    290 
    291 					// Replacing all the fields.
    292 					if ( $add_field || ( ( is_admin() && ( 'post-new.php' === $pagenow || 'post.php' === $pagenow ) ) || ( ! is_admin() ) ) ) {
    293 						$run_hooks = true;
    294 
    295 						$box_id = 'redux-' . $this->parent->args['opt_name'] . '-metabox-' . $box['id'];
    296 
    297 						// phpcs:ignore WordPress.NamingConventions.ValidHookName
    298 						do_action( 'redux/' . $this->parent->args['opt_name'] . '/extensions/metabox/init', $this, $box );
    299 
    300 						if ( isset( $box['page_template'] ) && 'page' === $this->post_type ) {
    301 							if ( ! is_array( $box['page_template'] ) ) {
    302 								$box['page_template'] = array( $box['page_template'] );
    303 							}
    304 
    305 							$this->wp_links[ $box_id ]['page_template'] = isset( $this->wp_links[ $box_id ]['page_template'] ) ? wp_parse_args( $this->wp_links[ $box_id ]['page_template'], $box['page_template'] ) : $box['page_template'];
    306 						}
    307 
    308 						if ( isset( $box['post_format'] ) && ( in_array( $this->post_type, $this->post_types, true ) || '' === $this->post_type ) ) {
    309 							if ( ! is_array( $box['post_format'] ) ) {
    310 								$box['post_format'] = array( $box['post_format'] );
    311 							}
    312 
    313 							$this->wp_links[ $box_id ]['post_format'] = isset( $this->wp_links[ $box_id ]['post_format'] ) ? wp_parse_args( $this->wp_links[ $box_id ]['post_format'], $box['post_format'] ) : $box['post_format'];
    314 						}
    315 
    316 						$this->meta[ $this->post_id ] = $this->get_meta( $this->post_id );
    317 
    318 						foreach ( $box['sections'] as $sk => $section ) {
    319 							if ( isset( $section['fields'] ) && ! empty( $section['fields'] ) ) {
    320 								foreach ( $section['fields'] as $fk => $field ) {
    321 									if ( ! isset( $field['class'] ) ) {
    322 										$field['class'] = '';
    323 
    324 										$this->boxes[ $bk ]['sections'][ $sk ]['fields'][ $fk ] = $field;
    325 									}
    326 
    327 									if ( $add_field || ( ( is_admin() && ( 'post-new.php' === $pagenow || 'post.php' === $pagenow ) ) || ( ! is_admin() ) ) ) {
    328 										if ( empty( $field['id'] ) ) {
    329 											continue;
    330 										}
    331 
    332 										if ( isset( $field['default'] ) ) {
    333 											$this->options_defaults[ $field['id'] ] = $field['default'];
    334 										} else {
    335 											$this->options_defaults[ $field['id'] ] = $this->field_default( $field );
    336 										}
    337 
    338 										foreach ( $box['post_types'] as $type ) {
    339 											$this->post_type_fields[ $type ][ $field['id'] ] = 1;
    340 										}
    341 
    342 										if ( isset( $field['output'] ) && ! empty( $field['output'] ) ) {
    343 											$this->output[ $field['id'] ] = isset( $this->output[ $field['id'] ] ) ? array_merge( $field['output'], $this->output[ $field['id'] ] ) : $field['output'];
    344 										}
    345 
    346 										// Detect what field types are being used.
    347 										if ( ! isset( $this->parent->fields[ $field['type'] ][ $field['id'] ] ) ) {
    348 											$this->parent->fields[ $field['type'] ][ $field['id'] ] = 1;
    349 										} else {
    350 											$this->parent->fields[ $field['type'] ] = array( $field['id'] => 1 );
    351 										}
    352 
    353 										if ( isset( $this->options_defaults[ $field['id'] ] ) ) {
    354 											$this->to_replace[ $field['id'] ] = $field;
    355 										}
    356 									}
    357 
    358 									if ( ! isset( $this->parent->options[ $field['id'] ] ) ) {
    359 										$this->parent->sections[ ( count( $this->parent->sections ) - 1 ) ]['fields'][] = $field;
    360 									}
    361 
    362 									if ( ! isset( $this->meta[ $this->post_id ][ $field['id'] ] ) ) {
    363 										$this->meta[ $this->post_id ][ $field['id'] ] = $this->options_defaults[ $field['id'] ];
    364 									}
    365 
    366 									// Only override if it exists, and it's not the default.
    367 									// phpcs:ignore Squiz.PHP.CommentedOutCode
    368 									// if ( isset( $this->meta[ $this->post_id ][ $field['id'] ] ) && isset( $field['default'] ) && $this->meta[ $this->post_id ][ $field['id'] ] === $field['default'] ) {
    369 									// unset($this->meta[$this->post_id][$field['id']]);
    370 									// } .
    371 								}
    372 							}
    373 						}
    374 					}
    375 				}
    376 			}
    377 
    378 			if ( isset( $run_hooks ) && true === $run_hooks ) {
    379 				$this->parent_options = '';
    380 
    381 				if ( ! empty( $this->to_replace ) ) {
    382 					foreach ( $this->to_replace as $id => $field ) {
    383 
    384 						// phpcs:ignore WordPress.NamingConventions.ValidHookName
    385 						add_filter( "redux/options/{$this->parent->args['opt_name']}/field/$id/register", array( $this, 'replace_field' ) );
    386 					}
    387 				}
    388 
    389 				add_filter( "redux/options/{$this->parent->args['opt_name']}/options", array( $this, 'override_options' ) );
    390 				add_filter( "redux/field/{$this->parent->args['opt_name']}/_can_output_css", array( $this, 'override_can_output_css' ) );
    391 				add_filter( "redux/field/{$this->parent->args['opt_name']}/output_css", array( $this, 'output_css' ) );
    392 			}
    393 		}
    394 
    395 		/**
    396 		 * Replace Field.
    397 		 *
    398 		 * @param array $field Field array.
    399 		 *
    400 		 * @return mixed
    401 		 */
    402 		public function replace_field( array $field ) {
    403 			if ( isset( $this->to_replace[ $field['id'] ] ) ) {
    404 				$field = $this->to_replace[ $field['id'] ];
    405 			}
    406 
    407 			return $field;
    408 		}
    409 
    410 		/**
    411 		 * Override CSS output.
    412 		 *
    413 		 * @param array $field Field array.
    414 		 *
    415 		 * @return array
    416 		 */
    417 		public function override_can_output_css( array $field ): array {
    418 			if ( isset( $this->output[ $field['id'] ] ) ) {
    419 				$field['force_output'] = true;
    420 			}
    421 
    422 			return $field;
    423 		}
    424 
    425 		/**
    426 		 * Output CSS.
    427 		 *
    428 		 * @param array $field Field array.
    429 		 *
    430 		 * @return array
    431 		 */
    432 		public function output_css( array $field ): array {
    433 			if ( isset( $this->output[ $field['id'] ] ) ) {
    434 				$field['output'] = $this->output[ $field['id'] ];
    435 			}
    436 
    437 			return $field;
    438 		}
    439 
    440 		/**
    441 		 * Make sure the defaults are the defaults
    442 		 *
    443 		 * @param array $options Options array.
    444 		 *
    445 		 * @return array
    446 		 */
    447 		public function override_options( array $options ): array {
    448 			$this->parent->_default_values();
    449 			$this->parent_defaults = $this->parent->options_defaults;
    450 
    451 			$meta = $this->get_meta( $this->post_id );
    452 			$data = wp_parse_args( $meta, $this->options_defaults );
    453 
    454 			foreach ( $data as $key => $value ) {
    455 				if ( isset( $meta[ $key ] ) && '' !== $meta[ $key ] ) {
    456 					$data[ $key ] = $meta[ $key ];
    457 					continue;
    458 				}
    459 
    460 				if ( isset( $options[ $key ] ) ) {
    461 					if ( isset( $options[ $key ] ) ) {
    462 						$data[ $key ] = $options[ $key ];
    463 					}
    464 				}
    465 			}
    466 
    467 			$this->parent->options_defaults = wp_parse_args( $this->options_defaults, $this->parent->options_defaults );
    468 
    469 			return wp_parse_args( $data, $options );
    470 		}
    471 
    472 		/**
    473 		 * Loop start.
    474 		 *
    475 		 * @param mixed $the_post WP_Post object.
    476 		 *
    477 		 * @return array|void
    478 		 */
    479 		public function loop_start( $the_post = array() ) {
    480 			if ( is_admin() ) {
    481 				return $the_post;
    482 			}
    483 
    484 			if ( isset( $the_post ) && is_array( $the_post ) ) {
    485 				global $post;
    486 				$the_post = $post;
    487 			}
    488 
    489 			if ( isset( $GLOBALS[ $this->parent->args['global_variable'] . '-loop' ] ) ) {
    490 				$GLOBALS[ $this->parent->args['global_variable'] ] = $GLOBALS[ $this->parent->args['global_variable'] . '-loop' ];
    491 				unset( $GLOBALS[ $this->parent->args['global_variable'] . '-loop' ] );
    492 			}
    493 
    494 			// Override these values if they differ from the admin panel defaults.  ;) .
    495 			if ( in_array( $the_post->post_type, $this->post_types, true ) ) {
    496 				$meta = $this->get_meta( $the_post->ID );
    497 				if ( empty( $meta ) ) {
    498 					return;
    499 				}
    500 
    501 				// Backup the args.
    502 				$GLOBALS[ $this->parent->args['global_variable'] . '-loop' ] = $GLOBALS[ $this->parent->args['global_variable'] ];
    503 				$GLOBALS[ $this->parent->args['global_variable'] ]           = wp_parse_args( $meta, $GLOBALS[ $this->parent->args['global_variable'] . '-loop' ] );
    504 			}
    505 		}
    506 
    507 		/**
    508 		 * Loop end.
    509 		 */
    510 		public function loop_end() {
    511 			if ( isset( $GLOBALS[ $this->parent->args['global_variable'] . '-loop' ] ) ) {
    512 				$GLOBALS[ $this->parent->args['global_variable'] ] = $GLOBALS[ $this->parent->args['global_variable'] . '-loop' ];
    513 
    514 				unset( $GLOBALS[ $this->parent->args['global_variable'] . '-loop' ] );
    515 			}
    516 		}
    517 
    518 		/**
    519 		 * Enqueue fields.
    520 		 */
    521 		public function enqueue() {
    522 			global $pagenow;
    523 
    524 			$types = array();
    525 
    526 			// Enqueue CSS.
    527 			foreach ( $this->boxes as $key => $box ) {
    528 				if ( empty( $box['sections'] ) ) {
    529 					continue;
    530 				}
    531 
    532 				if ( isset( $box['post_types'] ) ) {
    533 					$types = array_merge( $box['post_types'], $types );
    534 				}
    535 
    536 				if ( isset( $box['post_types'] ) && ! empty( $box['post_types'] ) ) {
    537 					if ( ! is_array( $box['post_types'] ) ) {
    538 						$box['post_types']                 = array( $box['post_types'] );
    539 						$this->boxes[ $key ]['post_types'] = $box['post_types'];
    540 					}
    541 				}
    542 			}
    543 
    544 			if ( 'post-new.php' === $pagenow || 'post.php' === $pagenow ) {
    545 				global $post;
    546 
    547 				if ( in_array( $post->post_type, $types, true ) ) {
    548 					$this->parent->transients       = get_transient( $this->parent->args['opt_name'] . '-transients-metaboxes' );
    549 					$this->parent->transients_check = $this->parent->transients;
    550 
    551 					if ( isset( $this->parent->transients['notices'] ) ) {
    552 						$this->notices                              = $this->parent->transients['notices'];
    553 						$this->parent->transients['last_save_mode'] = 'metaboxes';
    554 					}
    555 
    556 					delete_transient( $this->parent->args['opt_name'] . '-transients-metaboxes' );
    557 					$this->parent->_enqueue();
    558 
    559 					// phpcs:ignore WordPress.NamingConventions.ValidHookName
    560 					do_action( "redux/metaboxes/{$this->parent->args['opt_name']}/enqueue" );
    561 
    562 					// phpcs:ignore WordPress.NamingConventions.ValidHookName
    563 					do_action( "redux/{$this->parent->args['opt_name']}/extensions/metaboxes/enqueue" );
    564 
    565 					/**
    566 					 * Redux metaboxes CSS
    567 					 * filter 'redux/page/{opt_name}/enqueue/redux-extension-metaboxes-css'
    568 					 *
    569 					 * @param string  bundled stylesheet src
    570 					 */
    571 					wp_enqueue_style(
    572 						'redux-extension-metaboxes-js',
    573 						apply_filters( "redux/metaboxes/{$this->parent->args['opt_name']}/enqueue/redux-extension-metaboxes-css", $this->extension_url . 'redux-extension-metaboxes.css' ), // phpcs:ignore: WordPress.NamingConventions.ValidHookName
    574 						array(),
    575 						self::$version
    576 					);
    577 
    578 					/**
    579 					 * Redux metaboxes JS
    580 					 * filter 'redux/page/{opt_name}/enqueue/redux-extension-metaboxes-js
    581 					 *
    582 					 * @param string  bundled javscript
    583 					 */
    584 					wp_enqueue_script(
    585 						'redux-extension-metaboxes-js',
    586 						apply_filters( "redux/metaboxes/{$this->parent->args['opt_name']}/enqueue/redux-extension-metaboxes-js", $this->extension_url . 'redux-extension-metaboxes' . Redux_Functions::isMin() . '.js' ), // phpcs:ignore: WordPress.NamingConventions.ValidHookName
    587 						array( 'jquery', 'redux-js' ),
    588 						self::$version,
    589 						true
    590 					);
    591 
    592 					// Values used by the javascript.
    593 					wp_localize_script( 'redux-extension-metaboxes-js', 'reduxMetaboxes', $this->wp_links );
    594 				}
    595 			}
    596 		}
    597 
    598 		/* Post URLs to IDs function, supports custom post types - borrowed and modified from url_to_postid() in wp-includes/rewrite.php */
    599 
    600 		// Taken from http://betterwp.net/wordpress-tips/url_to_postid-for-custom-post-types/
    601 		// Customized to work with non-rewrite URLs
    602 		// Modified by Dovy Paukstys (@dovy) of Redux Framework.
    603 
    604 		/**
    605 		 * URL to PostID.
    606 		 *
    607 		 * @param string $url URL.
    608 		 *
    609 		 * @return int|mixed|void
    610 		 */
    611 		private function url_to_postid( string $url ) {
    612 			global $wp_rewrite, $pagenow;
    613 
    614 			if ( ! empty( $this->post_id ) ) {
    615 				return $this->post_id;
    616 			}
    617 
    618 			if ( isset( $_GET['post'] ) ) {  // phpcs:ignore WordPress.Security.NonceVerification
    619 				$post = (int) sanitize_text_field( wp_unslash( $_GET['post'] ) );  // phpcs:ignore WordPress.Security.NonceVerification
    620 
    621 				if ( ! empty( $post ) && is_numeric( $post ) ) {
    622 					return $post;
    623 				}
    624 			}
    625 
    626 			if ( 'post-new.php' === $pagenow || 'wp-login.php' === $pagenow ) {
    627 				return;
    628 			}
    629 
    630 			if ( is_admin() && 'post.php' === $pagenow && isset( $_POST['post_ID'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    631 				$post_id = sanitize_text_field( wp_unslash( $_POST['post_ID'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
    632 
    633 				if ( ! empty( $post_id ) && is_numeric( $post_id ) ) {
    634 					return $post_id;
    635 				}
    636 			}
    637 
    638 			$post_id = url_to_postid( $url );
    639 
    640 			if ( isset( $post_id ) && '' !== (string) $post_id && 0 !== $post_id ) {
    641 				return $post_id;
    642 			}
    643 
    644 			// First, check to see if there is a 'p=N' or 'page_id=N' to match against.
    645 			if ( preg_match( '#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values ) ) {
    646 				$id = absint( $values[2] );
    647 				if ( $id ) {
    648 					return $id;
    649 				}
    650 			}
    651 
    652 			// Check to see if we are using rewrite rules.
    653 			if ( isset( $wp_rewrite ) ) {
    654 				$rewrite = $wp_rewrite->wp_rewrite_rules();
    655 			}
    656 
    657 			// Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options.
    658 			if ( empty( $rewrite ) ) {
    659 				if ( isset( $_GET ) && ! empty( $_GET ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    660 
    661 					/************************************************************************
    662 					 * ADDED: Trys checks URL for ?posttype=postname
    663 					 ************************************************************************ */
    664 
    665 					// Assign $url to $temp_url just incase. :) .
    666 					$temp_url = $url;
    667 
    668 					// Get rid of the #anchor.
    669 					$url_split = explode( '#', $temp_url );
    670 					$temp_url  = $url_split[0];
    671 
    672 					// Get rid of URL ?query=string.
    673 					$url_query = explode( '&', $temp_url );
    674 					$temp_url  = $url_query[0];
    675 
    676 					// Get rid of ? mark.
    677 					$url_query = explode( '?', $temp_url );
    678 
    679 					if ( isset( $url_query[1] ) && ! empty( $url_query[1] ) && strpos( $url_query[1], '=' ) ) {
    680 						$url_query = explode( '=', $url_query[1] );
    681 
    682 						if ( isset( $url_query[0] ) && isset( $url_query[1] ) ) {
    683 							$args = array(
    684 								'name'      => $url_query[1],
    685 								'post_type' => $url_query[0],
    686 								'showposts' => 1,
    687 							);
    688 
    689 							if ( get_posts( $args ) === $post ) {
    690 								return $post[0]->ID;
    691 							}
    692 						}
    693 					}
    694 
    695 					foreach ( $GLOBALS['wp_post_types'] as $key => $value ) {
    696 						if ( isset( $_GET[ $key ] ) && ! empty( $_GET[ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    697 							$args = array(
    698 								'name'      => sanitize_text_field( wp_unslash( $_GET[ $key ] ) ), // phpcs:ignore WordPress.Security.NonceVerification
    699 								'post_type' => $key,
    700 								'showposts' => 1,
    701 							);
    702 
    703 							if ( get_posts( $args ) === $post ) {
    704 								return $post[0]->ID;
    705 							}
    706 						}
    707 					}
    708 				}
    709 			}
    710 
    711 			// Get rid of the #anchor.
    712 			$url_split = explode( '#', $url );
    713 			$url       = $url_split[0];
    714 
    715 			// Get rid of URL ?query=string.
    716 			$url_query = explode( '?', $url );
    717 			$url       = $url_query[0];
    718 
    719 			// Set the correct URL scheme.
    720 			$scheme = wp_parse_url( home_url(), PHP_URL_SCHEME );
    721 			$url    = set_url_scheme( $url, $scheme );
    722 
    723 			// Add 'www.' if it is absent and should be there.
    724 			if ( false !== strpos( home_url(), '://www.' ) && false === strpos( $url, '://www.' ) ) {
    725 				$url = str_replace( '://', '://www.', $url );
    726 			}
    727 
    728 			// Strip 'www.' if it is present and shouldn't be.
    729 			if ( false === strpos( home_url(), '://www.' ) ) {
    730 				$url = str_replace( '://www.', '://', $url );
    731 			}
    732 
    733 			// Strip 'index.php/' if we're not using path info permalinks.
    734 			if ( isset( $wp_rewrite ) && ! $wp_rewrite->using_index_permalinks() ) {
    735 				$url = str_replace( 'index.php/', '', $url );
    736 			}
    737 
    738 			if ( false !== strpos( $url, home_url() ) ) {
    739 				// Chop off http://domain.com.
    740 				$url = str_replace( home_url(), '', $url );
    741 			} else {
    742 				// Chop off /path/to/blog.
    743 				$home_path = wp_parse_url( home_url() );
    744 				$home_path = $home_path['path'] ?? '';
    745 				$url       = str_replace( $home_path, '', $url );
    746 			}
    747 
    748 			// Trim leading and lagging slashes.
    749 			$url = trim( $url, '/' );
    750 
    751 			$request = $url;
    752 
    753 			if ( empty( $request ) && ( ! isset( $_GET ) || empty( $_GET ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    754 				return get_option( 'page_on_front' );
    755 			}
    756 
    757 			// Look for matches.
    758 			$request_match = $request;
    759 
    760 			foreach ( (array) $rewrite as $match => $query ) {
    761 				// If the requesting file is the anchor of the match, prepend it
    762 				// to the path info.
    763 				if ( ! empty( $url ) && ( $url !== $request ) && ( strpos( $match, $url ) === 0 ) ) {
    764 					$request_match = $url . '/' . $request;
    765 				}
    766 
    767 				if ( preg_match( "!^$match!", $request_match, $matches ) ) {
    768 					global $wp;
    769 
    770 					// Got a match.
    771 					// Trim the query of everything up to the '?'.
    772 					$query = preg_replace( '!^.+\?!', '', $query );
    773 
    774 					// Substitute the substring matches into the query.
    775 					$query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
    776 
    777 					// Filter out non-public query vars.
    778 					parse_str( $query, $query_vars );
    779 
    780 					$query = array();
    781 
    782 					foreach ( (array) $query_vars as $key => $value ) {
    783 						if ( in_array( $key, $wp->public_query_vars, true ) ) {
    784 							$query[ $key ] = $value;
    785 						}
    786 					}
    787 
    788 					/************************************************************************
    789 					 * ADDED: $GLOBALS['wp_post_types'] doesn't seem to have custom postypes
    790 					 * Trying below to find posttypes in $rewrite rules
    791 					 ************************************************************************ */
    792 
    793 					// PostType Array.
    794 					$custom_post_type = false;
    795 					$post_types       = array();
    796 
    797 					foreach ( $rewrite as $key => $value ) {
    798 						if ( preg_match( '/post_type=([^&]+)/i', $value, $matched ) ) {
    799 							if ( isset( $matched[1] ) && ! in_array( $matched[1], $post_types, true ) ) {
    800 								$post_types[] = $matched[1];
    801 							}
    802 						}
    803 					}
    804 
    805 					foreach ( (array) $query_vars as $key => $value ) {
    806 						if ( in_array( $key, $post_types, true ) ) {
    807 							$custom_post_type = true;
    808 
    809 							$query['post_type'] = $key;
    810 							$query['postname']  = $value;
    811 						}
    812 					}
    813 
    814 					/************************************************************************
    815 					 * END ADD
    816 					 ************************************************************************ */
    817 
    818 					// Taken from class-wp.php.
    819 					foreach ( $GLOBALS['wp_post_types'] as $post_type => $t ) {
    820 						if ( isset( $t->query_var ) ) {
    821 							$post_type_query_vars[ $t->query_var ] = $post_type;
    822 						}
    823 					}
    824 
    825 					foreach ( $wp->public_query_vars as $wpvar ) {
    826 						if ( isset( $wp->extra_query_vars[ $wpvar ] ) ) {
    827 							$query[ $wpvar ] = $wp->extra_query_vars[ $wpvar ];
    828 						} elseif ( isset( $_POST[ $wpvar ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    829 							$query[ $wpvar ] = sanitize_text_field( wp_unslash( $_POST[ $wpvar ] ) ); // phpcs:ignore WordPress.Security.NonceVerification
    830 						} elseif ( isset( $_GET[ $wpvar ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
    831 							$query[ $wpvar ] = sanitize_text_field( wp_unslash( $_GET[ $wpvar ] ) ); // phpcs:ignore WordPress.Security.NonceVerification
    832 						} elseif ( isset( $query_vars[ $wpvar ] ) ) {
    833 							$query[ $wpvar ] = $query_vars[ $wpvar ];
    834 						}
    835 
    836 						if ( ! empty( $query[ $wpvar ] ) ) {
    837 							if ( ! is_array( $query[ $wpvar ] ) ) {
    838 								$query[ $wpvar ] = (string) $query[ $wpvar ];
    839 							} else {
    840 								foreach ( $query[ $wpvar ] as $vkey => $v ) {
    841 									if ( ! is_object( $v ) ) {
    842 										$query[ $wpvar ][ $vkey ] = (string) $v;
    843 									}
    844 								}
    845 							}
    846 
    847 							if ( isset( $post_type_query_vars[ $wpvar ] ) ) {
    848 								$query['post_type'] = $post_type_query_vars[ $wpvar ];
    849 								$query['name']      = $query[ $wpvar ];
    850 							}
    851 						}
    852 					}
    853 
    854 					// Do the query.
    855 					if ( isset( $query['pagename'] ) && ! empty( $query['pagename'] ) ) {
    856 						$args = array(
    857 							'name'      => $query['pagename'],
    858 							'post_type' => 'page',
    859 							'showposts' => 1,
    860 						);
    861 
    862 						if ( get_posts( $args ) === $post ) {
    863 							return $post[0]->ID;
    864 						}
    865 					}
    866 
    867 					if ( ( ! isset( $query['page'] ) || empty( $query['page'] ) ) && ( ! isset( $query['pagename'] ) || empty( $query['pagename'] ) ) ) {
    868 						return 0;
    869 					}
    870 
    871 					$query = new WP_Query( $query );
    872 
    873 					if ( ! empty( $query->posts ) && $query->is_singular ) {
    874 						return $query->post->ID;
    875 					} else {
    876 
    877 						// WooCommerce override.
    878 						if ( isset( $query->query['post_type'] ) && 'product' === $query->query['post_type'] && class_exists( 'WooCommerce' ) ) {
    879 							return get_option( 'woocommerce_shop_page_id' );
    880 						}
    881 
    882 						return 0;
    883 					}
    884 				}
    885 			}
    886 
    887 			return 0;
    888 		}
    889 
    890 		/**
    891 		 * Get default values.
    892 		 */
    893 		public function default_values() {
    894 			if ( ! empty( $this->boxes ) && empty( $this->options_defaults ) ) {
    895 				foreach ( $this->boxes as $key => $box ) {
    896 					if ( empty( $box['sections'] ) ) {
    897 						continue;
    898 					}
    899 
    900 					// fill the cache.
    901 					foreach ( $box['sections'] as $sk => $section ) {
    902 						if ( ! isset( $section['id'] ) ) {
    903 							if ( ! is_numeric( $sk ) || ! isset( $section['title'] ) ) {
    904 								$section['id'] = $sk;
    905 							} else {
    906 								$section['id'] = sanitize_text_field( $section['title'], $sk );
    907 							}
    908 							$this->boxes[ $key ]['sections'][ $sk ] = $section;
    909 						}
    910 						if ( isset( $section['fields'] ) ) {
    911 							foreach ( $section['fields'] as $k => $field ) {
    912 
    913 								if ( empty( $field['id'] ) && empty( $field['type'] ) ) {
    914 									continue;
    915 								}
    916 
    917 								if ( in_array( $field['type'], array( 'ace_editor' ), true ) && isset( $field['options'] ) ) {
    918 									$this->boxes[ $key ]['sections'][ $sk ]['fields'][ $k ]['args'] = $field['options'];
    919 									unset( $this->boxes[ $key ]['sections'][ $sk ]['fields'][ $k ]['options'] );
    920 								}
    921 
    922 								if ( 'section' === $field['type'] && isset( $field['indent'] ) && ( true === $field['indent'] || 'true' === $field['indent'] ) ) {
    923 									$field['class']  = $field['class'] ?? '';
    924 									$field['class'] .= 'redux-section-indent-start';
    925 
    926 									$this->boxes[ $key ]['sections'][ $sk ]['fields'][ $k ] = $field;
    927 								}
    928 
    929 								$this->parent->field_default_values( $field );
    930 							}
    931 						}
    932 					}
    933 				}
    934 			}
    935 
    936 			if ( empty( $this->meta[ $this->post_id ] ) ) {
    937 				$this->meta[ $this->post_id ] = $this->get_meta( $this->post_id );
    938 			}
    939 		}
    940 
    941 		/**
    942 		 * Add Meta Boxes
    943 		 */
    944 		public function add() {
    945 			if ( empty( $this->boxes ) || ! is_array( $this->boxes ) ) {
    946 				return;
    947 			}
    948 
    949 			foreach ( $this->boxes as $key => $box ) {
    950 				if ( empty( $box['sections'] ) ) {
    951 					continue;
    952 				}
    953 
    954 				// Save users from themselves.
    955 				if ( isset( $box['position'] ) && ! in_array( strtolower( $box['position'] ), array( 'normal', 'advanced', 'side' ), true ) ) {
    956 					unset( $box['position'] );
    957 				}
    958 
    959 				if ( isset( $box['priority'] ) && ! in_array( strtolower( $box['priority'] ), array( 'high', 'core', 'default', 'low' ), true ) ) {
    960 					unset( $box['priority'] );
    961 				}
    962 
    963 				$defaults = array(
    964 					'id'         => $key . '-' . $this->parent->args['opt_name'],
    965 					'post_types' => array( 'page', 'post' ),
    966 					'position'   => 'normal',
    967 					'priority'   => 'high',
    968 				);
    969 
    970 				$box = wp_parse_args( $box, $defaults );
    971 				if ( isset( $box['post_types'] ) && ! empty( $box['post_types'] ) ) {
    972 					foreach ( $box['post_types'] as $posttype ) {
    973 						if ( isset( $box['title'] ) ) {
    974 							$title = $box['title'];
    975 						} else {
    976 							if ( isset( $box['sections'] ) && 1 === count( $box['sections'] ) && isset( $box['sections'][0]['fields'] ) && 1 === count( $box['sections'][0]['fields'] ) && isset( $box['sections'][0]['fields'][0]['title'] ) ) {
    977 
    978 								// If only one field in this box.
    979 								$title = $box['sections'][0]['fields'][0]['title'];
    980 							} else {
    981 								$title = ucfirst( $posttype ) . ' ' . __( 'Options', 'redux-framework' );
    982 							}
    983 						}
    984 
    985 						$args = array(
    986 							'position' => $box['position'],
    987 							'sections' => $box['sections'],
    988 							'key'      => $key,
    989 						);
    990 
    991 						// Override the parent args on a metabox level.
    992 						if ( ! isset( $this->orig_args ) || empty( $this->orig_args ) ) {
    993 							$this->orig_args = $this->parent->args;
    994 						}
    995 
    996 						if ( isset( $box['args'] ) ) {
    997 							$this->parent->args = wp_parse_args( $box['args'], $this->orig_args );
    998 						} elseif ( $this->parent->args !== $this->orig_args ) {
    999 							$this->parent->args = $this->orig_args;
   1000 						}
   1001 
   1002 						add_filter( 'postbox_classes_' . $posttype . '_redux-' . $this->parent->args['opt_name'] . '-metabox-' . $box['id'], array( $this, 'add_box_classes' ) );
   1003 
   1004 						// phpcs:ignore WordPress.NamingConventions.ValidHookName
   1005 						do_action( 'redux/' . $this->parent->args['opt_name'] . '/extensions/metabox/add', $this, $box, $posttype );
   1006 
   1007 						if ( isset( $box['post_format'] ) ) {
   1008 							add_filter( 'postbox_classes_' . $posttype . '_redux-' . $this->parent->args['opt_name'] . '-metabox-' . $box['id'], array( $this, 'add_box_hide_class' ) );
   1009 						}
   1010 
   1011 						// phpcs:ignore Generic.Strings.UnnecessaryStringConcat
   1012 						call_user_func( 'add' . '_meta' . '_box', 'redux-' . $this->parent->args['opt_name'] . '-metabox-' . $box['id'], $title, array( $this, 'generate_boxes' ), $posttype, $box['position'], $box['priority'], $args );
   1013 					}
   1014 				}
   1015 			}
   1016 		}
   1017 
   1018 		/**
   1019 		 * Add hidden class to metabox DIV.
   1020 		 *
   1021 		 * @param array $classes Class array.
   1022 		 *
   1023 		 * @return array
   1024 		 */
   1025 		public function add_box_hide_class( array $classes ): array {
   1026 			$classes[] = 'hide';
   1027 
   1028 			return $classes;
   1029 		}
   1030 
   1031 		/**
   1032 		 * Field Defaults.
   1033 		 *
   1034 		 * @param mixed $field_id ID.
   1035 		 *
   1036 		 * @return mixed|string
   1037 		 */
   1038 		private function field_default( $field_id ) {
   1039 			if ( ! isset( $this->parent->options_defaults ) ) {
   1040 				$this->parent->options_defaults = $this->parent->_default_values();
   1041 			}
   1042 
   1043 			if ( ! isset( $this->parent->options ) || empty( $this->parent->options ) ) {
   1044 				$this->parent->get_options();
   1045 			}
   1046 
   1047 			$this->options = $this->parent->options;
   1048 
   1049 			if ( isset( $this->parent->options[ $field_id['id'] ] ) && isset( $this->parent->options_defaults[ $field_id['id'] ] ) && $this->parent->options[ $field_id['id'] ] !== $this->parent->options_defaults[ $field_id['id'] ] ) {
   1050 				return $this->parent->options[ $field_id['id'] ];
   1051 			} else {
   1052 				if ( empty( $this->options_defaults ) ) {
   1053 					$this->default_values(); // fill cache.
   1054 				}
   1055 
   1056 				$data = '';
   1057 				if ( ! empty( $this->options_defaults ) ) {
   1058 					$data = $this->options_defaults[ $field_id['id'] ] ?? '';
   1059 				}
   1060 
   1061 				if ( empty( $data ) && isset( $this->parent->options_defaults[ $field_id['id'] ] ) ) {
   1062 					$data = $this->parent->options_defaults[ $field_id['id'] ] ?? '';
   1063 				}
   1064 
   1065 				return $data;
   1066 			}
   1067 
   1068 		}
   1069 
   1070 		/**
   1071 		 * Function to get and cache the post meta.
   1072 		 *
   1073 		 * @param mixed $id ID.
   1074 		 *
   1075 		 * @return array
   1076 		 */
   1077 		private function get_meta( $id ): array {
   1078 			if ( ! isset( $this->meta[ $id ] ) ) {
   1079 				$this->meta[ $id ] = array();
   1080 				$o_data            = get_post_meta( $id );
   1081 
   1082 				// phpcs:ignore WordPress.NamingConventions.ValidHookName
   1083 				$o_data = apply_filters( "redux/metaboxes/{$this->parent->args['opt_name']}/get_meta", $o_data );
   1084 
   1085 				if ( ! empty( $o_data ) ) {
   1086 					foreach ( $o_data as $key => $value ) {
   1087 						if ( 1 === count( $value ) ) {
   1088 							$this->meta[ $id ][ $key ] = maybe_unserialize( $value[0] );
   1089 						} else {
   1090 							$new_value = array_map( 'maybe_unserialize', $value );
   1091 
   1092 							if ( is_array( $new_value ) ) {
   1093 								$this->meta[ $id ][ $key ] = $new_value[0];
   1094 							} else {
   1095 								$this->meta[ $id ][ $key ] = $new_value;
   1096 							}
   1097 						}
   1098 					}
   1099 				}
   1100 
   1101 				if ( isset( $this->meta[ $id ][ $this->parent->args['opt_name'] ] ) ) {
   1102 					$data = maybe_unserialize( $this->meta[ $id ][ $this->parent->args['opt_name'] ] );
   1103 
   1104 					foreach ( $data as $key => $value ) {
   1105 						$this->meta[ $id ][ $key ] = $value;
   1106 						update_post_meta( $id, $key, $value );
   1107 					}
   1108 
   1109 					unset( $this->meta[ $id ][ $this->parent->args['opt_name'] ] );
   1110 
   1111 					delete_post_meta( $id, $this->parent->args['opt_name'] );
   1112 				}
   1113 			}
   1114 
   1115 			return $this->meta[ $id ];
   1116 		}
   1117 
   1118 		/**
   1119 		 * Get values.
   1120 		 *
   1121 		 * @param mixed  $the_post Post oject/id.
   1122 		 * @param string $meta_key Meta key.
   1123 		 * @param mixed  $def_val  Def value.
   1124 		 *
   1125 		 * @return array|mixed|string
   1126 		 */
   1127 		public function get_values( $the_post, string $meta_key = '', $def_val = '' ) {
   1128 
   1129 			// Override these values if they differ from the admin panel defaults.  ;) .
   1130 			if ( isset( $the_post->post_type ) && in_array( $the_post->post_type, $this->post_types, true ) ) {
   1131 				if ( isset( $this->post_type_values[ $the_post->post_type ] ) ) {
   1132 					$meta = $this->post_type_fields[ $the_post->post_type ];
   1133 				} else {
   1134 					$defaults = array();
   1135 					if ( ! empty( $this->post_type_fields[ $the_post->post_type ] ) ) {
   1136 						foreach ( $this->post_type_fields[ $the_post->post_type ] as $key => $null ) {
   1137 							if ( isset( $this->options_defaults[ $key ] ) ) {
   1138 								$defaults[ $key ] = $this->options_defaults[ $key ];
   1139 							}
   1140 						}
   1141 					}
   1142 
   1143 					$meta = wp_parse_args( $this->get_meta( $the_post->ID ), $defaults );
   1144 
   1145 					$this->post_type_fields[ $the_post->post_type ] = $meta;
   1146 				}
   1147 
   1148 				if ( ! empty( $meta_key ) ) {
   1149 					if ( ! isset( $meta[ $meta_key ] ) ) {
   1150 						$meta[ $meta_key ] = $def_val;
   1151 					}
   1152 
   1153 					return $meta[ $meta_key ];
   1154 				} else {
   1155 					return $meta;
   1156 				}
   1157 			}
   1158 
   1159 			return $def_val;
   1160 		}
   1161 
   1162 		/**
   1163 		 * Generate Boxes.
   1164 		 *
   1165 		 * @param mixed $post    ID.
   1166 		 * @param array $metabox Metabox array.
   1167 		 */
   1168 		public function generate_boxes( $post, array $metabox ) {
   1169 			global $wpdb;
   1170 
   1171 			if ( isset( $metabox['args']['permissions'] ) && ! empty( $metabox['args']['permissions'] ) && ! Redux_Helpers::current_user_can( $metabox['args']['permissions'] ) ) {
   1172 				return;
   1173 			}
   1174 
   1175 			$sections = $metabox['args']['sections'];
   1176 
   1177 			wp_nonce_field( 'redux_metaboxes_meta_nonce', 'redux_metaboxes_meta_nonce' );
   1178 
   1179 			wp_dequeue_script( 'json-view-js' );
   1180 
   1181 			$sidebar = true;
   1182 
   1183 			if ( 'side' === $metabox['args']['position'] || 1 === count( $sections ) || ( isset( $metabox['args']['sidebar'] ) && false === $metabox['args']['sidebar'] ) ) {
   1184 				$sidebar = false; // Show the section dividers or not.
   1185 			}
   1186 
   1187 			?>
   1188 			<input
   1189 					type="hidden"
   1190 					id="currentSection"
   1191 					name="<?php echo esc_attr( $this->parent->args['opt_name'] ); ?>[redux-metabox-section]" value=""/>
   1192 			<div
   1193 					data-index="<?php echo esc_attr( $metabox['args']['key'] ); ?>"
   1194 					data-opt-name="<?php echo esc_attr( $this->parent->args['opt_name'] ); ?>"
   1195 					class="redux-container<?php echo esc_attr( ( $sidebar ) ? ' redux-has-sections' : ' redux-no-sections' ); ?> redux-box-<?php echo esc_attr( $metabox['args']['position'] ); ?>">
   1196 				<div class="redux-notices">
   1197 					<?php if ( 'side' !== $metabox['args']['position'] || ( isset( $metabox['args']['sidebar'] ) && false !== $metabox['args']['sidebar'] ) ) { ?>
   1198 						<div class="saved_notice admin-notice notice-blue" style="display:none;">
   1199 							<?php // phpcs:ignore WordPress.NamingConventions.ValidHookName ?>
   1200 							<strong><?php echo esc_html( apply_filters( "redux-imported-text-{$this->parent->args['opt_name']}", esc_html__( 'Settings Imported!', 'redux-framework' ) ) ); ?></strong>
   1201 						</div>
   1202 						<div class="redux-save-warn notice-yellow">
   1203 							<?php // phpcs:ignore WordPress.NamingConventions.ValidHookName ?>
   1204 							<strong><?php echo esc_html( apply_filters( "redux-changed-text-{$this->parent->args['opt_name']}", esc_html__( 'Settings have changed, you should save them!', 'redux-framework' ) ) ); ?></strong>
   1205 						</div>
   1206 					<?php } ?>
   1207 					<div class="redux-field-errors notice-red">
   1208 						<strong> <span></span> <?php echo esc_html__( 'error(s) were found!', 'redux-framework' ); ?>
   1209 						</strong>
   1210 					</div>
   1211 					<div class="redux-field-warnings notice-yellow">
   1212 						<strong> <span></span> <?php echo esc_html__( 'warning(s) were found!', 'redux-framework' ); ?>
   1213 						</strong>
   1214 					</div>
   1215 				</div>
   1216 				<?php
   1217 				echo '<a href="javascript:void(0);" class="expand_options hide" style="display:none;">' . esc_html__( 'Expand', 'redux-framework' ) . '</a>';
   1218 				if ( $sidebar ) {
   1219 					?>
   1220 					<div class="redux-sidebar">
   1221 						<ul class="redux-group-menu">
   1222 							<?php
   1223 
   1224 							foreach ( $sections as $s_key => $section ) {
   1225 								if ( isset( $section['permissions'] ) && ! empty( $section['permissions'] ) && ! Redux_Helpers::current_user_can( $section['permissions'] ) ) {
   1226 									continue;
   1227 								}
   1228 
   1229 								echo $this->parent->section_menu( $s_key, $section, '_box_' . $metabox['id'], $sections ); // phpcs:ignore WordPress.Security.EscapeOutput
   1230 							}
   1231 							?>
   1232 						</ul>
   1233 					</div>
   1234 				<?php } ?>
   1235 
   1236 				<div class="redux-main">
   1237 					<?php
   1238 					$update_localize = false;
   1239 
   1240 					foreach ( $sections as $s_key => $section ) {
   1241 						if ( isset( $section['permissions'] ) && ! empty( $section['permissions'] ) && ! Redux_Helpers::current_user_can( $section['permissions'] ) ) {
   1242 							continue;
   1243 						}
   1244 						if ( isset( $section['fields'] ) && ! empty( $section['fields'] ) ) {
   1245 							if ( isset( $section['args'] ) ) {
   1246 								$this->parent->args = wp_parse_args( $section['args'], $this->orig_args );
   1247 							} elseif ( $this->parent->args !== $this->orig_args ) {
   1248 								$this->parent->args = $this->orig_args;
   1249 							}
   1250 
   1251 							$hide             = $sidebar ? '' : ' display-group';
   1252 							$section['class'] = isset( $section['class'] ) ? ' ' . $section['class'] : '';
   1253 							echo '<div id="' . esc_attr( $s_key ) . '_box_' . esc_attr( $metabox['id'] ) . '_section_group" class="redux-group-tab' . esc_attr( $section['class'] ) . ' redux_metabox_panel' . esc_attr( $hide ) . '">';
   1254 
   1255 							if ( isset( $section['title'] ) && ! empty( $section['title'] ) ) {
   1256 								echo '<h3 class="redux-section-title">' . wp_kses_post( $section['title'] ) . '</h3>';
   1257 							}
   1258 
   1259 							if ( isset( $section['desc'] ) && ! empty( $section['desc'] ) ) {
   1260 								echo '<div class="redux-section-desc">' . wp_kses_post( $section['desc'] ) . '</div>';
   1261 							}
   1262 
   1263 							echo '<table class="form-table"><tbody>';
   1264 							foreach ( $section['fields'] as $f_key => $field ) {
   1265 								if ( isset( $field['permissions'] ) && ! empty( $field['permissions'] ) && ! Redux_Helpers::current_user_can( $field['permissions'] ) ) {
   1266 									continue;
   1267 								}
   1268 								$field['name'] = $this->parent->args['opt_name'] . '[' . $field['id'] . ']';
   1269 
   1270 								$is_hidden = false;
   1271 								$ex_style  = '';
   1272 								if ( isset( $field['hidden'] ) && $field['hidden'] ) {
   1273 									$is_hidden = true;
   1274 									$ex_style  = ' style="border-bottom: none;"';
   1275 								}
   1276 
   1277 								echo '<tr valign="top"' . $ex_style . '>'; // phpcs:ignore WordPress.Security.EscapeOutput
   1278 
   1279 								$th = $this->parent->get_header_html( $field );
   1280 
   1281 								if ( $is_hidden ) {
   1282 									$str_pos = strpos( $th, 'redux_field_th' );
   1283 
   1284 									if ( $str_pos > - 1 ) {
   1285 										$th = str_replace( 'redux_field_th', 'redux_field_th hide', $th );
   1286 									}
   1287 								}
   1288 
   1289 								if ( $sidebar ) {
   1290 									if ( ! ( isset( $metabox['args']['sections'] ) && 1 === count( $metabox['args']['sections'] ) && isset( $metabox['args']['sections'][0]['fields'] ) && 1 === count( $metabox['args']['sections'][0]['fields'] ) ) && isset( $field['title'] ) ) {
   1291 										echo '<th scope="row">';
   1292 										if ( ! empty( $th ) ) {
   1293 											echo $th; // phpcs:ignore WordPress.Security.EscapeOutput
   1294 										}
   1295 										echo '</th>';
   1296 										echo '<td>';
   1297 									}
   1298 								} else {
   1299 									echo '<td>' . $th . ''; // phpcs:ignore WordPress.Security.EscapeOutput
   1300 								}
   1301 
   1302 								if ( 'section' === $field['type'] && ( 'true' === $field['indent'] || true === $field['indent'] ) ) {
   1303 									$field['class']  = $field['class'] ?? '';
   1304 									$field['class'] .= 'redux-section-indent-start';
   1305 								}
   1306 
   1307 								if ( ! isset( $this->meta[ $this->post_id ][ $field['id'] ] ) ) {
   1308 									$this->meta[ $this->post_id ][ $field['id'] ] = '';
   1309 								}
   1310 
   1311 								$this->parent->render_class->field_input( $field, $this->meta[ $this->post_id ][ $field['id'] ], true );
   1312 								echo '</td></tr>';
   1313 							}
   1314 							echo '</tbody></table>';
   1315 							echo '</div>';
   1316 						}
   1317 					}
   1318 					?>
   1319 				</div>
   1320 				<div class="clear"></div>
   1321 			</div>
   1322 			<?php
   1323 		}
   1324 
   1325 		/**
   1326 		 * Save meta boxes
   1327 		 * Runs when a post is saved and does an action which the write panel save scripts can hook into.
   1328 		 *
   1329 		 * @access public
   1330 		 *
   1331 		 * @param mixed $post_id Post ID.
   1332 		 * @param mixed $post Post.
   1333 		 *
   1334 		 * @return mixed
   1335 		 */
   1336 		public function meta_boxes_save( $post_id, $post ) {
   1337 			if ( isset( $_POST['vc_inline'] ) && sanitize_text_field( wp_unslash( $_POST['vc_inline'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
   1338 				return $post_id;
   1339 			}
   1340 
   1341 			if ( isset( $_POST['post_ID'] ) && strval( $post_id ) !== $_POST['post_ID'] ) { // phpcs:ignore WordPress.Security.NonceVerification
   1342 				return $post_id;
   1343 			}
   1344 
   1345 			// Check if our nonce is set.
   1346 			if ( ! isset( $_POST['redux_metaboxes_meta_nonce'] ) || ! isset( $_POST[ $this->parent->args['opt_name'] ] ) ) {
   1347 				return $post_id;
   1348 			}
   1349 
   1350 			$meta = $this->get_meta( $post_id );
   1351 
   1352 			// Verify that the nonce is valid.
   1353 			// Validate fields (if needed).
   1354 			if ( ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['redux_metaboxes_meta_nonce'] ) ), 'redux_metaboxes_meta_nonce' ) ) {
   1355 				return $post_id;
   1356 			}
   1357 
   1358 			// If this is an autosave, our form has not been submitted, so we don't want to do anything.
   1359 			if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
   1360 				return $post_id;
   1361 			}
   1362 
   1363 			// Check the user's permissions, even allowing custom capabilities.
   1364 			$obj = get_post_type_object( $post->post_type );
   1365 			if ( ! current_user_can( $obj->cap->edit_post, $post_id ) ) {
   1366 				return $post_id;
   1367 			}
   1368 
   1369 			// Import.
   1370 			if ( isset( $_POST[ $this->parent->args['opt_name'] ]['import_code'] ) && ! empty( $_POST[ $this->parent->args['opt_name'] ]['import_code'] ) ) {
   1371 				$import = json_decode( sanitize_text_field( wp_unslash( $_POST[ $this->parent->args['opt_name'] ]['import_code'] ) ), true );
   1372 				unset( $_POST[ $this->parent->args['opt_name'] ]['import_code'] );
   1373 
   1374 				foreach ( Redux_Helpers::sanitize_array( wp_unslash( $_POST[ $this->parent->args['opt_name'] ] ) ) as $key => $value ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
   1375 					if ( ! isset( $import[ $key ] ) ) {
   1376 						$import[ $key ] = $value;
   1377 					}
   1378 				}
   1379 
   1380 				$_POST[ $this->parent->args['opt_name'] ] = $import;
   1381 			}
   1382 
   1383 			$to_save    = array();
   1384 			$to_compare = array();
   1385 			$to_delete  = array();
   1386 			$dont_save  = true;
   1387 
   1388 			if ( isset( $this->parent->args['metaboxes_save_defaults'] ) && $this->parent->args['metaboxes_save_defaults'] ) {
   1389 				$dont_save = false;
   1390 			}
   1391 
   1392 			foreach ( Redux_Helpers::sanitize_array( wp_unslash( $_POST[ $this->parent->args['opt_name'] ] ) ) as $key => $value ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
   1393 
   1394 				// Have to remove the escaping for array comparison.
   1395 				if ( is_array( $value ) ) {
   1396 					foreach ( $value as $k => $v ) {
   1397 						if ( ! is_array( $v ) ) {
   1398 							$value[ $k ] = wp_unslash( $v );
   1399 						}
   1400 					}
   1401 				}
   1402 
   1403 				$save = true;
   1404 
   1405 				// parent_options.
   1406 				if ( ! $dont_save && isset( $this->options_defaults[ $key ] ) && $value === $this->options_defaults[ $key ] ) {
   1407 					$save = false;
   1408 				}
   1409 
   1410 				if ( $save && isset( $this->parent_options[ $key ] ) && $this->parent_options[ $key ] !== $value ) {
   1411 					$save = false;
   1412 				}
   1413 
   1414 				if ( $save ) {
   1415 					$to_save[ $key ]    = $value;
   1416 					$to_compare[ $key ] = $this->parent->options[ $key ] ?? '';
   1417 				} else {
   1418 					$to_delete[ $key ] = $value;
   1419 				}
   1420 			}
   1421 
   1422 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
   1423 			$to_save = apply_filters( 'redux/metaboxes/save/before_validate', $to_save, $to_compare, $this->sections );
   1424 
   1425 			$validate = $this->parent->_validate_values( $to_save, $to_compare, $this->sections );
   1426 
   1427 			// Validate fields (if needed).
   1428 			foreach ( $to_save as $key => $value ) {
   1429 				if ( isset( $validate[ $key ] ) && $value !== $validate[ $key ] ) {
   1430 					if ( isset( $this->parent->options[ $key ] ) && $validate[ $key ] === $this->parent->options[ $key ] ) {
   1431 						unset( $to_save[ $key ] );
   1432 					} else {
   1433 						$to_save[ $key ] = $validate[ $key ];
   1434 					}
   1435 				}
   1436 			}
   1437 
   1438 			if ( ! empty( $this->parent->errors ) || ! empty( $this->parent->warnings ) ) {
   1439 				$this->parent->transients['notices'] = ( isset( $this->parent->transients['notices'] ) && is_array( $this->parent->transients['notices'] ) ) ? $this->parent->transients['notices'] : array();
   1440 
   1441 				if ( ! isset( $this->parent->transients['notices']['errors'] ) || $this->parent->transients['notices']['errors'] !== $this->parent->errors ) {
   1442 					$this->parent->transients['notices']['errors'] = $this->parent->errors;
   1443 					$update_transients                             = true;
   1444 				}
   1445 
   1446 				if ( ! isset( $this->parent->transients['notices']['warnings'] ) || $this->parent->transients['notices']['warnings'] !== $this->parent->warnings ) {
   1447 					$this->parent->transients['notices']['warnings'] = $this->parent->warnings;
   1448 					$update_transients                               = true;
   1449 				}
   1450 
   1451 				if ( isset( $update_transients ) ) {
   1452 					$this->parent->transients['notices']['override'] = 1;
   1453 					set_transient( $this->parent->args['opt_name'] . '-transients-metaboxes', $this->parent->transients );
   1454 				}
   1455 			}
   1456 
   1457 			if ( isset( $_POST['post_type'] ) ) {
   1458 				$check = $this->post_type_fields[ sanitize_text_field( wp_unslash( $_POST['post_type'] ) ) ];
   1459 			}
   1460 
   1461 			// phpcs:ignore WordPress.NamingConventions.ValidHookName
   1462 			$to_save = apply_filters( 'redux/metaboxes/save', $to_save, $to_compare, $this->sections );
   1463 
   1464 			foreach ( $to_save as $key => $value ) {
   1465 				$prev_value = $this->meta[ $post_id ][ $key ] ?? '';
   1466 
   1467 				if ( isset( $check[ $key ] ) ) {
   1468 					unset( $check[ $key ] );
   1469 				}
   1470 
   1471 				update_post_meta( $post_id, $key, $value, $prev_value );
   1472 			}
   1473 
   1474 			foreach ( $to_delete as $key => $value ) {
   1475 				if ( isset( $check[ $key ] ) ) {
   1476 					unset( $check[ $key ] );
   1477 				}
   1478 
   1479 				$prev_value = $this->meta[ $post_id ][ $key ] ?? '';
   1480 				delete_post_meta( $post_id, $key, $prev_value );
   1481 			}
   1482 
   1483 			foreach ( $check as $key => $value ) {
   1484 				delete_post_meta( $post_id, $key );
   1485 			}
   1486 		}
   1487 
   1488 		/**
   1489 		 * Some functions, like the term recount, require the visibility to be set prior. Lets save that here.
   1490 		 *
   1491 		 * @access public
   1492 		 *
   1493 		 * @param mixed $post_id Post ID.
   1494 		 *
   1495 		 * @return void
   1496 		 */
   1497 		public function pre_post_update( $post_id ) {
   1498 			if ( isset( $_POST['_visibility'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
   1499 				update_post_meta( $post_id, '_visibility', sanitize_text_field( wp_unslash( $_POST['_visibility'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
   1500 			}
   1501 
   1502 			if ( isset( $_POST['_stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
   1503 				update_post_meta( $post_id, '_stock_status', sanitize_text_field( wp_unslash( $_POST['_stock_status'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
   1504 			}
   1505 		}
   1506 
   1507 		/**
   1508 		 * Show any stored error messages.
   1509 		 *
   1510 		 * @access public
   1511 		 * @return void
   1512 		 */
   1513 		public function meta_boxes_show_errors() {
   1514 			if ( isset( $this->notices['errors'] ) && ! empty( $this->notices['errors'] ) ) {
   1515 				echo '<div id="redux_metaboxes_errors" class="error fade">';
   1516 				echo '<p><strong><span></span> ' . count( $this->notices['errors'] ) . ' ' . esc_html__( 'error(s) were found!', 'redux-framework' ) . '</strong></p>';
   1517 				echo '</div>';
   1518 			}
   1519 
   1520 			if ( isset( $this->notices['warnings'] ) && ! empty( $this->notices['warnings'] ) ) {
   1521 				echo '<div id="redux_metaboxes_warnings" class="error fade" style="border-left-color: #E8E20C;">';
   1522 				echo '<p><strong><span></span> ' . count( $this->notices['warnings'] ) . ' ' . esc_html__( 'warnings(s) were found!', 'redux-framework' ) . '</strong></p>';
   1523 				echo '</div>';
   1524 			}
   1525 		}
   1526 	}
   1527 }
   1528 
   1529 if ( ! function_exists( 'redux_metaboxes_loop_start' ) ) {
   1530 	/**
   1531 	 * Start loop.
   1532 	 *
   1533 	 * @param string $opt_name Panel opt_name.
   1534 	 * @param array  $the_post Post object.
   1535 	 */
   1536 	function redux_metaboxes_loop_start( string $opt_name, array $the_post = array() ) {
   1537 		$redux     = ReduxFrameworkInstances::get_instance( $opt_name );
   1538 		$metaboxes = $redux->extensions['metaboxes'];
   1539 
   1540 		$metaboxes->loop_start( $the_post );
   1541 	}
   1542 }
   1543 
   1544 if ( ! function_exists( 'redux_metaboxes_loop_end' ) ) {
   1545 	/**
   1546 	 * End loop.
   1547 	 *
   1548 	 * @param string $opt_name Panel opt_name.
   1549 	 * @param array  $the_post Post object.
   1550 	 */
   1551 	function redux_metaboxes_loop_end( string $opt_name, array $the_post = array() ) {
   1552 		$redux     = ReduxFrameworkInstances::get_instance( $opt_name );
   1553 		$metaboxes = $redux->extensions['metaboxes'];
   1554 
   1555 		$metaboxes->loop_end();
   1556 	}
   1557 }
   1558 
   1559 if ( ! function_exists( 'redux_post_meta' ) ) {
   1560 	/**
   1561 	 * Retrieve post meta values/settings.
   1562 	 *
   1563 	 * @param string $opt_name Panel opt_name.
   1564 	 * @param mixed  $the_post Post ID.
   1565 	 * @param string $meta_key Meta key.
   1566 	 * @param mixed  $def_val  Default value.
   1567 	 *
   1568 	 * @return string|void
   1569 	 */
   1570 	function redux_post_meta( string $opt_name = '', $the_post = array(), string $meta_key = '', $def_val = '' ) {
   1571 		return Redux::get_post_meta( $opt_name, $the_post, $meta_key, $def_val );
   1572 	}
   1573 }