Current File : /var/www/e360ban/wp-content/plugins/toolset-blocks/application/models/shortcode/post/body.php
<?php

use const OTGS\Toolset\Views\UserCapabilities\EDIT_VIEWS;

/**
 * Class WPV_Shortcode_Post_Body
 *
 * @since 2.5.0
 */
class WPV_Shortcode_Post_Body extends WPV_Shortcode_Base {

	const SHORTCODE_NAME = 'wpv-post-body';

	/**
	 * @var array
	 */
	private $shortcode_atts = array(
		'item'         => null, // post
		'id'           => null, // synonym for 'item'
		'post_id'      => null, // synonym for 'item'
		'view_template'    => 'None',
		'output'       => 'normal',
		'suppress_filters' => 'false'
	);

	/**
	 * @var array
	 */
	private $infinite_loop_keys = array();

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

	/**
	 * @var array
	 */
	private $user_atts;


	/**
	 * @var Toolset_Shortcode_Attr_Interface
	 */
	private $item;

	/**
	 * WPV_Shortcode_Post_Body constructor.
	 *
	 * @param Toolset_Shortcode_Attr_Interface $item
	 */
	public function __construct(
		Toolset_Shortcode_Attr_Interface $item
	) {
		$this->item  = $item;
	}

	/**
	 * Get the shortcode output value.
	 *
	 * @param $atts
	 * @param $content
	 * @return string
	 * @since 2.5.0
	 * @since 2.8.3 Do not render the content or apply a CT to an excluded post type.
	 * @since 3.2.0 Complete refactor as this is not a normal post shortcode:
	 *     - Render anyway when referencing a valid CT over a valid object.
	 *         - Consider loop templates for ters and user Views.
	 *     - Fail gracefully when requesting a missing CT.
	 *     - Fail gracefully when requesting a missing post with item attributes.
	 *     - Fail gracefully when requesting to render a post and there is no post set.
	 */
	public function get_value( $atts, $content = null ) {
		$this->user_atts    = shortcode_atts( $this->shortcode_atts, $atts );
		$this->user_content = $content;

		if ( ! $item_id = $this->item->get( $this->user_atts ) ) {
			// no valid item
			return $this->get_value_without_context( $atts );
		}

		// We do have a context, which can be a global post or a numeric item ID.
		$item = $this->get_post( $item_id );

		global $post;
		$post_switched = false;
		$post_cloned = null;
		if ( $this->should_switch_global_post_to_item_attribute( $item ) ) {
			$post_switched = true;
			$post_cloned = $post;
			$post = $item;
		}

		// Avoid the case for a shortcode over a post when no post is set, belongs to an excluded post type, or is password protected.
		$current_item_type = apply_filters( 'wpv_filter_wpv_get_query_type', 'posts' );
		if ( 'posts' === $current_item_type ) {
			if ( false === ( $post instanceof \WP_Post ) ) {
				if ( $post_switched ) {
					$post = $post_cloned;
				}
				return;
			} elseif ( $this->is_excluded_post_type( $post ) ) {
				if ( $post_switched ) {
					$post = $post_cloned;
				}
				return;
			} else if ( $this->should_require_post_password( $post ) ) {
				$post_protected_password_form = get_the_password_form( $post );

				/**
				 * Filter the form for posts protected by password that were supposed to be rendered by this shortcode.
				 *
				 * @param string $post_protected_password_form The default WordPress password form
				 * @param object $post The post object to which this shortcode is related to
				 * @param array $atts The array of attributes passed to this shortcode
				 * @return string
				 * @since 1.7.0
				 */
				$out = apply_filters( 'wpv_filter_post_protected_body', $post_protected_password_form, $post, $atts );
				if ( $post_switched ) {
					$post = $post_cloned;
				}
				return $out;
			}
		}

		if ( ! isset( $atts['view_template'] ) ) {
			$out = $this->get_undocumented_legacy_outcome();

			if ( $post_switched ) {
				$post = $post_cloned;
			}

			return $out;
		}

		if ( 'none' === strtolower( $this->user_atts['view_template'] ) ) {
			$out = $this->get_post_content_value();

			if ( $post_switched ) {
				$post = $post_cloned;
			}

			return $out;
		}

		$out = $this->get_content_template_value();

		if ( $post_switched ) {
			$post = $post_cloned;
		}

		return $out;
	}

