cache = new Conditions_Cache();
add_action( 'wp_loaded', [ $this, 'register_conditions' ] ); // After Plugins Registered CPT.
add_action( 'wp_trash_post', [ $this, 'purge_post_from_cache' ] );
add_action( 'untrashed_post', [ $this, 'on_untrash_post' ] );
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
add_action( 'manage_' . Source_Local::CPT . '_posts_columns', [ $this, 'admin_columns_headers' ] );
add_action( 'manage_' . Source_Local::CPT . '_posts_custom_column', [ $this, 'admin_columns_content' ], 10, 2 );
}
public function on_untrash_post( $post_id ) {
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$document = $theme_builder_module->get_document( $post_id );
if ( $document ) {
$conditions = $document->get_meta( '_elementor_conditions' );
if ( $conditions ) {
$this->cache->add( $document, $conditions )->save();
}
}
}
public function admin_columns_headers( $posts_columns ) {
$offset = 3;
$posts_columns = array_slice( $posts_columns, 0, $offset, true ) + [
'instances' => esc_html__( 'Instances', 'elementor-pro' ),
] + array_slice( $posts_columns, $offset, null, true );
return $posts_columns;
}
public function admin_columns_content( $column_name, $post_id ) {
if ( 'instances' !== $column_name ) {
return;
}
$instances = $this->get_document_instances( $post_id );
if ( ! empty( $instances ) ) {
// PHPCS - the method get_document_instances is safe.
echo implode( '
', $instances ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo esc_html__( 'None', 'elementor-pro' );
}
}
/**
* @access public
*
* @param Ajax $ajax_manager
*/
public function register_ajax_actions( $ajax_manager ) {
$ajax_manager->register_ajax_action( 'pro_theme_builder_save_conditions', [ $this, 'ajax_save_theme_template_conditions' ] );
$ajax_manager->register_ajax_action( 'pro_theme_builder_conditions_check_conflicts', [ $this, 'ajax_check_conditions_conflicts' ] );
}
/**
* @throws \Exception
*/
public function ajax_check_conditions_conflicts( $request ) {
$document = Utils::_unstable_get_document_for_edit( $request['editor_post_id'] );
$condition = $request['condition'];
unset( $condition['_id'] );
$condition = rtrim( implode( '/', $condition ), '/' );
$conflicted = array_map( function ( $conflict ) {
return sprintf(
'%s', $conflict['edit_url'], $conflict['template_title']
);
}, $this->get_conditions_conflicts( $document->get_main_id(), $condition ) );
if ( empty( $conflicted ) ) {
return '';
}
return esc_html__( 'Elementor recognized that you have set this location for other templates: ', 'elementor-pro' ) .
' ' .
implode( ', ', $conflicted );
}
public function get_conditions_conflicts_by_location( $condition, $location, $ignore_post_id = null ) {
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$location_settings = $theme_builder_module->get_locations_manager()->get_location( $location );
if ( ! empty( $location_settings['multiple'] ) ) {
return [];
}
$conditions_groups = $this->cache->get_by_location( $location );
$conflicted = [];
if ( ! empty( $conditions_groups ) ) {
foreach ( $conditions_groups as $template_id => $conditions ) {
if ( ! get_post( $template_id ) ) {
$this->purge_post_from_cache( $template_id );
}
if ( $ignore_post_id === $template_id ) {
continue;
}
if ( false !== array_search( $condition, $conditions, true ) ) {
$template_title = esc_html( get_the_title( $template_id ) );
$document = $theme_builder_module->get_document( $template_id );
if ( ! $document instanceof Theme_Document ) {
Plugin::$instance->logger->get_logger()->error( "Error fetching document in conditions manager. Template: $template_title" );
}
$edit_url = isset( $document ) ? $document->get_edit_url() : '';
$conflicted[] = [
'template_id' => $template_id,
'template_title' => $template_title,
'edit_url' => $edit_url,
];
}
}
}
return $conflicted;
}
public function get_conditions_conflicts( $post_id, $condition ) {
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$document = $theme_builder_module->get_document( $post_id );
return $this->get_conditions_conflicts_by_location( $condition, $document->get_location(), $post_id );
}
/**
* @throws \Exception
*/
public function ajax_save_theme_template_conditions( $request ) {
$document = Utils::_unstable_get_document_for_edit( $request['editor_post_id'] );
if ( ! isset( $request['conditions'] ) ) {
$request['conditions'] = [];
}
$is_saved = $this->save_conditions( $document->get_main_id(), $request['conditions'] );
if ( ! $is_saved ) {
throw new \Exception( 'Error while saving conditions.', Exceptions::INTERNAL_SERVER_ERROR );
}
}
private function register_condition( $id, $args = [] ) {
if ( isset( $this->conditions[ $id ] ) ) {
return;
}
$class_name = ucfirst( $id );
$class_name = '\\ElementorPro\\Modules\\ThemeBuilder\\Conditions\\' . $class_name;
/** @var Condition_Base $condition */
$condition = new $class_name( $args );
$this->register_condition_instance( $condition );
foreach ( $condition->get_sub_conditions() as $key => $val ) {
if ( is_numeric( $key ) ) {
$id = $val;
$args = [];
} else {
$id = $key;
$args = $val;
}
$this->register_condition( $id, $args );
}
}
/**
* @param Condition_Base $instance
*/
public function register_condition_instance( $instance ) {
$this->conditions[ $instance->get_name() ] = $instance;
}
/**
* @param $id
*
* @return Condition_Base|bool
*/
public function get_condition( $id ) {
return isset( $this->conditions[ $id ] ) ? $this->conditions[ $id ] : false;
}
public function get_conditions_config() {
$config = [];
foreach ( $this->conditions as $condition ) {
$config[ $condition->get_name() ] = $condition->get_config();
}
return $config;
}
public function get_document_instances( $post_id ) {
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$document = $theme_builder_module->get_document( $post_id );
$summary = [];
if ( ! $document ) {
return $summary;
}
$document_conditions = $this->get_document_conditions( $document );
if ( ! empty( $document_conditions ) ) {
foreach ( $document_conditions as $document_condition ) {
if ( 'exclude' === $document_condition['type'] ) {
continue;
}
$condition_name = ! empty( $document_condition['sub_name'] ) ? $document_condition['sub_name'] : $document_condition['name'];
$condition = $this->get_condition( $condition_name );
if ( ! $condition ) {
continue;
}
if ( ! empty( $document_condition['sub_id'] ) ) {
$instance_label = $condition->get_label() . " #{$document_condition['sub_id']}";
} else {
$instance_label = $condition->get_all_label();
}
$summary[ $condition->get_name() ] = $instance_label;
}
}
return $summary;
}
public function register_conditions() {
$this->register_condition( 'general' );
/**
* Elementor theme conditions registration.
*
* Fires when a new theme condition is registered. This hook allows developers
* to register new theme conditions.
*
* @param Conditions_Manager $this An instance of conditions manager.
*/
do_action( 'elementor/theme/register_conditions', $this );
}
public function save_conditions( $post_id, $conditions ) {
$conditions_to_save = [];
foreach ( $conditions as $condition ) {
unset( $condition['_id'] );
$conditions_to_save[] = rtrim( implode( '/', $condition ), '/' );
}
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$document = $theme_builder_module->get_document( $post_id );
if ( ! $document ) {
return false;
}
if ( empty( $conditions_to_save ) ) {
$is_saved = $document->delete_meta( '_elementor_conditions' );
} else {
$is_saved = $document->update_meta( '_elementor_conditions', $conditions_to_save );
}
$this->cache->regenerate();
return $is_saved;
}
public function get_location_templates( $location ) {
$conditions_priority = [];
$conditions_groups = $this->cache->get_by_location( $location );
if ( empty( $conditions_groups ) ) {
return $conditions_priority;
}
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$location_manager = $theme_builder_module->get_locations_manager();
$excludes = [];
foreach ( $conditions_groups as $theme_template_id => $conditions ) {
/**
* Template ID for theme location templates.
*
* Filters the template ID for theme location templates.
*
* @param int $theme_template_id Template ID.
* @param string $location Theme location.
*/
$theme_template_id = apply_filters( 'elementor/theme/get_location_templates/template_id', $theme_template_id, $location );
foreach ( $conditions as $condition ) {
$parsed_condition = $this->parse_condition( $condition );
$include = $parsed_condition['type'];
$name = $parsed_condition['name'];
$sub_name = $parsed_condition['sub_name'];
$sub_id = $parsed_condition['sub_id'];
$is_include = 'include' === $include;
$condition_instance = $this->get_condition( $name );
if ( ! $condition_instance ) {
continue;
}
$condition_pass = $condition_instance->check( [] );
$sub_condition_instance = null;
if ( $condition_pass && $sub_name ) {
$sub_condition_instance = $this->get_condition( $sub_name );
if ( ! $sub_condition_instance ) {
continue;
}
$args = [
'id' => apply_filters( 'elementor/theme/get_location_templates/condition_sub_id', $sub_id, $parsed_condition ),
];
$condition_pass = $sub_condition_instance->check( $args );
}
if ( $condition_pass ) {
$post_status = get_post_status( $theme_template_id );
if ( 'publish' !== $post_status ) {
$location_manager->inspector_log( [
'location' => $location,
'document' => $theme_builder_module->get_document( $theme_template_id ),
'description' => 'Skipped, is not Published',
] );
continue;
}
if ( $is_include ) {
$conditions_priority[ $theme_template_id ] = $this->get_condition_priority( $condition_instance, $sub_condition_instance, $sub_id );
} else {
$excludes[] = $theme_template_id;
}
}
} // End foreach().
} // End foreach().
foreach ( $excludes as $exclude_id ) {
unset( $conditions_priority[ $exclude_id ] );
}
asort( $conditions_priority );
return $conditions_priority;
}
public function get_theme_templates_ids( $location ) {
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$location_manager = $theme_builder_module->get_locations_manager();
// In case the user want to preview any page with a theme_template_id,
// like http://domain.com/any-post/?preview=1&theme_template_id=6453
$force_template_id = Utils::_unstable_get_super_global_value( $_GET, 'theme_template_id' );
if ( $force_template_id ) {
$document = $theme_builder_module->get_document( $force_template_id );
// e.g. header / header
if ( $document && $location === $document->get_location() ) {
$location_manager->inspector_log( [
'location' => $location,
'document' => $document,
'description' => 'Force Template by URL param',
] );
return [
$force_template_id => 1,
];
}
}
$current_post_id = get_the_ID();
$document = $theme_builder_module->get_document( $current_post_id );
if ( $document && $location === $document->get_location() ) {
$location_manager->inspector_log( [
'location' => $location,
'document' => $document,
'description' => 'Current Edited Template',
] );
return [
$current_post_id => 1,
];
}
$templates = $this->get_location_templates( $location );
return $templates;
}
/**
* @param Condition_Base $condition_instance
* @param Condition_Base $sub_condition_instance
* @param int $sub_id
*
* @return mixed
* @throws \Exception
*/
private function get_condition_priority( $condition_instance, $sub_condition_instance, $sub_id ) {
$priority = $condition_instance::get_priority();
if ( $sub_condition_instance ) {
if ( $sub_condition_instance::get_priority() < $priority ) {
$priority = $sub_condition_instance::get_priority();
}
$priority -= 10;
if ( $sub_id ) {
$priority -= 10;
} elseif ( 0 === count( $sub_condition_instance->get_sub_conditions() ) ) {
// if no sub conditions - it's more specific.
$priority -= 5;
}
}
return $priority;
}
/**
* @param Theme_Document $document
*
* @return array
*/
public function get_document_conditions( $document ) {
$saved_conditions = $document->get_main_meta( '_elementor_conditions' );
$conditions = [];
if ( is_array( $saved_conditions ) ) {
foreach ( $saved_conditions as $condition ) {
$conditions[] = $this->parse_condition( $condition );
}
}
return $conditions;
}
protected function parse_condition( $condition ) {
list ( $type, $name, $sub_name, $sub_id ) = array_pad( explode( '/', $condition ), 4, '' );
return compact( 'type', 'name', 'sub_name', 'sub_id' );
}
/**
* @param $location
*
* @return Theme_Document[]
*/
public function get_documents_for_location( $location ) {
if ( isset( $this->location_cache[ $location ] ) ) {
return $this->location_cache[ $location ];
}
$theme_templates_ids = $this->get_theme_templates_ids( $location );
/** @var Module $theme_builder_module */
$theme_builder_module = Module::instance();
$location_settings = $theme_builder_module->get_locations_manager()->get_location( $location );
$documents = [];
foreach ( $theme_templates_ids as $theme_template_id => $priority ) {
$document = $theme_builder_module->get_document( $theme_template_id );
if ( $document ) {
$documents[ $theme_template_id ] = $document;
}
if ( empty( $location_settings['multiple'] ) ) {
break;
}
}
$this->location_cache[ $location ] = $documents;
return $documents;
}
public function purge_post_from_cache( $post_id ) {
return $this->cache->remove( $post_id )->save();
}
public function get_cache() {
return $this->cache;
}
public function clear_cache() {
$this->cache->clear();
}
public function clear_location_cache() {
$this->location_cache = [];
}
}