Current File : /var/www/e360ban/wp-content/plugins/wp-views/application/models/shortcode/control/post_ancestor.php
<?php

/**
 * Manage the wpv-control-post-ancestor shortcode and its legacy wpv-control-item alias.
 *
 * @since m2m
 */
abstract class WPV_Shortcode_Control_Post_Ancestor {

	const SHORTCODE_NAME = 'wpv-control-post-ancestor';
	const SHORTCODE_NAME_ALIAS = 'wpv-control-item';

	const WP_QUERY_ANCESTOR_TREE_ROOF_FLAG = 'wpv_ancestor_tree_roof_query';

	/**
	 * @var string[]
	 */
	protected $shortcode_atts = array(
		'type'					=> '',
		'url_param'				=> '',
		'ancestor_type'			=> '',
		'ancestor_tree'			=> '',
		'default_label'			=> '',
		'returned_pt_parents'	=> '',
		'format'				=> '%%NAME%%',
		'orderby'				=> 'title',
		'order'					=> 'ASC',
		'style'					=> '',
		'class'					=> '',
		'label_style'			=> '',
		'label_class'			=> '',
		'output'				=> 'bootstrap'
	);

	/**
	 * @var string[]
	 */
	protected $required_atts = array(
		'url_param', 'type', 'ancestor_type', 'ancestor_tree'
	);

	/**
	 * @var string[]
	 */
	protected $allowed_orderby_values = array(
		'id'			=> 'ID',
		'title'			=> 'post_title',
		'date'			=> 'post_date',
		'date_modified'	=> 'post_modified',
		'comment_count'	=> 'comment_count'
	);

	/**
	 * @var string|null
	 */
	protected $user_content;

	/**
	 * @var string[]
	 */
	protected $user_atts;

	/**
	 * @var string
	 */
	protected $shortcode;

	/**
	 * @var WPV_Filter_Post_Relationship
	 */
	protected $filter;

	/**
	 * @var string[]
	 */
	protected $relationship_closest;

	/**
	 * @var array
	 */
	protected $relationship_pieces;

	/**
	 * @var array
	 */
	protected $relationship_tree;

	/**
	 * @var string
	 */
	protected $relationship_tree_ground;

	/**
	 * @var string
	 */
	protected $relationship_tree_roof;

	/**
	 * @var array
	 */
	protected $ancestor_data;

	/**
	 * @var string
	 */
	protected $url_param;

	/**
	 * @var string
	 */
	protected $orderby;

	/**
	 * @var string
	 */
	protected $order;

	/**
	 * @var array
	 */
	protected $dependency_and_counters_data;

	/**
	 * @var array
	 */
	protected $filter_options;

	/**
	 * @var string
	 */
	protected $style;

	/**
	 * @var string[]
	 */
	protected $class_array;


	/**
	 * Constructor.
	 *
	 * @param $shortcode
	 */
	public function __construct( $shortcode ) {
		$this->shortcode = $shortcode;
		if ( self::SHORTCODE_NAME_ALIAS === $this->shortcode ) {
			$this->shortcode_atts['output'] = 'legacy';
		}
		$this->shortcode_atts['default_label'] = __( 'Please select', 'wpv-views' );
	}