	/**
	 * Try to generate some output when the item API can not return a valid match.
	 *
	 * This means that:
	 * - the item attributes could not be resolved.
	 * - OR there are no item attributes but the current global post is not set.
	 * In this case, we can only generate a valid outcome if the View is inside a terms or users loop
	 * AND the shortcode references a CT, and not the current post content.
	 *
	 * @param array $atts The shortcode attributes
	 * @return void|string
	 * @since 3.2
	 */
	private function get_value_without_context( $atts ) {
		// Here the current post is not set and the attributes might point to a missing post.
		if ( $this->has_specific_item_attribute() ) {
			// Context failed because it references a non existing item.
			return;
		}

		$current_item_type = apply_filters( 'wpv_filter_wpv_get_query_type', 'posts' );
		if ( 'posts' === $current_item_type ) {
			// Trying to display content for a post that does not exist.
			return;
		}

		if (
			! isset( $atts['view_template'] )
			|| 'none' === strtolower( $atts['view_template'] )
		) {
			// There is no post, hence there is no undocumented glitch to render.
			return;
		}

		return $this->get_content_template_value();
	}

	/**
	 * Get the content generated by the CT referenced in the shortcode attributes.
	 *
	 * @return void|string
	 * @since 3.2
	 */
	private function get_content_template_value() {
		$infinite_loop_index = $this->get_infinite_loop_index();

		if ( isset( $this->infinite_loop_keys[ $infinite_loop_index ] ) ) {
			return $this->get_infinite_loop_error();
		}

		$template_id = \WPV_Content_Template_Embedded::get_template_id_by_name( $this->user_atts['view_template'] );

		if ( 0 === $template_id ) {
			return;
		}

		do_action( 'wpv_register_printed_content_template', $template_id );

		global $WPVDebug;

		$this->infinite_loop_keys[ $infinite_loop_index ] = 1;

		do_action( 'wpv_before_shortcode_post_body' );

		$output_mode = get_post_meta( $template_id, '_wpv_view_template_mode', true );
		$hook = ( 'true' === $this->user_atts['suppress_filters'] ) ? 'wpv_filter_wpv_the_content_suppressed' : 'the_content';

		$WPVDebug->wpv_debug_start( $template_id, $this->user_atts, 'content-template' );
		$WPVDebug->set_index();

		$unprocessed_value_to_render = $this->get_unprocessed_value_to_render();
		$hooks_to_restore = array();
		$hooks_to_restore_add = true;

		$this->disable_wpml_alt_lang();

		$nested_templates_memory = $this->manage_nested_templates();

		if (
			$this->has_blocks( $unprocessed_value_to_render )
			|| 'raw_mode' === $output_mode
		) {
			$hooks_to_restore = $this->maybe_disable_formatting_hooks( $hook );
			$unprocessed_value_to_render = $this->force_filter_template_content( $unprocessed_value_to_render, $template_id );
		} else {
			$hooks_to_restore = $this->maybe_enforce_formatting_hooks( $hook );
			$hooks_to_restore_add = false;
		}

		$out = apply_filters( $hook, $unprocessed_value_to_render );

		/**
		 * Filter the outcome of this shortcode.
		 *
		 * @param string $out The outcome of the shortcode.
		 * @param int $template_id The ID of the applied Content Template.
		 * @return string
		 * @since 3.2
		 */
		$out = apply_filters( 'wpv_filter_wpv-post-body_output', $out, $template_id );

		$this->maybe_restore_formatting_hooks( $hook, $hooks_to_restore, $hooks_to_restore_add );

		$this->restore_nested_templates( $nested_templates_memory );

		$this->restore_wpml_alt_lang();

		$WPVDebug->add_log_item( 'output', $out );
		$WPVDebug->wpv_debug_end();

		do_action( 'wpv_after_shortcode_post_body' );

		unset( $this->infinite_loop_keys[ $infinite_loop_index ] );

		return $out;
	}

