HEX
Server: LiteSpeed
System: Linux node612.namehero.net 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64
User: dfwparty (1186)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/dfwparty/www/wp-content/plugins/ultimate-elementor/abilities/trait-uael-abilities-helpers.php
<?php
/**
 * Shared Abilities Helpers.
 *
 * Provides common utilities used across multiple ability classes:
 * template validation, theme compatibility resolution, and location normalization.
 *
 * @package UAEL
 * @since 1.45.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Trait UAEL_Abilities_Helpers
 *
 * Shared helper methods for HFE ability classes.
 *
 * @since 1.45.0
 */
trait UAEL_Abilities_Helpers {

	/**
	 * Validate that a post ID belongs to the elementor-hf CPT.
	 *
	 * @since 1.45.0
	 *
	 * @param int $post_id Post ID to validate.
	 * @return WP_Post|WP_Error The post object or error.
	 */
	protected function validate_template( $post_id ) {
		$post = get_post( $post_id );

		if ( ! $post || 'elementor-hf' !== $post->post_type ) {
			return new WP_Error(
				'uael_invalid_template',
				__( 'Template not found or is not an HFE template.', 'uael' ),
				array( 'status' => 404 )
			);
		}

		return $post;
	}

	/**
	 * Validate that a post ID belongs to any Elementor-enabled post type.
	 *
	 * Accepts pages, posts, HFE templates, and any custom post type that
	 * Elementor supports (configured in Elementor > Settings > General).
	 *
	 * @since 1.45.0
	 *
	 * @param int $post_id Post ID to validate.
	 * @return WP_Post|WP_Error The post object or error.
	 */
	protected function validate_elementor_post( $post_id ) {
		$post = get_post( $post_id );

		if ( ! $post ) {
			return new WP_Error(
				'uael_post_not_found',
				__( 'Post not found.', 'uael' ),
				array( 'status' => 404 )
			);
		}

		$is_supported = false;

		// Always accept HFE templates.
		if ( 'elementor-hf' === $post->post_type ) {
			$is_supported = true;
		} else {
			// Accept any post type Elementor supports.
			$supported_cpts = get_option( 'elementor_cpt_support', array( 'page', 'post' ) );

			if ( is_array( $supported_cpts ) && in_array( $post->post_type, $supported_cpts, true ) ) {
				$is_supported = true;
			} elseif ( 'builder' === get_post_meta( $post->ID, '_elementor_edit_mode', true ) ) {
				// Accept any post already edited with Elementor.
				$is_supported = true;
			}
		}

		if ( ! $is_supported ) {
			return new WP_Error(
				'uael_unsupported_post_type',
				sprintf(
					/* translators: %s: post type slug */
					__( 'Post type "%s" is not supported by Elementor.', 'uael' ),
					$post->post_type
				),
				array( 'status' => 400 )
			);
		}

		// Object-level authorization: a capability check on the ability is not
		// enough — verify the current user is allowed to edit THIS specific post.
		// `edit_post` is a meta capability that respects per-object ownership,
		// so an Author cannot tamper with pages/templates authored by others.
		if ( ! current_user_can( 'edit_post', $post->ID ) ) {
			return new WP_Error(
				'uael_forbidden',
				__( 'You are not allowed to edit this post.', 'uael' ),
				array( 'status' => 403 )
			);
		}

		return $post;
	}

	/**
	 * Resolve the current theme compatibility method.
	 *
	 * Reads the uael_is_theme_supported and uael_compatibility_option options
	 * to determine whether the theme uses native support, CSS override, or default fallback.
	 *
	 * @since 1.45.0
	 *
	 * @return string 'native', 'css-override', or 'default'.
	 */
	protected function resolve_compat_method() {
		$theme_supported = get_option( 'uael_is_theme_supported', false );
		$compat_option   = get_option( 'uael_compatibility_option', '1' );

		if ( $theme_supported ) {
			return 'native';
		}

		return '2' === $compat_option ? 'css-override' : 'default';
	}

	/**
	 * Normalize location meta into a consistent object structure.
	 *
	 * Post meta stores locations as { rule: [...], specific: [...] }.
	 * Ensures both keys exist with proper array values for schema compliance.
	 *
	 * @since 1.45.0
	 *
	 * @param mixed $locations Raw meta value.
	 * @return array Normalized location object with rule and specific keys.
	 */
	protected function normalize_locations( $locations ) {
		if ( empty( $locations ) || ! is_array( $locations ) ) {
			return array(
				'rule'     => array(),
				'specific' => array(),
			);
		}

		return array(
			'rule'     => ! empty( $locations['rule'] ) && is_array( $locations['rule'] )
				? array_values( $locations['rule'] )
				: array(),
			'specific' => ! empty( $locations['specific'] ) && is_array( $locations['specific'] )
				? array_values( $locations['specific'] )
				: array(),
		);
	}

	/**
	 * Guard write operations behind the AI Tools "allow modifications" setting.
	 *
	 * Shared by every handler that creates, edits, or deletes content so the
	 * check stays in one place. Returns boolean true when modifications are
	 * permitted; callers should bail with the returned WP_Error otherwise.
	 *
	 * @since 1.45.0
	 *
	 * @return true|WP_Error True when allowed, WP_Error (403) when disabled.
	 */
	protected function check_modifications_allowed() {
		$settings = get_option( 'uae_mcp_settings', array() );

		if ( empty( $settings['allow_modifications'] ) ) {
			return new \WP_Error(
				'uael_modifications_disabled',
				__( 'Modifications disabled in AI Tools settings.', 'uael' ),
				array( 'status' => 403 )
			);
		}

		return true;
	}
}