	/**
	 * Get the shortcode output value.
	 *
	 * @param $atts
	 * @param $content
	 *
	 * @return string
	 *
	 * @since m2m
	 */
	public function get_value( $atts, $content = null ) {
		$this->user_atts    = shortcode_atts( $this->shortcode_atts, $atts );
		$this->user_content = $content;

		$filter_manager = WPV_Filter_Manager::get_instance();
		$this->filter = $filter_manager->get_filter( Toolset_Element_Domain::POSTS, 'relationship' );

		if ( ! $this->filter->is_types_installed() ) {
			return __( 'You need the Types plugin to render this custom search control.', 'wpv-views' );
		}

		$this->relationship_closest = $this->filter->get_closest_related_to_returned_post_types();

		// Remove once ported
		$this->user_atts['returned_pt_parents'] = implode( ',' , $this->relationship_closest );

		$this->user_atts['url_param'] = $this->filter->get_filter_data( 'url_param' );
		$this->user_atts['ancestor_tree'] = $this->filter->get_filter_data( 'ancestors' );

		$format = $this->filter->get_filter_data( 'format' );

		if ( ! empty( $format ) ) {
			$this->user_atts['format'] = $format;
		}

		foreach ( $this->required_atts as $required_att ) {
			if ( empty( $this->user_atts[ $required_att ] ) ) {
				return sprintf( __( 'The %s attribute is missing.', 'wpv-views' ), $required_att );
			}
		}

		// Set basic data about the relationships tree (including roof and ground),
		// and the current ancestor, plus output classes and styles
		$this->set_data();

		if ( ! array_key_exists( $this->ancestor_data['type'], $this->relationship_tree ) ) {
			return __( 'The ancestor_type argument refers to a post type that is not included in the relationship tree.', 'wpv-views' );
		}

		if ( ! in_array( $this->relationship_tree_ground, $this->relationship_closest ) ) {
			return __( 'The ancestors argument does not end with a valid post that is related for the returned post types on this View.', 'wpv-views' );
		}

		// Orderby and order
		if ( ! array_key_exists( $this->user_atts['orderby'], $this->allowed_orderby_values ) ) {
			$this->user_atts['orderby'] = 'title';
		}
		$this->orderby = $this->allowed_orderby_values[ $this->user_atts['orderby'] ];
		$this->order = ( 'DESC' == strtoupper( $this->user_atts['order'] ) ) ? 'DESC' : 'ASC';

		// URL parameter and extra classes
		if ( $this->ancestor_data['type'] == $this->relationship_tree_ground ) {
			$this->url_param = $this->user_atts['url_param'];
			$this->class_array[] = 'js-wpv-filter-trigger';
		} else {
			$this->url_param = $this->user_atts['url_param'] . '-' . $this->ancestor_data['type'];
			$this->class_array[] = 'js-wpv-post-relationship-update';
		}

		// Default label
		if ( ! empty( $this->user_atts['default_label'] ) ) {
			$aux_array = apply_filters( 'wpv_filter_wpv_get_rendered_views_ids', array() );
			$view_name = get_post_field( 'post_name', end( $aux_array ) );
			$this->user_atts['default_label'] = wpv_translate(
				$this->ancestor_data['type'] . '_default_label',
				$this->user_atts['default_label'],
				false,
				'View ' . $view_name
			);

			$this->user_atts['default_label'] = wpv_translate(
				$this->ancestor_data['type'] . '@' . $this->ancestor_data['relationship'] . '.' . $this->ancestor_data['role'] . '_default_label',
				$this->user_atts['default_label'],
				false,
				'View ' . $view_name
			);
		}

		// Format
		if ( ! empty( $this->user_atts['format'] ) ) {
			$aux_array = apply_filters( 'wpv_filter_wpv_get_rendered_views_ids', array() );
			$view_name = get_post_field( 'post_name', end( $aux_array ) );
			$this->user_atts['format'] = wpv_translate(
				$this->ancestor_data['type'] . '_format',
				$this->user_atts['format'],
				false,
				'View ' . $view_name
			);

			$this->user_atts['format'] = wpv_translate(
				$this->ancestor_data['type'] . '@' . $this->ancestor_data['relationship'] . '.' . $this->ancestor_data['role'] . '_format',
				$this->user_atts['format'],
				false,
				'View ' . $view_name
			);
		}

		// Filter options:
		$this->filter_options = array();
		if ( $this->ancestor_data['type'] == $this->relationship_tree_roof ) {
			$this->filter_options = $this->get_relationship_tree_roof_options();
		} else {
			$this->filter_options = $this->get_relationship_tree_item_options();
		}

		// Dependency and counters are different
		$this->setup_frontend_dependency_and_counters_data_with_cache();

		$output = '';

		// Lets try to keep output methods shared too
		switch ( $this->user_atts['output'] ) {
			case 'legacy':
				$output = $this->get_legacy_value();
				break;
			case 'bootstrap':
				$output = $this->get_bootstrap_value();
				break;
		}

		$this->clear_data();
		return $output;
	}

	/**
	 * Set shortcode basic data, including:
	 * - Relationships tree, roof and ground.
	 * - Current ancestor data.
	 * - Output styles and classnames.
	 *
	 * @since m2m
	 */
	protected function set_data() {
		$this->relationship_pieces = explode( '>', $this->user_atts['ancestor_tree'] );
		$this->relationship_tree = $this->filter->get_relationship_tree( $this->user_atts['ancestor_tree'] );

		$ancestors_keys = array_keys( $this->relationship_tree );
		$this->relationship_tree_roof = $ancestors_keys[0];
		$this->relationship_tree_ground = array_pop( $ancestors_keys );

		$ancestor_data = explode( '@', $this->user_atts['ancestor_type'] );
		$this->ancestor_data = array_key_exists( $ancestor_data[0], $this->relationship_tree )
			? $this->relationship_tree[ $ancestor_data[0] ]
			: array(
				'type' => '',
				'relationship' => '',
				'role' => '',
				'role_target' => ''
			);

		$this->class_array = explode( ' ', $this->user_atts['class'] );
		$this->style = $this->user_atts['style'];
	}