	/**
	 * Get the content generated by the current global post content.
	 *
	 * In the unexpected event of this being called when n global post is set,
	 * self::get_unprocessed_value_to_render will take care of producing an empty string.
	 *
	 * @return string
	 * @since 3.2
	 */
	private function get_post_content_value() {
		$infinite_loop_index = $this->get_infinite_loop_index();

		if ( isset( $this->infinite_loop_keys[ $infinite_loop_index ] ) ) {
			return $this->get_infinite_loop_error();
		}

		global $WPVDebug;

		$this->infinite_loop_keys[ $infinite_loop_index ] = 1;

		do_action( 'wpv_before_shortcode_post_body' );

		// normal (default) - use wpautop
		// raw - remove wpautop
		// inherit - when used inside a Content Template, inherit its wpautop setting; when used outside a Template, inherit from the post itself (so add format, just like "normal")
		$output_mode = $this->user_atts['output'];
		$hook = ( 'true' === $this->user_atts['suppress_filters'] ) ? 'wpv_filter_wpv_the_content_suppressed' : 'the_content';

		$WPVDebug->wpv_debug_start( 'none', $this->user_atts, 'content-template' );
		$WPVDebug->set_index();

		$unprocessed_value_to_render = $this->get_unprocessed_value_to_render();
		$hooks_to_restore = array();
		$hooks_to_restore_add = true;

		$this->disable_wpml_alt_lang();

		$nested_templates_memory = $this->manage_nested_templates();

		if (
			$this->has_blocks( $unprocessed_value_to_render )
			|| 'raw' === $output_mode
		) {
			$hooks_to_restore = $this->maybe_disable_formatting_hooks( $hook );
		} elseif ( 'normal' === $output_mode ) {
			$hooks_to_restore = $this->maybe_enforce_formatting_hooks( $hook );
			$hooks_to_restore_add = false;
		}

		$out = apply_filters( $hook, $unprocessed_value_to_render );

		/**
		 * Filter the outcome of this shortcode.
		 *
		 * @param string $out The outcome of the shortcode.
		 * @param int $template_id The ID of the applied Content Template, which right here is zero.
		 * @return string
		 * @since 3.2
		 */
		$out = apply_filters( 'wpv_filter_wpv-post-body_output', $out, 0 );

		$this->maybe_restore_formatting_hooks( $hook, $hooks_to_restore, $hooks_to_restore_add );

		$this->restore_nested_templates( $nested_templates_memory );

		$this->restore_wpml_alt_lang();

		$WPVDebug->add_log_item( 'output', $out );
		$WPVDebug->wpv_debug_end();

		do_action( 'wpv_after_shortcode_post_body' );

		unset( $this->infinite_loop_keys[ $infinite_loop_index ] );

		return $out;
	}

	/**
	 * Make official the unofficial undocumented legacy behavior when the view_template attribute is missing.
	 *
	 * This case, which is not officially suported, has been used by clients
	 * to have different outcomes in different scenarios:
	 * - the CT assigned to the single post in single pages.
	 * - the CT assigned to archive loops on archive pages.
	 * Although we do not support this case, we can not just remove it.
	 * This code offloads the outcome decision to WPV_template::the_content.
	 *
	 * @return string
	 * @since 3.2
	 */
	private function get_undocumented_legacy_outcome() {
		$infinite_loop_index = $this->get_infinite_loop_index();
		$infinite_loop_index .= '##no#view_template#attribute##';

		if ( isset( $this->infinite_loop_keys[ $infinite_loop_index ] ) ) {
			return $this->get_infinite_loop_error();
		}

		global $WPVDebug, $WPV_templates;

		$this->infinite_loop_keys[ $infinite_loop_index ] = 1;

		do_action( 'wpv_before_shortcode_post_body' );

		$WPVDebug->wpv_debug_start( 'none', $this->user_atts, 'content-template' );
		$WPVDebug->set_index();

		$unprocessed_value_to_render = $this->get_unprocessed_value_to_render();

		$this->disable_wpml_alt_lang();

		// Keep the wpautop management from $WPV_templates.
		// Note that $outpt_mode is always 'normal' here.
		// NOTE BUG: we need to first remove_wpautop because for some reason not doing so switches the global $post to the top_current_page one
		$wpautop_was_removed = $WPV_templates->is_wpautop_removed();

		$WPV_templates->remove_wpautop();
		$WPV_templates->restore_wpautop();

		if ( 'true' === $this->user_atts['suppress_filters'] ) {

			/**
			 * Mimics the the_content filter on wpv-post-body shortcodes with attribute suppress_filters="true"
			 * Check WPV_template::init()
			 *
			 * @param string $unprocessed_value_to_render The current post content.
			 * @return string
			 * @since 1.8.0
			 */
			$out .= apply_filters( 'wpv_filter_wpv_the_content_suppressed', $unprocessed_value_to_render );
		} else {
			// Avoid attachment previews.
			$prepend_attachment_priority = has_filter( 'the_content', 'prepend_attachment' );
			if ( false !== $prepend_attachment_priority ) {
				remove_filter( 'the_content', 'prepend_attachment' );
			}

			$filter_state = new WPV_WP_filter_state( 'the_content' );
			$out .= apply_filters( 'the_content', $unprocessed_value_to_render );
			$filter_state->restore();

			if ( false !== $prepend_attachment_priority ) {
				add_filter( 'the_content', 'prepend_attachment', $prepend_attachment_priority );
			}
		}

		$out = '';

		$this->restore_wpml_alt_lang();

		// Restore the wpautop configuration only if is has been changed
		if ( $wpautop_was_removed ) {
			$WPV_templates->remove_wpautop();
		} else {
			$WPV_templates->restore_wpautop();
		}

		$WPVDebug->add_log_item( 'output', $out );
		$WPVDebug->wpv_debug_end();

		do_action( 'wpv_after_shortcode_post_body' );

		unset( $this->infinite_loop_keys[ $infinite_loop_index ] );

		return $out;
	}

	/**
	 * Calculate the infinite loop index for the current scenario.
	 *
	 * @return string
	 * @since 3.2
	 */
	private function get_infinite_loop_index() {
		global $WPVDebug, $WP_Views, $post;

		$current_item_type = apply_filters( 'wpv_filter_wpv_get_query_type', 'posts' );
		$current_stop_infinite_loop_key = $current_item_type . '-';

		switch ( $current_item_type ) {
			case 'posts':
				$current_stop_infinite_loop_key .= (string) $post->ID . '-';
				if ( $WPVDebug->user_can_debug() ) {
					$WPVDebug->add_log( 'content-template', $post );
				}
				break;
			case 'taxonomy':
				$current_stop_infinite_loop_key .= (string) $WP_Views->taxonomy_data['term']->term_id . '-';
				if ( $WPVDebug->user_can_debug() ) {
					$WPVDebug->add_log( 'content-template', $WP_Views->taxonomy_data['term'] );
				}
				break;
			case 'users':
				$current_stop_infinite_loop_key .= (string) $WP_Views->users_data['term']->ID . '-';
				if ( $WPVDebug->user_can_debug() ) {
					$WPVDebug->add_log( 'content-template', $WP_Views->users_data['term'] );
				}
				break;
		}

		$current_stop_infinite_loop_key .= $this->user_atts['view_template'];

		return $current_stop_infinite_loop_key;
	}

	/**
	 * Get the error message for admins when falling into an infinite loop.
	 *
	 * @return string
	 * @since 3.2
	 */
	private function get_infinite_loop_error() {
		if ( ! current_user_can( EDIT_VIEWS ) ) {
			return;
		}

		$infinite_loop_debug = '<p style="font-weight:bold !important;color: red !important;">'
			. __( 'Content not displayed because it produces an infinite loop.', 'wpv-views' )
			. '<br />'
			. __( 'The wpv-post-body shortcode was called more than once with the same attributes over the same context, triggering an infinite loop.', 'wpv-views' )
			. '</p>';

		return $infinite_loop_debug;
	}