	/**
	 * Get the posts to include in the ancestor roof selector.
	 *
	 * Note that the way to gather such options depend on WPML.
	 * - When the relevant post type is set to display as translated, default to a
	 *   WP_Query and let WPML do its magic to return results.
	 * - When the relevant post type is translatable, apply filters per current language.
	 * - Otherwise, just get all IDs and titles.
	 *
	 * @return array of objects with at least ID and post_title properties.
	 * @since m2m
	 */
	protected function get_relationship_tree_roof_options() {
		$values_to_prepare = array();

		$needs_wpml_support = $this->filter->is_wpml_installed_and_ready();

		if (
			$needs_wpml_support
			&& apply_filters( 'wpml_is_display_as_translated_post_type', false, $this->ancestor_data['type'] )
		) {
			// Translation mode: display as translated.
			// In this case we default to the WP_Query hooked results from WPML.
			// This is slightly more expensive than a custom query,
			// but the custom query here would be too complex.
			$ancestor_query_args = array(
				'post_type' => $this->ancestor_data['type'],
				'posts_per_page' => -1,
				'no_found_rows' => true,
				'update_post_term_cache' => false,
				'update_post_meta_cache' => false,
				'ignore_sticky_posts' => true,
				self::WP_QUERY_ANCESTOR_TREE_ROOF_FLAG => true,
			);

			add_filter( 'posts_fields', array( $this, 'query_just_id_and_title' ), 10, 2 );
			$ancestor_query = new \WP_Query( $ancestor_query_args );
			remove_filter( 'posts_fields', array( $this, 'query_just_id_and_title' ), 10 );

			return $ancestor_query->posts;
		}

		// In any other case, run a custom query, as it is cheaper.
		global $wpdb;
		$wpml_join = $wpml_where = "";
		if (
			$needs_wpml_support
			&& apply_filters( 'wpml_is_translated_post_type', false, $this->ancestor_data['type'] )
		) {
			$wpml_current_language = apply_filters( 'wpml_current_language', null );
			$wpml_join = " JOIN {$wpdb->prefix}icl_translations icl_t ";
			$wpml_where = " AND p.ID = icl_t.element_id AND icl_t.language_code = %s AND icl_t.element_type LIKE 'post_%' ";
			$values_to_prepare[] = $wpml_current_language;
		}

		$values_to_prepare[] = $this->ancestor_data['type'];

		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT p.ID, p.post_title
				FROM {$wpdb->posts} p {$wpml_join}
				WHERE p.post_status = 'publish'
				{$wpml_where}
				AND p.post_type = %s
				ORDER BY p.{$this->orderby} {$this->order}",
				$values_to_prepare
			)
		);
	}

	/**
	 * Callback on the posts_fields filter to adjust the returned fields from a WP_Query.
	 *
	 * @param string $fields
	 * @param \WP_Query $query
	 * @return string
	 */
	public function query_just_id_and_title( $fields, $query ) {
		if ( $query->get( self::WP_QUERY_ANCESTOR_TREE_ROOF_FLAG ) ) {
			global $wpdb;
			$fields = "$wpdb->posts.ID, $wpdb->posts.post_title";
		}

		return $fields;
	}

	/**
	 * Get the posts to include in any ancestor selector but the tree roof.
	 *
	 * The actual implementation depends on whether m2m has been activated.
	 *
	 * @return array
	 *
	 * @since m2m
	 */
	protected abstract function get_relationship_tree_item_options();

	/**
	 * Get the cache target data for search dependency and counters.
	 *
	 * The actual data to cache depends on whether m2m has been activated.
	 *
	 * @return array
	 *
	 * @since m2m
	 */
	protected abstract function set_target_for_cache_extended_for_post_relationship();

	/**
	 * Calculate the needed data to be used by dependency and counters, including auxiliar queries.
	 *
	 * @since m2m
	 */
	protected function setup_frontend_dependency_and_counters_data_with_cache() {
		$object_settings = $this->filter->get_current_object_settings();

		$this->dependency_and_counters_data = array(
			'use_query_cache' => 'disabled',
			'dependency' => 'disabled',
			'counters' => 'disabled',
			'empty_action' => 'hide',
			'relationship_cache' => array(),
			'auxiliar_query_count' => false
		);

		if (
			isset( $object_settings['dps'] )
			&& is_array( $object_settings['dps'] )
			&& isset( $object_settings['dps']['enable_dependency'] )
			&& $object_settings['dps']['enable_dependency'] == 'enable'
		) {
			$this->dependency_and_counters_data['dependency'] = 'enabled';
			$force_disable_dependant = apply_filters( 'wpv_filter_wpv_get_force_disable_dps', false );
			if ( $force_disable_dependant ) {
				$this->dependency_and_counters_data['dependency'] = 'disabled';
			}
		}
		if (
			strpos( $this->user_atts['format'], '%%COUNT%%' ) !== false
			|| strpos( $this->user_atts['default_label'], '%%COUNT%%' ) !== false
		) {
			$this->dependency_and_counters_data['counters'] = 'enabled';
		}

		$this->dependency_and_counters_data['use_query_cache'] =
			(
				'enabled' == $this->dependency_and_counters_data['dependency']
				|| 'enabled' == $this->dependency_and_counters_data['counters']
			)
			? 'enabled'
			: 'disabled';

		if ( 'disabled' === $this->dependency_and_counters_data['use_query_cache'] ) {
			return;
		}

		// Empty action
		$empty_action_type = $this->user_atts['type'];
		switch ( $empty_action_type ) {
			case 'radio':
				$empty_action_type = 'radios';
				break;
			case 'multi-select':
			case 'multiselect':
				$empty_action_type = 'multi_select';
				break;
		}
		if ( isset( $object_settings['dps'][ 'empty_' . $empty_action_type ] ) ) {
			$this->dependency_and_counters_data['empty_action'] = $object_settings['dps'][ 'empty_' . $empty_action_type ];
		}

		if ( ! $this->is_ancestor_filter_submitted() ) {
			// This is when there is no value selected
			// And will return the natural cache
			WPV_Cache::generate_cache_extended_for_post_relationship();
		} else {
			// When there is a selected value, create a pseudo-cache based on all the other filters
			// Note that as this is a hierarquical filter, disabling the current means leaving the ancestors ones
			$current_filter = $_GET[ $this->url_param ];
			$object_settings = $this->filter->get_current_object_settings();
			unset( $_GET[ $this->url_param ] );
			$query = apply_filters( 'wpv_filter_wpv_get_dependant_extended_query_args', array(), $object_settings, array( 'relationship' => 'pr_filter_post__in' ) );
			$aux_cache_query = null;
			if (
				isset( $query['post__in'] )
				&& is_array( $query['post__in'] )
				&& isset( $query['pr_filter_post__in'] )
				&& is_array( $query['pr_filter_post__in'] )
			) {
				$diff = array_diff( $query['post__in'], $query['pr_filter_post__in'] );
				if ( empty( $diff ) ) {// TODO maybe we can skip the query here
					unset( $query['post__in'] );
				} else {
					$query['post__in'] = $diff;
				}
			}
			$aux_cache_query = new WP_Query( $query );
			if (
				is_array( $aux_cache_query->posts )
				&& ! empty( $aux_cache_query->posts )
			) {
				WPV_Cache::generate_cache_extended_for_post_relationship(
					$aux_cache_query->posts,
					$this->set_target_for_cache_extended_for_post_relationship()
				);
				$this->dependency_and_counters_data['auxiliar_query_count'] = count( $aux_cache_query->posts );
			} else {
				// Just in case, this will return the natural cache
				WPV_Cache::generate_cache_extended_for_post_relationship();
			}
			$_GET[ $this->url_param  ] = $current_filter;
		}
		// Now, generate the WPV_Cache::$stored_relationship_cache from the current ancestor data
		// Notice that this just extends the native cache with the one generated by the current relationship data
		// so it clears previous iteration automatically, as it is non-permanent data
		$ancestor_tree_array = array_keys( $this->relationship_tree );
		$calculate_counters = ( 'enabled' == $this->dependency_and_counters_data['counters'] );
		WPV_Cache::generate_post_relationship_tree_cache( $ancestor_tree_array, $calculate_counters );
		$this->dependency_and_counters_data['relationship_cache'] = WPV_Cache::$stored_relationship_cache;
	}

	/**
	 * Check whether the current search filter has been submitted.
	 *
	 * Just needs to check that the associated URL parameter is in the URL query string.
	 *
	 * @return bool
	 *
	 * @since m2m
	 */
	protected function is_ancestor_filter_submitted() {
		if ( empty( $_GET[ $this->url_param ] ) ) {
			return false;
		}
		if ( $_GET[ $this->url_param ] === 0 ) {
			return false;
		}
		if (
			is_array( $_GET[ $this->url_param ] )
			&& in_array( (string) 0, $_GET[ $this->url_param ] )
		) {
			return false;
		}
		return true;
	}

	/**
	 * Clear any shortcode stored data.
	 *
	 * @since m2m
	 */
	protected function clear_data() {
		$this->relationship_closest = array();
		$this->relationship_tree = array();
		$this->relationship_tree_ground = null;
		$this->relationship_tree_roof = null;

	}

	/**
	 * Get the shortcode value for the legacy output.
	 *
	 * @return string
	 *
	 * @since m2m
	 */
	protected function get_legacy_value() {
		$return = '';

		switch ( $this->user_atts['type'] ) {
			case 'select':
			case 'multi-select':
				// Add the default value to the top of the options if $type is select, with a 0 value
				$options = array();
				if ( 'select' === $this->user_atts['type'] ) {
					if ( empty( $this->user_atts['default_label'] ) ) {
						$options[''] = 0;
					} else {
						if ( strpos( $this->user_atts['default_label'], '%%COUNT%%' ) !== false ) {
							if ( $this->dependency_and_counters_data['auxiliar_query_count'] !== false ) {
								$this->user_atts['default_label'] = str_replace(
									'%%COUNT%%',
									$this->dependency_and_counters_data['auxiliar_query_count'],
									$this->user_atts['default_label']
								);
							} else {
								$current_post_query = apply_filters( 'wpv_filter_wpv_get_post_query', null );
								$this->user_atts['default_label'] = str_replace(
									'%%COUNT%%',
									$current_post_query->found_posts, $this->user_atts['default_label']
								);
							}
						}
						$options[ $this->user_atts['default_label'] ] = 0;
					}
				}
				// Create the basic $element that will hold the wpv_form_control attributes
				$element = array(
					'field'	=> array(
						'#type'			=> 'select',
						'#attributes'	=> array(
							'style'					=> $this->user_atts['style'],
							'class'					=> implode( ' ', $this->class_array ),
							'data-currentposttype'	=> $this->ancestor_data['type']
						),
						'#inline'		=> true
					)
				);
				// Build the name, id and default values depending whether we are dealing with a real parent or not
				$element['field']['#default_value'] = array( 0 );
				$element['field']['#name'] = $this->url_param . '[]';
				if ( $this->ancestor_data['type'] == $this->relationship_tree_ground ) {
					$element['field']['#id'] = 'wpv_control_' . $this->user_atts['type']
						. '_' . $this->user_atts['url_param'];
				} else {
					$element['field']['#id'] = 'wpv_control_' . $this->user_atts['type']
						. '_' . $this->user_atts['url_param'] . '_' . $this->ancestor_data['type'];
				}
				if ( isset( $_GET[ $this->url_param ] ) ) {
					$element['field']['#default_value'] = $_GET[ $this->url_param ] ;
				}
				// Security check: this must always be an array!
				if ( ! is_array( $element['field']['#default_value'] ) ) {
					$element['field']['#default_value'] = array( $element['field']['#default_value'] );
				}
				// Loop through the posts and add them as options like post_title => ID
				foreach ( $this->filter_options as $pa_item ) {
					$options[ $pa_item->ID ] = array(
						'#title'	=> str_replace( '%%NAME%%', $pa_item->post_title, $this->user_atts['format'] ),
						'#value'	=> $pa_item->ID,
						'#inline'	=> true,
						'#after'	=> '<br />'
					);
					if (
						'enabled' === $this->dependency_and_counters_data['dependency']
						|| 'enabled' === $this->dependency_and_counters_data['counters']
					) {
						if (
							isset( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ] )
							&& is_array( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ] )
						) {
							if (
								'enabled' === $this->dependency_and_counters_data['counters']
								&& isset( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ]['count'] )
							) {
								$options[ $pa_item->ID ]['#title'] = str_replace(
									'%%COUNT%%',
									$this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ]['count'],
									$options[ $pa_item->ID ]['#title']
								);
							}
						} else {
							if ( 'enabled' === $this->dependency_and_counters_data['counters'] ) {
								$options[ $pa_item->ID ]['#title'] = str_replace(
									'%%COUNT%%',
									'0',
									$options[ $pa_item->ID ]['#title']
								);
							}
							if (
								'enabled' === $this->dependency_and_counters_data['dependency']
								&& ! in_array( $pa_item->ID, $element['field']['#default_value'] )
							) {
								$options[ $pa_item->ID ]['#disable'] = 'true';
								$options[ $pa_item->ID ]['#labelclass'] = 'wpv-parametric-disabled';
								if ( 'hide' === $this->dependency_and_counters_data['empty_action'] ) {
									unset( $options[ $pa_item->ID ] );
								}
							}
						}
					}
				}
				$element['field']['#options'] = $options;
				// If there are no options and is multi-select, break NOTE this break is for hide, maybe disable, we will see
				/* if (
					$this->user_atts['type'] == 'multi-select'
					&& count( $options ) == 0
				) {
				//	break;
				} */
				// If there is only one option for select or none for multi-select, disable this form control NOTE review this
				if (
					count( $options ) == 1
					&& $this->user_atts['type'] == 'select'
				) {
					$element['field']['#attributes']['disabled'] = 'disabled';
				}
				// If type is multi-select, use it
				if ( $this->user_atts['type'] == 'multi-select' ) {
					$element['field']['#multiple'] = 'multiple';
				}
				// Create the form control and add it to the $returned_value
				$return .= wpv_form_control( $element );
				break;
			case 'checkboxes':
				$options = array();
				// Create the basic $element that will hold the wpv_form_control attributes
				$element = array(
					'field'	=> array(
						'#type'			=> $this->user_atts['type'],
						'#attributes'	=> array(
							'style' => $this->user_atts['style'],
							'class' => implode( ' ', $this->class_array ),
						),
						'#inline'		=> true,
						'#before'		=> '<div class="wpcf-checkboxes-group">', //we need to wrap them for js purposes
						'#after'		=> '</div>'
					)
				);
				// Build the name, id and default values depending whether we are dealing with a real parent or not
				$element['field']['#default_value'] = array( -1 );
				if ( isset( $_GET[ $this->url_param ] ) ) {
					$element['field']['#default_value'] = $_GET[ $this->url_param ];
				}
				$element['field']['#name'] = $this->url_param . '[]';
				if ( $this->ancestor_data['type'] == $this->relationship_tree_ground ) {
					$element['field']['#id'] = 'wpv_control_' . $this->user_atts['type']
						. '_' . $this->user_atts['url_param'];
				} else {
					$element['field']['#id'] = 'wpv_control_' . $this->user_atts['type']
						. '_' . $this->user_atts['url_param'] . '_' . $this->ancestor_data['type'];
				}
				// Security check: this must always be an array!
				if ( ! is_array( $element['field']['#default_value'] ) ) {
					$element['field']['#default_value'] = array( $element['field']['#default_value'] );
				}
				// Loop through the posts and add them as options
				foreach ( $this->filter_options as $pa_item ) {
					$options[ $pa_item->ID ] = array(
						'#name'				=> $this->url_param . '[]',
						'#title'			=> str_replace( '%%NAME%%', $pa_item->post_title, $this->user_atts['format'] ),
						'#value'			=> $pa_item->ID,
						'#default_value'	=> in_array( $pa_item->ID, $element['field']['#default_value'] ), // set default using option titles too
						'#inline'			=> true,
						'#after'			=> '&nbsp;&nbsp;',
						'#attributes'	=> array(
							'data-currentposttype'	=> $this->ancestor_data['type'],
							'data-triggerer'		=> 'rel-relationship',
							'style'					=> $this->user_atts['style'],
							'class'					=> implode( ' ', $this->class_array )
						),
						'#labelclass'		=> $this->user_atts['label_class'],
						'#labelstyle'		=> $this->user_atts['label_style']
					);
					if (
						'enabled' === $this->dependency_and_counters_data['dependency']
						|| 'enabled' === $this->dependency_and_counters_data['counters']
					) {
						if (
							isset( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ] )
							&& is_array( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ] )
						) {
							if (
								'enabled' === $this->dependency_and_counters_data['counters']
								&& isset( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ]['count'] )
							) {
								$options[ $pa_item->ID ]['#title'] = str_replace(
									'%%COUNT%%',
									$this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ]['count'],
									$options[ $pa_item->ID ]['#title']
								);
							}
						} else {
							if ( 'enabled' === $this->dependency_and_counters_data['counters'] ) {
								$options[ $pa_item->ID ]['#title'] = str_replace( '%%COUNT%%', '0', $options[ $pa_item->ID ]['#title'] );
							}
							if (
								'enabled' === $this->dependency_and_counters_data['dependency']
								& ! in_array( $pa_item->ID, $element['field']['#default_value'] )
							) {
								$options[ $pa_item->ID ]['#attributes']['#disabled'] = 'true';
								$options[ $pa_item->ID ]['#labelclass'] .= ' wpv-parametric-disabled';
								if ( 'hide' === $this->dependency_and_counters_data['empty_action'] ) {
									unset( $options[ $pa_item->ID ] );
								}
							}
						}
					}
				}
				$element['field']['#options'] = $options;
				// Calculate the control
				$return .= wpv_form_control( $element );
				break;
			case 'radio':
			case 'radios':
				// Create the basic $element that will hold the wpv_form_control attributes
				$element = array(
					'field'	=> array(
						'#type'			=> 'radios',
						'#attributes'	=> array(
							'style'					=> $this->user_atts['style'],
							'class'					=> implode( ' ', $this->class_array ),
							'data-currentposttype'	=> $this->ancestor_data['type'],
							'data-triggerer'		=> 'rel-relationship'
						),
						'#inline'		=> true
					)
				);
				$options = array();
				if ( ! empty( $this->user_atts['default_label'] ) ) {

					if ( strpos( $this->user_atts['default_label'], '%%COUNT%%' ) !== false ) {
						if ( $this->dependency_and_counters_data['auxiliar_query_count'] !== false ) {
							$this->user_atts['default_label'] = str_replace(
								'%%COUNT%%',
								$this->dependency_and_counters_data['auxiliar_query_count'],
								$this->user_atts['default_label']
							);
						} else {
							$current_post_query = apply_filters( 'wpv_filter_wpv_get_post_query', null );
							$this->user_atts['default_label'] = str_replace(
								'%%COUNT%%',
								$current_post_query->found_posts,
								$this->user_atts['default_label']
							);
						}
					}

					$options[ $this->user_atts['default_label'] ] = array(
						'#title'	=> $this->user_atts['default_label'],
						'#value'	=> 0,
						'#inline'	=> true,
						'#after'	=> '<br />'
					);
					if (
						$this->ancestor_data['type'] == $this->relationship_tree_ground
						&& 'enabled' === $this->dependency_and_counters_data['dependency']
						&& count( $this->filter_options ) == 0
						&& (
							! isset( $_GET[ $this->url_param ] )
							|| $_GET[ $this->url_param ] != ''
						)
					) {
						$options[ $this->user_atts['default_label'] ]['#disable'] = 'true';
						$options[ $this->user_atts['default_label'] ]['#labelclass'] = ' wpv-parametric-disabled';
					}
				}
				// Build the name, id and default values depending whether we are dealing with a real parent or not
				$element['field']['#default_value'] = 0;
				$element['field']['#name'] = $this->url_param;
				if (
					isset( $_GET[ $this->url_param ] )
					&& $_GET[ $this->url_param ] != 0
				) {
					$element['field']['#default_value'] = $_GET[ $this->url_param ];
				}
				if ( $this->ancestor_data['type'] == $this->relationship_tree_ground  ) {
					$element['field']['#id'] = 'wpv_control_' . $this->user_atts['type']
						. '_' . $this->user_atts['url_param'];
				} else {
					$element['field']['#id'] = 'wpv_control_' . $this->user_atts['type']
						. '_' . $this->user_atts['url_param'] . '_' . $this->ancestor_data['type'];
				}
				// Security check: this must always be a string!
				if ( is_array( $element['field']['#default_value'] ) ) {
					$element['field']['#default_value'] = reset( $element['field']['#default_value'] );
				}
				// Loop through the posts and add them as options like post_title => ID
				foreach ( $this->filter_options as $pa_item ) {
					$options[ $pa_item->ID ] = array(
						'#title'		=> str_replace( '%%NAME%%', $pa_item->post_title, $this->user_atts['format'] ),
						'#value'		=> $pa_item->ID,
						'#inline'		=> true,
						'#after'		=> '<br />',
						'#labelclass'	=> $this->user_atts['label_class'],
						'#labelstyle'	=> $this->user_atts['label_style']
					);
					if (
						'enabled' === $this->dependency_and_counters_data['dependency']
						|| 'enabled' === $this->dependency_and_counters_data['counters']
					) {
						if (
							isset( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ] )
							&& is_array( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ] )
						) {
							if (
								'enabled' === $this->dependency_and_counters_data['counters']
								&& isset( $this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ]['count'] )
							) {
								$options[ $pa_item->ID ]['#title'] = str_replace(
									'%%COUNT%%',
									$this->dependency_and_counters_data['relationship_cache'][ $pa_item->ID ]['count'],
									$options[ $pa_item->ID ]['#title']
								);
							}
						} else {
							if ( 'enabled' === $this->dependency_and_counters_data['counters'] ) {
								$options[ $pa_item->ID ]['#title'] = str_replace( '%%COUNT%%', '0', $options[ $pa_item->ID ]['#title'] );
							}
							if (
								'enabled' === $this->dependency_and_counters_data['dependency']
								&& $pa_item->ID != $element['field']['#default_value']
							) {
								$options[ $pa_item->ID ]['#disable'] = 'true';
								$options[ $pa_item->ID ]['#labelclass'] .= ' wpv-parametric-disabled';
								if ( 'hide' === $this->dependency_and_counters_data['empty_action'] ) {
									unset( $options[ $pa_item->ID ] );
								}
							}
						}
					}
				}
				$element['field']['#options'] = $options;
				// If there is only one option, disable this form control
				//This is not really needed,asin this case we are breaking above TODO review this
				if ( count( $options ) == 0 ) {
					$element['field']['#attributes']['disabled'] = 'disabled';
				}
				// Calculate the control
				$return .= wpv_form_control( $element );
				break;
			default:
				break;
		}
		return $return;
	}

	/**
	 * Get the shortcode value for the bootstrap output.
	 *
	 * @return string
	 *
	 * @since m2m
	 */
	protected function get_bootstrap_value() {

		$walker_args = array(
			'name'				=> $this->url_param,
			'selected'			=> array( 0 ),
			'format'			=> $this->user_atts['format'],
			'style'				=> $this->user_atts['style'],
			'class'				=> '',
			'label_style'		=> $this->user_atts['label_style'],
			'label_class'		=> $this->user_atts['label_class'],
			'extra_class'		=> $this->class_array,
			'output'			=> $this->user_atts['output'],
			'type'				=> $this->user_atts['type'],
			'ancestor_type'		=> $this->ancestor_data['type'],
			'use_query_cache'	=> $this->dependency_and_counters_data['use_query_cache'],
			'dependency'		=> $this->dependency_and_counters_data['dependency'],
			'counters'			=> $this->dependency_and_counters_data['counters'],
			'empty_action'		=> $this->dependency_and_counters_data['empty_action'],
			'query_cache'		=> $this->dependency_and_counters_data['relationship_cache'],
			'auxiliar_query_count'	=> $this->dependency_and_counters_data['auxiliar_query_count']
		);

		if ( isset( $_GET[ $this->url_param ] ) ) {
			if ( is_array( $_GET[ $this->url_param ] ) ) {
				$walker_args['selected'] = $_GET[ $this->url_param ];
			} else {
				$walker_args['selected'] = array( $_GET[ $this->url_param ] );
			}
		}

		$return = '';

		switch ( $this->user_atts['type'] ) {
			case 'select':
			case 'multi-select':
				$select_args = array(
					'id'	=> 'wpv_control_' . $this->user_atts['type']
						. '_' . $this->user_atts['url_param'] . '_' . $this->ancestor_data['type'],
					'name'	=> $walker_args['name'],
					'class'	=> ( empty( $walker_args['class'] ) ) ? array() : explode( ' ', $walker_args['class'] ),
					'data-currentposttype'	=> $this->ancestor_data['type']
				);

				$minimum_options_count_to_disable = 0;
				if ( 'select' === $this->user_atts['type'] ) {
					if ( empty( $this->user_atts['default_label'] ) ) {
						$default_option = new stdClass();
						$default_option->ID = 0;
						$default_option->post_title = '';
					} else {
						$default_option = new stdClass();
						$default_option->ID = 0;
						$default_option->post_title = $this->user_atts['default_label'];
					}
					array_unshift( $this->filter_options, $default_option );
					$minimum_options_count_to_disable = 1;
				}

				if (
					$this->ancestor_data['type'] !== $this->relationship_tree_roof
					&& count( $this->filter_options ) == $minimum_options_count_to_disable
					&& $this->user_atts['type'] == 'select'
				) {
					$select_args['disabled'] = 'disabled';
				}

				$post_relationship_walker = new WPV_Walker_Post_Relationship_Select( $walker_args );

				// Backwards compatibility: for the actual parent post type, we do nto add the type name to the id attribute
				if ( $this->ancestor_data['type'] === $this->relationship_tree_ground ) {
					$select_args['id'] = 'wpv_control_' . $this->user_atts['type'] . '_' . $this->user_atts['url_param'];
				}

				$select_args['class'] = array_merge( $select_args['class'], $this->class_array );
				$select_args['class'] = array_unique( $select_args['class'] );

				switch ( $walker_args['output'] ) {
					case 'bootstrap':
						$select_args['class'][] = 'form-control';
						break;
					case 'legacy':
					default:
						$select_args['class'][] = 'wpcf-form-select form-select select';
						break;
				}

				if ( ! empty( $walker_args['style'] ) ) {
					$select_args['style'] = $walker_args['style'];
				}

				if ( 'multi-select' == $walker_args['type'] ) {
					$select_args['name'] = $walker_args['name'] . '[]';
					$select_args['multiple'] = 'multiple';
				}

				$return .= '<select';
				foreach ( $select_args as $att_key => $att_value ) {
					if (
						in_array( $att_key, array( 'style', 'class' ) )
						&& empty( $att_value )
					) {
						continue;
					}
					$return .= ' ' . $att_key . '="';
					if ( is_array( $att_value ) ) {
						$att_real_value = implode( ' ', $att_value );
						$return .= $att_real_value;
					} else {
						$return .= $att_value;
					}
					$return .= '"';
				}
				$return .=  '>';
				$return .=  call_user_func_array( array( &$post_relationship_walker, 'walk' ), array( $this->filter_options, 0 ) );
				$return .=  '</select>';
				return $return;
				break;
			case 'radio':
			case 'radios':
				if ( ! empty( $this->user_atts['default_label'] ) ) {
					$default_option = new stdClass();
					$default_option->ID = 0;
					$default_option->post_title = $this->user_atts['default_label'];
					array_unshift( $this->filter_options, $default_option );
				}
				$post_relationship_walker = new WPV_Walker_Post_Relationship_Radios( $walker_args );
				$return .= call_user_func_array( array( &$post_relationship_walker, 'walk' ), array( $this->filter_options, 0 ) );
				return $return;
				break;
			case 'checkboxes':
				$post_relationship_walker = new WPV_Walker_Post_Relationship_Checkboxes( $walker_args );
				$return .= call_user_func_array( array( &$post_relationship_walker, 'walk' ), array( $this->filter_options, 0 ) );
				return $return;
				break;
		}

		return '';
	}

}
Page Not Found
Parece que el enlace que apuntaba aquí no sirve. ¿Quieres probar con una búsqueda?
¡Hola!