	/**
	 * Check whether a given variable is a post instance of a post type to exclude.
	 *
	 * @param \WP_post $item
	 * @return bool
	 */
	private function is_excluded_post_type( \WP_Post $item ) {
		$toolset_exclude_list = new \Toolset_Post_Type_Exclude_List();
		if ( $toolset_exclude_list->is_excluded( $item->post_type ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Check whether the shortcode includes an item (or synonim) attribute with a value.
	 *
	 * @return bool
	 * @since 3.2
	 */
	private function has_specific_item_attribute() {
		if (
			! empty ( $this->user_atts['item'] )
			|| ! empty ( $this->user_atts['id'] )
			|| ! empty ( $this->user_atts['post_id'] )
		) {
			// The shortcode was told to render a specific post.
			return true;
		}

		return false;
	}

	/**
	 * Disable the WPML mechanism to display links to ranslated content.
	 *
	 * @since 3.2
	 */
	private function disable_wpml_alt_lang() {
		// Remove the icl language switcher to stop WPML from add the
		// "This post is avaiable in XXXX" twice.
		// Before WPML 3.6.0
		add_filter( 'icl_post_alternative_languages', '__return_empty_string', 999 );
		// After WPML 3.6.0
		add_filter( 'wpml_ls_post_alternative_languages', '__return_empty_string', 999 );
	}

	/**
	 * Restore the WPML mechanism to display links to ranslated content.
	 *
	 * @since 3.2
	 */
	private function restore_wpml_alt_lang() {
		// Before WPML 3.6.0
		remove_filter( 'icl_post_alternative_languages', '__return_empty_string', 999 );
		// After WPML 3.6.0
		remove_filter( 'wpml_ls_post_alternative_languages', '__return_empty_string', 999 );
	}

	/**
	 * Keep track of nested applications of this shortcode over the same post object.
	 *
	 * @return null|string
	 * @since 3.2
	 */
	private function manage_nested_templates() {
		global $post;
		if ( false === ( $post instanceof \WP_Post ) ) {
			return null;
		}

		$nested_templates_memory = null;
		if (
			isset( $post->view_template_override )
			&& $post->view_template_override != ''
		) {
			$nested_templates_memory = $post->view_template_override;
		}

		$post->view_template_override = $this->user_atts['view_template'];

		return $nested_templates_memory;
	}

	/**
	 * Restore the reference to the previous instance of this shortcode, if any.
	 *
	 * @param null|string $nested_templates_memory
	 * @since 3.2
	 */
	private function restore_nested_templates( $nested_templates_memory ) {
		global $post;
		if ( false === ( $post instanceof \WP_Post ) ) {
			return;
		}

		if ( isset( $post->view_template_override ) ) {
			if ( $nested_templates_memory ) {
				$post->view_template_override = $nested_templates_memory;
			} else {
				unset( $post->view_template_override );
			}
		}
	}

	/**
	 * After resolving the item shortcode attribute, check whether the global post should be replaced by it.
	 *
	 * @param null|\WP_Post $item
	 * @return bool
	 * @since 3.2
	 */
	private function should_switch_global_post_to_item_attribute( $item ) {
		if ( null === $item ) {
			return false;
		}

		global $post;

		if ( false === ( $post instanceof \WP_Post ) ) {
			return true;
		}

		if ( $post->ID !== $item->ID ) {
			return true;
		}

		return false;
	}

	/**
	 * Get the unprocessed content to render by the shortcode.
	 *
	 * It can be the CT content if the shortcode is set to render one,
	 * or the current post body if set to render None CT.
	 *
	 * @return string
	 * @since 3.2
	 */
	private function get_unprocessed_value_to_render() {
		if ( strtolower( $this->user_atts['view_template'] ) === 'none' ) {
			global $post;
			return ( $post instanceof \WP_Post ) ? $post->post_content : '';
		} else {
			$template_id = \WPV_Content_Template_Embedded::get_template_id_by_name( $this->user_atts['view_template'] );
			return ( 0 === $template_id ) ? '' : get_post_field( 'post_content', $template_id );
		}
	}

	/**
	 * Make sure that the filter on a Content Template output is run.
	 *
	 * This is needed when disabling the WPV_template::the_content filter over the main applied hook.
	 * For historical reasons Content Template outcome has always been filtered over that the_content callback;
	 * however, as we manage the post body shortcode here, that got lost.
	 * This method restates the natural expected behavior.
	 *
	 * @param string $template_content
	 * @param int $template_id
	 * @return string
	 */
	private function force_filter_template_content( $template_content, $template_id ) {
		global $post;
		if ( false === ( $post instanceof \WP_Post ) ) {
			return $template_content;
		}

		return apply_filters( 'wpv_filter_content_template_output', $template_content, $template_id, $post->ID, 'listing-' . $post->post_type );
	}

	/**
	 * Check whether we are rendering data for a post that is password protected.
	 *
	 * @param null|\WP_Post $post
	 * @return bool
	 * @since 3.2
	 */
	private function should_require_post_password( $post ) {
		if ( false === ( $post instanceof \WP_Post ) ) {
			return false;
		}

		return post_password_required( $post );
	}

	/**
	 * Check whether a piece of content has been created using the blocks editor.
	 *
	 * @param string $content
	 * @return bool
	 * @since 3.2
	 */
	private function has_blocks( $content ) {
		if ( function_exists('has_blocks') ) {
			return has_blocks( $content );
		}

		return false;
	}

	/**
	 * Disable the known formatting hooks that might be assigned to a given hook.
	 *
	 * @param string $hook
	 * @return array List of hooks that were removed, by their priorities.
	 * @since 3.2
	 */
	private function maybe_disable_formatting_hooks( $hook ) {
		global $WPV_templates;

		$callbacks_candidates = array(
			'wpautop',
			'shortcode_unautop',
			'gutenberg_wpautop',
			'prepend_attachment',
			array( $WPV_templates, 'the_content' ),
			array( $WPV_templates, 'restore_wpautop' ),
		);
		$callbacks = array();

		foreach ( $callbacks_candidates as $callback ) {
			$priority = has_filter( $hook, $callback );
			if ( false !== $priority ) {
				remove_filter( $hook, $callback, $priority );
				$callbacks[ $priority ] = toolset_getarr( $callbacks, $priority, array() );
				$callbacks[ $priority ][] = $callback;
			}
		}

		return $callbacks;
	}

	/**
	 * Enforce the known formatting hooks that might be missing from a given hook.
	 *
	 * @param string $hook
	 * @return array List of hooks that were nforced, by their priorities.
	 * @since 3.2
	 */
	private function maybe_enforce_formatting_hooks( $hook ) {
		global $WPV_templates;

		$callbacks_candidates = array(
			'1' => array(
				array( $WPV_templates, 'the_content' ),
			),
			'6' => array(
				'gutenberg_wpautop',
			),
			'10' => array(
				'wpautop',
				'shortcode_unautop',
				'prepend_attachment',
			),
			'999' => array(
				array( $WPV_templates, 'restore_wpautop' ),
			),
		);
		$callbacks = array();

		foreach ( $callbacks_candidates as $priority_candidate => $callbacks_list ) {
			foreach ( $callbacks_list as $callback ) {
				$priority = has_filter( $hook, $callback );
				if (
					false === $priority
					&& is_callable( $callback )
				) {
					add_filter( $hook, $callback, $priority_candidate );
					$callbacks[ $priority_candidate ] = toolset_getarr( $callbacks, $priority_candidate, array() );
					$callbacks[ $priority_candidate ][] = $callback;
				}
			}
		}

		return $callbacks;
	}

	/**
	 * Rstore or remove hooks that were removed or enforced, to return to an original state.
	 *
	 * @param string $hook
	 * @param array $callbacks List of hooks to restore or remove, by their priorities.
	 * @param bool $add Whether to add or remove those hooks.
	 * @since 3.2
	 */
	private function maybe_restore_formatting_hooks( $hook, $callbacks, $add = true ) {
		foreach ( $callbacks as $priority => $callbacks_list ) {
			foreach ( $callbacks_list as $callback ) {
				if ( ! is_callable( $callback ) ) {
					continue;
				}
				if ( $add ) {
					add_filter( $hook, $callback, $priority );
				} else {
					remove_filter( $hook, $callback, $priority );
				}
			}
		}
	}
}
Page Not Found
Parece que el enlace que apuntaba aquí no sirve. ¿Quieres probar con una búsqueda?
¡Hola!