first commit
This commit is contained in:
617
wp-content/plugins/elementor/core/admin/admin-notices.php
Normal file
617
wp-content/plugins/elementor/core/admin/admin-notices.php
Normal file
@@ -0,0 +1,617 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Admin;
|
||||
|
||||
use Elementor\Api;
|
||||
use Elementor\Core\Admin\UI\Components\Button;
|
||||
use Elementor\Core\Base\Module;
|
||||
use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Settings;
|
||||
use Elementor\Tracker;
|
||||
use Elementor\User;
|
||||
use Elementor\Utils;
|
||||
use Elementor\Core\Admin\Notices\Base_Notice;
|
||||
use Elementor\Core\Admin\Notices\Elementor_Dev_Notice;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Admin_Notices extends Module {
|
||||
|
||||
const DEFAULT_EXCLUDED_PAGES = [ 'plugins.php', 'plugin-install.php', 'plugin-editor.php' ];
|
||||
|
||||
private $plain_notices = [
|
||||
'api_notice',
|
||||
'api_upgrade_plugin',
|
||||
'tracker',
|
||||
'rate_us_feedback',
|
||||
'role_manager_promote',
|
||||
'experiment_promotion',
|
||||
'design_not_appearing',
|
||||
'plugin_image_optimization',
|
||||
];
|
||||
|
||||
private $elementor_pages_count = null;
|
||||
|
||||
private $install_time = null;
|
||||
|
||||
private $current_screen_id = null;
|
||||
|
||||
private function get_notices() {
|
||||
$notices = [
|
||||
new Elementor_Dev_Notice(),
|
||||
];
|
||||
|
||||
/**
|
||||
* Admin notices.
|
||||
*
|
||||
* Filters Elementor admin notices.
|
||||
*
|
||||
* This hook can be used by external developers to manage existing
|
||||
* admin notice or to add new notices for Elementor add-ons.
|
||||
*
|
||||
* @param array $notices A list of notice classes.
|
||||
*/
|
||||
$notices = apply_filters( 'elementor/core/admin/notices', $notices );
|
||||
|
||||
return $notices;
|
||||
}
|
||||
|
||||
private function get_install_time() {
|
||||
if ( null === $this->install_time ) {
|
||||
$this->install_time = Plugin::$instance->get_install_time();
|
||||
}
|
||||
|
||||
return $this->install_time;
|
||||
}
|
||||
|
||||
private function get_elementor_pages_count() {
|
||||
if ( null === $this->elementor_pages_count ) {
|
||||
$elementor_pages = new \WP_Query( [
|
||||
'no_found_rows' => true,
|
||||
'post_type' => 'any',
|
||||
'post_status' => 'publish',
|
||||
'fields' => 'ids',
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'meta_key' => '_elementor_edit_mode',
|
||||
'meta_value' => 'builder',
|
||||
] );
|
||||
|
||||
$this->elementor_pages_count = $elementor_pages->post_count;
|
||||
}
|
||||
|
||||
return $this->elementor_pages_count;
|
||||
}
|
||||
|
||||
private function notice_api_upgrade_plugin() {
|
||||
$upgrade_notice = Api::get_upgrade_notice();
|
||||
if ( empty( $upgrade_notice ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'update_plugins' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for upgrades.
|
||||
$update_plugins = get_site_transient( 'update_plugins' );
|
||||
|
||||
$has_remote_update_package = ! ( empty( $update_plugins ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ] ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ]->package ) );
|
||||
|
||||
if ( ! $has_remote_update_package && empty( $upgrade_notice['update_link'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $has_remote_update_package ) {
|
||||
$product = $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ];
|
||||
|
||||
$details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $product->slug . '§ion=changelog&TB_iframe=true&width=600&height=800' );
|
||||
$upgrade_url = wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' . ELEMENTOR_PLUGIN_BASE ), 'upgrade-plugin_' . ELEMENTOR_PLUGIN_BASE );
|
||||
$new_version = $product->new_version;
|
||||
} else {
|
||||
$upgrade_url = $upgrade_notice['update_link'];
|
||||
$details_url = $upgrade_url;
|
||||
|
||||
$new_version = $upgrade_notice['version'];
|
||||
}
|
||||
|
||||
// Check if upgrade messages should be shown.
|
||||
if ( version_compare( ELEMENTOR_VERSION, $upgrade_notice['version'], '>=' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$notice_id = 'upgrade_notice_' . $upgrade_notice['version'];
|
||||
if ( User::is_user_notice_viewed( $notice_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
/* translators: 1: Details URL, 2: Accessibility text, 3: Version number, 4: Update URL, 5: Accessibility text. */
|
||||
__( 'There is a new version of Elementor Page Builder available. <a href="%1$s" class="thickbox open-plugin-details-modal" aria-label="%2$s">View version %3$s details</a> or <a href="%4$s" class="update-link" aria-label="%5$s">update now</a>.', 'elementor' ),
|
||||
esc_url( $details_url ),
|
||||
esc_attr( sprintf(
|
||||
/* translators: %s: Elementor version. */
|
||||
__( 'View Elementor version %s details', 'elementor' ),
|
||||
$new_version
|
||||
) ),
|
||||
$new_version,
|
||||
esc_url( $upgrade_url ),
|
||||
esc_attr( esc_html__( 'Update Now', 'elementor' ) )
|
||||
);
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'Update Notification', 'elementor' ),
|
||||
'description' => $message,
|
||||
'button' => [
|
||||
'icon_classes' => 'dashicons dashicons-update',
|
||||
'text' => esc_html__( 'Update Now', 'elementor' ),
|
||||
'url' => $upgrade_url,
|
||||
],
|
||||
'id' => $notice_id,
|
||||
];
|
||||
|
||||
$this->print_admin_notice( $options );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function notice_api_notice() {
|
||||
$admin_notice = Api::get_admin_notice();
|
||||
if ( empty( $admin_notice ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$notice_id = 'admin_notice_api_' . $admin_notice['notice_id'];
|
||||
if ( User::is_user_notice_viewed( $notice_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'Update Notification', 'elementor' ),
|
||||
'description' => $admin_notice['notice_text'],
|
||||
'id' => $notice_id,
|
||||
];
|
||||
|
||||
$this->print_admin_notice( $options );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function notice_tracker() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show tracker notice after 24 hours from installed time.
|
||||
if ( strtotime( '+24 hours', $this->get_install_time() ) > time() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( '1' === get_option( 'elementor_tracker_notice' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( Tracker::is_allow_track() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 2 > $this->get_elementor_pages_count() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Skip for development env.
|
||||
$optin_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_into' ), 'opt_into' );
|
||||
$optout_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_out' ), 'opt_out' );
|
||||
|
||||
$tracker_description_text = esc_html__( 'Become a super contributor by opting in to share non-sensitive plugin data and to receive periodic email updates from us.', 'elementor' );
|
||||
|
||||
/**
|
||||
* Tracker admin description text.
|
||||
*
|
||||
* Filters the admin notice text for non-sensitive data collection.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $tracker_description_text Description text displayed in admin notice.
|
||||
*/
|
||||
$tracker_description_text = apply_filters( 'elementor/tracker/admin_description_text', $tracker_description_text );
|
||||
|
||||
$message = esc_html( $tracker_description_text ) . ' <a href="https://go.elementor.com/usage-data-tracking/" target="_blank">' . esc_html__( 'Learn more.', 'elementor' ) . '</a>';
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'Love using Elementor?', 'elementor' ),
|
||||
'description' => $message,
|
||||
'button' => [
|
||||
'text' => esc_html__( 'Sure! I\'d love to help', 'elementor' ),
|
||||
'url' => $optin_url,
|
||||
'type' => 'cta',
|
||||
],
|
||||
'button_secondary' => [
|
||||
'text' => esc_html__( 'No thanks', 'elementor' ),
|
||||
'url' => $optout_url,
|
||||
'variant' => 'outline',
|
||||
'type' => 'cta',
|
||||
],
|
||||
];
|
||||
|
||||
$this->print_admin_notice( $options );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function notice_rate_us_feedback() {
|
||||
$notice_id = 'rate_us_feedback';
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'dashboard' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 10 >= $this->get_elementor_pages_count() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dismiss_url = add_query_arg( [
|
||||
'action' => 'elementor_set_admin_notice_viewed',
|
||||
'notice_id' => esc_attr( $notice_id ),
|
||||
], admin_url( 'admin-post.php' ) );
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'Congrats!', 'elementor' ),
|
||||
'description' => esc_html__( 'You created over 10 pages with Elementor. Great job! If you can spare a minute, please help us by leaving a five star review on WordPress.org.', 'elementor' ),
|
||||
'id' => $notice_id,
|
||||
'button' => [
|
||||
'text' => esc_html__( 'Happy To Help', 'elementor' ),
|
||||
'url' => 'https://go.elementor.com/admin-review/',
|
||||
'new_tab' => true,
|
||||
'type' => 'cta',
|
||||
],
|
||||
'button_secondary' => [
|
||||
'text' => esc_html__( 'Hide Notification', 'elementor' ),
|
||||
'classes' => [ 'e-notice-dismiss' ],
|
||||
'url' => esc_url_raw( $dismiss_url ),
|
||||
'new_tab' => true,
|
||||
'type' => 'cta',
|
||||
],
|
||||
];
|
||||
|
||||
$this->print_admin_notice( $options );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function notice_role_manager_promote() {
|
||||
$notice_id = 'role_manager_promote';
|
||||
|
||||
if ( Utils::has_pro() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( 'elementor_page_elementor-role-manager' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$users = new \WP_User_Query( [
|
||||
'fields' => 'ID',
|
||||
'number' => 10,
|
||||
] );
|
||||
|
||||
if ( 5 > $users->get_total() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'Managing a multi-user site?', 'elementor' ),
|
||||
'description' => esc_html__( 'With Elementor Pro, you can control user access and make sure no one messes up your design.', 'elementor' ),
|
||||
'id' => $notice_id,
|
||||
|
||||
'button' => [
|
||||
'text' => esc_html__( 'Learn More', 'elementor' ),
|
||||
'url' => 'https://go.elementor.com/plugin-promotion-role-manager/',
|
||||
'new_tab' => true,
|
||||
'type' => 'cta',
|
||||
],
|
||||
];
|
||||
|
||||
$options = Filtered_Promotions_Manager::get_filtered_promotion_data( $options, 'core/admin/notice_role_manager_promote', 'button', 'url' );
|
||||
|
||||
$this->print_admin_notice( $options );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function notice_experiment_promotion() {
|
||||
$notice_id = 'experiment_promotion';
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$experiments = Plugin::$instance->experiments;
|
||||
$is_all_performance_features_active = (
|
||||
$experiments->is_feature_active( 'e_lazyload' ) &&
|
||||
$experiments->is_feature_active( 'e_optimized_css_loading' ) &&
|
||||
$experiments->is_feature_active( 'e_font_icon_svg' )
|
||||
);
|
||||
|
||||
if ( $is_all_performance_features_active ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'Improve your site’s performance score.', 'elementor' ),
|
||||
'description' => esc_html__( 'With our experimental speed boosting features you can go faster than ever before. Look for the Performance label on our Experiments page and activate those experiments to improve your site loading speed.', 'elementor' ),
|
||||
'id' => $notice_id,
|
||||
'button' => [
|
||||
'text' => esc_html__( 'Try it out', 'elementor' ),
|
||||
'url' => Settings::get_settings_tab_url( 'experiments' ),
|
||||
'type' => 'cta',
|
||||
],
|
||||
'button_secondary' => [
|
||||
'text' => esc_html__( 'Learn more', 'elementor' ),
|
||||
'url' => 'https://go.elementor.com/wp-dash-experiment-promotion/',
|
||||
'new_tab' => true,
|
||||
'type' => 'cta',
|
||||
],
|
||||
];
|
||||
|
||||
$this->print_admin_notice( $options );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function notice_design_not_appearing() {
|
||||
$installs_history = get_option( 'elementor_install_history', [] );
|
||||
$is_first_install = 1 === count( $installs_history );
|
||||
|
||||
if ( $is_first_install || ! current_user_can( 'update_plugins' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$notice_id = 'design_not_appearing';
|
||||
$notice = User::get_user_notices()[ $notice_id ] ?? [];
|
||||
$notice_version = $notice['meta']['version'] ?? null;
|
||||
$is_version_changed = $this->get_elementor_version() !== $notice_version;
|
||||
|
||||
if ( $is_version_changed ) {
|
||||
User::set_user_notice( $notice_id, false, [ 'version' => $this->get_elementor_version() ] );
|
||||
}
|
||||
|
||||
if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard', 'update-core', 'plugins' ], true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( User::is_user_notice_viewed( $notice_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'The version was updated successfully!', 'elementor' ),
|
||||
'description' => sprintf(
|
||||
esc_html__( 'Encountering issues after updating the version? Don’t worry - we’ve collected all the fixes for troubleshooting common issues. %1$sFind a solution%2$s', 'elementor' ),
|
||||
'<a href="https://go.elementor.com/wp-dash-changes-do-not-appear-online/" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'id' => $notice_id,
|
||||
];
|
||||
|
||||
$excluded_pages = [];
|
||||
$this->print_admin_notice( $options, $excluded_pages );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// For testing purposes
|
||||
public function get_elementor_version() {
|
||||
return ELEMENTOR_VERSION;
|
||||
}
|
||||
|
||||
private function notice_plugin_image_optimization() {
|
||||
$notice_id = 'plugin_image_optimization';
|
||||
|
||||
if ( 'upload' !== $this->current_screen_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$attachments = new \WP_Query( [
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'any',
|
||||
'fields' => 'ids',
|
||||
] );
|
||||
|
||||
if ( 1 > $attachments->found_posts ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$plugin_file_path = 'image-optimization/image-optimization.php';
|
||||
$plugin_slug = 'image-optimization';
|
||||
|
||||
if ( is_plugin_active( $plugin_file_path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->is_plugin_installed( $plugin_file_path ) ) {
|
||||
$url = wp_nonce_url( 'plugins.php?action=activate&plugin=' . $plugin_file_path . '&plugin_status=all&paged=1&s', 'activate-plugin_' . $plugin_file_path );
|
||||
$cta_text = esc_html__( 'Activate Plugin', 'elementor' );
|
||||
} else {
|
||||
$url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
|
||||
$cta_text = esc_html__( 'Install Plugin', 'elementor' );
|
||||
}
|
||||
|
||||
$options = [
|
||||
'title' => esc_html__( 'Speed up your website with Image Optimizer by Elementor', 'elementor' ),
|
||||
'description' => esc_html__( 'Automatically compress and optimize images, resize larger files, or convert to WebP. Optimize images individually, in bulk, or on upload.', 'elementor' ),
|
||||
'id' => $notice_id,
|
||||
'type' => 'cta',
|
||||
'button_secondary' => [
|
||||
'text' => $cta_text,
|
||||
'url' => $url,
|
||||
'type' => 'cta',
|
||||
],
|
||||
];
|
||||
|
||||
$this->print_admin_notice( $options );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function is_plugin_installed( $file_path ): bool {
|
||||
$installed_plugins = get_plugins();
|
||||
|
||||
return isset( $installed_plugins[ $file_path ] );
|
||||
}
|
||||
|
||||
public function print_admin_notice( array $options, $exclude_pages = self::DEFAULT_EXCLUDED_PAGES ) {
|
||||
global $pagenow;
|
||||
|
||||
if ( in_array( $pagenow, $exclude_pages, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$default_options = [
|
||||
'id' => null,
|
||||
'title' => '',
|
||||
'description' => '',
|
||||
'classes' => [ 'notice', 'e-notice' ], // We include WP's default notice class so it will be properly handled by WP's js handler
|
||||
'type' => '',
|
||||
'dismissible' => true,
|
||||
'icon' => 'eicon-elementor',
|
||||
'button' => [],
|
||||
'button_secondary' => [],
|
||||
];
|
||||
|
||||
$options = array_replace_recursive( $default_options, $options );
|
||||
|
||||
$notice_classes = $options['classes'];
|
||||
$dismiss_button = '';
|
||||
$icon = '';
|
||||
|
||||
if ( $options['type'] ) {
|
||||
$notice_classes[] = 'e-notice--' . $options['type'];
|
||||
}
|
||||
|
||||
if ( $options['dismissible'] ) {
|
||||
$label = esc_html__( 'Dismiss this notice.', 'elementor' );
|
||||
$notice_classes[] = 'e-notice--dismissible';
|
||||
$dismiss_button = '<i class="e-notice__dismiss" role="button" aria-label="' . $label . '" tabindex="0"></i>';
|
||||
}
|
||||
|
||||
if ( $options['icon'] ) {
|
||||
$notice_classes[] = 'e-notice--extended';
|
||||
$icon = '<div class="e-notice__icon-wrapper"><i class="' . esc_attr( $options['icon'] ) . '" aria-hidden="true"></i></div>';
|
||||
}
|
||||
|
||||
$wrapper_attributes = [
|
||||
'class' => $notice_classes,
|
||||
];
|
||||
|
||||
if ( $options['id'] ) {
|
||||
$wrapper_attributes['data-notice_id'] = $options['id'];
|
||||
}
|
||||
?>
|
||||
<div <?php Utils::print_html_attributes( $wrapper_attributes ); ?>>
|
||||
<?php echo $dismiss_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<div class="e-notice__aside">
|
||||
<?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
</div>
|
||||
<div class="e-notice__content">
|
||||
<?php if ( $options['title'] ) { ?>
|
||||
<h3><?php echo wp_kses_post( $options['title'] ); ?></h3>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ( $options['description'] ) { ?>
|
||||
<p><?php echo wp_kses_post( $options['description'] ); ?></p>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ( ! empty( $options['button']['text'] ) || ! empty( $options['button_secondary']['text'] ) ) { ?>
|
||||
<div class="e-notice__actions">
|
||||
<?php
|
||||
foreach ( [ $options['button'], $options['button_secondary'] ] as $index => $button_settings ) {
|
||||
if ( empty( $button_settings['variant'] ) && $index ) {
|
||||
$button_settings['variant'] = 'outline';
|
||||
}
|
||||
|
||||
if ( empty( $button_settings['text'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$button = new Button( $button_settings );
|
||||
$button->print_button();
|
||||
} ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
|
||||
public function admin_notices() {
|
||||
$this->install_time = Plugin::$instance->get_install_time();
|
||||
$this->current_screen_id = get_current_screen()->id;
|
||||
|
||||
foreach ( $this->plain_notices as $notice ) {
|
||||
$method_callback = "notice_{$notice}";
|
||||
if ( $this->$method_callback() ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Base_Notice $notice_instance */
|
||||
foreach ( $this->get_notices() as $notice_instance ) {
|
||||
if ( ! $notice_instance->should_print() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->print_admin_notice( $notice_instance->get_config() );
|
||||
|
||||
// It exits the method to make sure it prints only one notice.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.9.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_notices', [ $this, 'admin_notices' ], 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module name.
|
||||
*
|
||||
* Retrieve the module name.
|
||||
*
|
||||
* @since 2.9.0
|
||||
* @access public
|
||||
*
|
||||
* @return string Module name.
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'admin-notices';
|
||||
}
|
||||
}
|
||||
1010
wp-content/plugins/elementor/core/admin/admin.php
Normal file
1010
wp-content/plugins/elementor/core/admin/admin.php
Normal file
File diff suppressed because it is too large
Load Diff
190
wp-content/plugins/elementor/core/admin/canary-deployment.php
Normal file
190
wp-content/plugins/elementor/core/admin/canary-deployment.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Admin;
|
||||
|
||||
use Elementor\Api;
|
||||
use Elementor\Core\Base\Module;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
// TODO: Move this class to pro version for better architecture.
|
||||
class Canary_Deployment extends Module {
|
||||
|
||||
const CURRENT_VERSION = ELEMENTOR_VERSION;
|
||||
const PLUGIN_BASE = ELEMENTOR_PLUGIN_BASE;
|
||||
|
||||
private $canary_deployment_info = null;
|
||||
|
||||
/**
|
||||
* Get module name.
|
||||
*
|
||||
* Retrieve the module name.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @access public
|
||||
*
|
||||
* @return string Module name.
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'canary-deployment';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check version.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @access public
|
||||
*
|
||||
* @param object $transient Plugin updates data.
|
||||
*
|
||||
* @return object Plugin updates data.
|
||||
*/
|
||||
public function check_version( $transient ) {
|
||||
// First transient before the real check.
|
||||
if ( ! isset( $transient->response ) ) {
|
||||
return $transient;
|
||||
}
|
||||
|
||||
// Placeholder
|
||||
$stable_version = '0.0.0';
|
||||
|
||||
if ( ! empty( $transient->response[ static::PLUGIN_BASE ]->new_version ) ) {
|
||||
$stable_version = $transient->response[ static::PLUGIN_BASE ]->new_version;
|
||||
}
|
||||
|
||||
if ( null === $this->canary_deployment_info ) {
|
||||
$this->canary_deployment_info = $this->get_canary_deployment_info();
|
||||
}
|
||||
|
||||
// Can be false - if canary version is not available.
|
||||
if ( empty( $this->canary_deployment_info ) ) {
|
||||
return $transient;
|
||||
}
|
||||
|
||||
if ( ! version_compare( $this->canary_deployment_info['new_version'], $stable_version, '>' ) ) {
|
||||
return $transient;
|
||||
}
|
||||
|
||||
$canary_deployment_info = $this->canary_deployment_info;
|
||||
|
||||
// Most of plugin info comes from the $transient but on first check - the response is empty.
|
||||
if ( ! empty( $transient->response[ static::PLUGIN_BASE ] ) ) {
|
||||
$canary_deployment_info = array_merge( (array) $transient->response[ static::PLUGIN_BASE ], $canary_deployment_info );
|
||||
}
|
||||
|
||||
$transient->response[ static::PLUGIN_BASE ] = (object) $canary_deployment_info;
|
||||
|
||||
return $transient;
|
||||
}
|
||||
|
||||
protected function get_canary_deployment_remote_info( $force ) {
|
||||
return Api::get_canary_deployment_info( $force );
|
||||
}
|
||||
|
||||
private function get_canary_deployment_info() {
|
||||
global $pagenow;
|
||||
|
||||
$force = 'update-core.php' === $pagenow && isset( $_GET['force-check'] );
|
||||
|
||||
$canary_deployment = $this->get_canary_deployment_remote_info( $force );
|
||||
|
||||
if ( empty( $canary_deployment['plugin_info']['new_version'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$canary_version = $canary_deployment['plugin_info']['new_version'];
|
||||
|
||||
if ( version_compare( $canary_version, static::CURRENT_VERSION, '<=' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! empty( $canary_deployment['conditions'] ) && ! $this->check_conditions( $canary_deployment['conditions'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $canary_deployment['plugin_info'];
|
||||
}
|
||||
|
||||
private function check_conditions( $groups ) {
|
||||
foreach ( $groups as $group ) {
|
||||
if ( $this->check_group( $group ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function check_group( $group ) {
|
||||
$is_or_relation = ! empty( $group['relation'] ) && 'OR' === $group['relation'];
|
||||
unset( $group['relation'] );
|
||||
$result = false;
|
||||
|
||||
foreach ( $group as $condition ) {
|
||||
// Reset results for each condition.
|
||||
$result = false;
|
||||
switch ( $condition['type'] ) {
|
||||
case 'wordpress': // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled
|
||||
// include an unmodified $wp_version
|
||||
include ABSPATH . WPINC . '/version.php';
|
||||
$result = version_compare( $wp_version, $condition['version'], $condition['operator'] );
|
||||
break;
|
||||
case 'multisite':
|
||||
$result = is_multisite() === $condition['multisite'];
|
||||
break;
|
||||
case 'language':
|
||||
$in_array = in_array( get_locale(), $condition['languages'], true );
|
||||
$result = 'in' === $condition['operator'] ? $in_array : ! $in_array;
|
||||
break;
|
||||
case 'plugin':
|
||||
if ( ! empty( $condition['plugin_file'] ) ) {
|
||||
$plugin_file = $condition['plugin_file']; // For PHP Unit tests.
|
||||
} else {
|
||||
$plugin_file = WP_PLUGIN_DIR . '/' . $condition['plugin']; // Default.
|
||||
}
|
||||
|
||||
$version = '';
|
||||
|
||||
if ( is_plugin_active( $condition['plugin'] ) && file_exists( $plugin_file ) ) {
|
||||
$plugin_data = get_plugin_data( $plugin_file );
|
||||
if ( isset( $plugin_data['Version'] ) ) {
|
||||
$version = $plugin_data['Version'];
|
||||
}
|
||||
}
|
||||
|
||||
$result = version_compare( $version, $condition['version'], $condition['operator'] );
|
||||
break;
|
||||
case 'theme':
|
||||
$theme = wp_get_theme();
|
||||
if ( wp_get_theme()->parent() ) {
|
||||
$theme = wp_get_theme()->parent();
|
||||
}
|
||||
|
||||
if ( $theme->get_template() === $condition['theme'] ) {
|
||||
$version = $theme->version;
|
||||
} else {
|
||||
$version = '';
|
||||
}
|
||||
|
||||
$result = version_compare( $version, $condition['version'], $condition['operator'] );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ( ( $is_or_relation && $result ) || ( ! $is_or_relation && ! $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.6.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_version' ] );
|
||||
}
|
||||
}
|
||||
192
wp-content/plugins/elementor/core/admin/feedback.php
Normal file
192
wp-content/plugins/elementor/core/admin/feedback.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Admin;
|
||||
|
||||
use Elementor\Api;
|
||||
use Elementor\Core\Base\Module;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tracker;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Feedback extends Module {
|
||||
|
||||
/**
|
||||
* @since 2.2.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'current_screen', function () {
|
||||
if ( ! $this->is_plugins_screen() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_feedback_dialog_scripts' ] );
|
||||
} );
|
||||
|
||||
// Ajax.
|
||||
add_action( 'wp_ajax_elementor_deactivate_feedback', [ $this, 'ajax_elementor_deactivate_feedback' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module name.
|
||||
*
|
||||
* Retrieve the module name.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*
|
||||
* @return string Module name.
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'feedback';
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue feedback dialog scripts.
|
||||
*
|
||||
* Registers the feedback dialog scripts and enqueues them.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function enqueue_feedback_dialog_scripts() {
|
||||
add_action( 'admin_footer', [ $this, 'print_deactivate_feedback_dialog' ] );
|
||||
|
||||
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
|
||||
|
||||
wp_register_script(
|
||||
'elementor-admin-feedback',
|
||||
ELEMENTOR_ASSETS_URL . 'js/admin-feedback' . $suffix . '.js',
|
||||
[
|
||||
'elementor-common',
|
||||
'wp-i18n',
|
||||
],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script( 'elementor-admin-feedback' );
|
||||
|
||||
wp_set_script_translations( 'elementor-admin-feedback', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @deprecated 3.1.0
|
||||
*/
|
||||
public function localize_feedback_dialog_settings() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' );
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Print deactivate feedback dialog.
|
||||
*
|
||||
* Display a dialog box to ask the user why he deactivated Elementor.
|
||||
*
|
||||
* Fired by `admin_footer` filter.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function print_deactivate_feedback_dialog() {
|
||||
$deactivate_reasons = [
|
||||
'no_longer_needed' => [
|
||||
'title' => esc_html__( 'I no longer need the plugin', 'elementor' ),
|
||||
'input_placeholder' => '',
|
||||
],
|
||||
'found_a_better_plugin' => [
|
||||
'title' => esc_html__( 'I found a better plugin', 'elementor' ),
|
||||
'input_placeholder' => esc_html__( 'Please share which plugin', 'elementor' ),
|
||||
],
|
||||
'couldnt_get_the_plugin_to_work' => [
|
||||
'title' => esc_html__( 'I couldn\'t get the plugin to work', 'elementor' ),
|
||||
'input_placeholder' => '',
|
||||
],
|
||||
'temporary_deactivation' => [
|
||||
'title' => esc_html__( 'It\'s a temporary deactivation', 'elementor' ),
|
||||
'input_placeholder' => '',
|
||||
],
|
||||
'elementor_pro' => [
|
||||
'title' => esc_html__( 'I have Elementor Pro', 'elementor' ),
|
||||
'input_placeholder' => '',
|
||||
'alert' => esc_html__( 'Wait! Don\'t deactivate Elementor. You have to activate both Elementor and Elementor Pro in order for the plugin to work.', 'elementor' ),
|
||||
],
|
||||
'other' => [
|
||||
'title' => esc_html__( 'Other', 'elementor' ),
|
||||
'input_placeholder' => esc_html__( 'Please share the reason', 'elementor' ),
|
||||
],
|
||||
];
|
||||
|
||||
?>
|
||||
<div id="elementor-deactivate-feedback-dialog-wrapper">
|
||||
<div id="elementor-deactivate-feedback-dialog-header">
|
||||
<i class="eicon-elementor-square" aria-hidden="true"></i>
|
||||
<span id="elementor-deactivate-feedback-dialog-header-title"><?php echo esc_html__( 'Quick Feedback', 'elementor' ); ?></span>
|
||||
</div>
|
||||
<form id="elementor-deactivate-feedback-dialog-form" method="post">
|
||||
<?php
|
||||
wp_nonce_field( '_elementor_deactivate_feedback_nonce' );
|
||||
?>
|
||||
<input type="hidden" name="action" value="elementor_deactivate_feedback" />
|
||||
|
||||
<div id="elementor-deactivate-feedback-dialog-form-caption"><?php echo esc_html__( 'If you have a moment, please share why you are deactivating Elementor:', 'elementor' ); ?></div>
|
||||
<div id="elementor-deactivate-feedback-dialog-form-body">
|
||||
<?php foreach ( $deactivate_reasons as $reason_key => $reason ) : ?>
|
||||
<div class="elementor-deactivate-feedback-dialog-input-wrapper">
|
||||
<input id="elementor-deactivate-feedback-<?php echo esc_attr( $reason_key ); ?>" class="elementor-deactivate-feedback-dialog-input" type="radio" name="reason_key" value="<?php echo esc_attr( $reason_key ); ?>" />
|
||||
<label for="elementor-deactivate-feedback-<?php echo esc_attr( $reason_key ); ?>" class="elementor-deactivate-feedback-dialog-label"><?php echo esc_html( $reason['title'] ); ?></label>
|
||||
<?php if ( ! empty( $reason['input_placeholder'] ) ) : ?>
|
||||
<input class="elementor-feedback-text" type="text" name="reason_<?php echo esc_attr( $reason_key ); ?>" placeholder="<?php echo esc_attr( $reason['input_placeholder'] ); ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ( ! empty( $reason['alert'] ) ) : ?>
|
||||
<div class="elementor-feedback-text"><?php echo esc_html( $reason['alert'] ); ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax elementor deactivate feedback.
|
||||
*
|
||||
* Send the user feedback when Elementor is deactivated.
|
||||
*
|
||||
* Fired by `wp_ajax_elementor_deactivate_feedback` action.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function ajax_elementor_deactivate_feedback() {
|
||||
$wpnonce = Utils::get_super_global_value( $_POST, '_wpnonce' ); // phpcs:ignore -- Nonce verification is made in `wp_verify_nonce()`.
|
||||
if ( ! wp_verify_nonce( $wpnonce, '_elementor_deactivate_feedback_nonce' ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'activate_plugins' ) ) {
|
||||
wp_send_json_error( 'Permission denied' );
|
||||
}
|
||||
|
||||
$reason_key = Utils::get_super_global_value( $_POST, 'reason_key' ) ?? '';
|
||||
$reason_text = Utils::get_super_global_value( $_POST, "reason_{$reason_key}" ) ?? '';
|
||||
|
||||
Api::send_feedback( $reason_key, $reason_text );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*/
|
||||
private function is_plugins_screen() {
|
||||
return in_array( get_current_screen()->id, [ 'plugins', 'plugins-network' ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\Menu;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Interfaces\Admin_Menu_Item;
|
||||
use Elementor\Core\Admin\Menu\Interfaces\Admin_Menu_Item_Has_Position;
|
||||
use Elementor\Core\Admin\Menu\Interfaces\Admin_Menu_Item_With_Page;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Admin_Menu_Manager {
|
||||
|
||||
/**
|
||||
* @var Admin_Menu_Item[]
|
||||
*/
|
||||
private $items = [];
|
||||
|
||||
public function register( $item_slug, Admin_Menu_Item $item ) {
|
||||
$this->items[ $item_slug ] = $item;
|
||||
}
|
||||
|
||||
public function unregister( $item_slug ) {
|
||||
unset( $this->items[ $item_slug ] );
|
||||
}
|
||||
|
||||
public function get( $item_slug ) {
|
||||
if ( empty( $this->items[ $item_slug ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->items[ $item_slug ];
|
||||
}
|
||||
|
||||
public function get_all() {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function register_actions() {
|
||||
add_action( 'admin_menu', function () {
|
||||
$this->register_wp_menus();
|
||||
}, 20 );
|
||||
|
||||
add_action( 'admin_head', function () {
|
||||
$this->hide_invisible_menus();
|
||||
} );
|
||||
}
|
||||
|
||||
private function register_wp_menus() {
|
||||
do_action( 'elementor/admin/menu/register', $this );
|
||||
|
||||
$hooks = [];
|
||||
|
||||
foreach ( $this->get_all() as $item_slug => $item ) {
|
||||
$is_top_level = empty( $item->get_parent_slug() );
|
||||
|
||||
if ( $is_top_level ) {
|
||||
$hooks[ $item_slug ] = $this->register_top_level_menu( $item_slug, $item );
|
||||
} else {
|
||||
$hooks[ $item_slug ] = $this->register_sub_menu( $item_slug, $item );
|
||||
}
|
||||
}
|
||||
|
||||
do_action( 'elementor/admin/menu/after_register', $this, $hooks );
|
||||
}
|
||||
|
||||
private function register_top_level_menu( $item_slug, Admin_Menu_Item $item ) {
|
||||
$has_page = ( $item instanceof Admin_Menu_Item_With_Page );
|
||||
$has_position = ( $item instanceof Admin_Menu_Item_Has_Position );
|
||||
|
||||
$page_title = $has_page ? $item->get_page_title() : '';
|
||||
$callback = $has_page ? [ $item, 'render' ] : '';
|
||||
$position = $has_position ? $item->get_position() : null;
|
||||
|
||||
return add_menu_page(
|
||||
$page_title,
|
||||
$item->get_label(),
|
||||
$item->get_capability(),
|
||||
$item_slug,
|
||||
$callback,
|
||||
'',
|
||||
$position
|
||||
);
|
||||
}
|
||||
|
||||
private function register_sub_menu( $item_slug, Admin_Menu_Item $item ) {
|
||||
$has_page = ( $item instanceof Admin_Menu_Item_With_Page );
|
||||
|
||||
$page_title = $has_page ? $item->get_page_title() : '';
|
||||
$callback = $has_page ? [ $item, 'render' ] : '';
|
||||
|
||||
return add_submenu_page(
|
||||
$item->get_parent_slug(),
|
||||
$page_title,
|
||||
$item->get_label(),
|
||||
$item->get_capability(),
|
||||
$item_slug,
|
||||
$callback
|
||||
);
|
||||
}
|
||||
|
||||
private function hide_invisible_menus() {
|
||||
foreach ( $this->get_all() as $item_slug => $item ) {
|
||||
if ( $item->is_visible() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$is_top_level = empty( $item->get_parent_slug() );
|
||||
|
||||
if ( $is_top_level ) {
|
||||
remove_menu_page( $item_slug );
|
||||
} else {
|
||||
remove_submenu_page( $item->get_parent_slug(), $item_slug );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
wp-content/plugins/elementor/core/admin/menu/base.php
Normal file
152
wp-content/plugins/elementor/core/admin/menu/base.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Admin\Menu;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base extends Base_Object {
|
||||
|
||||
private $args;
|
||||
|
||||
private $options;
|
||||
|
||||
private $submenus = [];
|
||||
|
||||
abstract protected function get_init_args();
|
||||
|
||||
public function __construct() {
|
||||
$this->init_args();
|
||||
|
||||
$this->init_options();
|
||||
|
||||
add_action( 'admin_menu', function() {
|
||||
$this->register();
|
||||
} );
|
||||
|
||||
if ( $this->options['separator'] ) {
|
||||
add_action( 'admin_menu', function() {
|
||||
$this->add_menu_separator();
|
||||
} );
|
||||
|
||||
add_filter( 'custom_menu_order', '__return_true' );
|
||||
|
||||
add_filter( 'menu_order', function( $menu_order ) {
|
||||
return $this->rearrange_menu_separator( $menu_order );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public function get_args( $arg = null ) {
|
||||
return self::get_items( $this->args, $arg );
|
||||
}
|
||||
|
||||
public function add_submenu( $submenu_args ) {
|
||||
$default_submenu_args = [
|
||||
'page_title' => '',
|
||||
'capability' => $this->args['capability'],
|
||||
'function' => null,
|
||||
'index' => null,
|
||||
];
|
||||
|
||||
$this->submenus[] = array_merge( $default_submenu_args, $submenu_args );
|
||||
}
|
||||
|
||||
protected function get_init_options() {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function register_default_submenus() {}
|
||||
|
||||
protected function register() {
|
||||
$args = $this->args;
|
||||
|
||||
add_menu_page( $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'], $args['icon_url'], $args['position'] );
|
||||
|
||||
$this->register_default_submenus();
|
||||
|
||||
do_action( 'elementor/admin/menu_registered/' . $args['menu_slug'], $this );
|
||||
|
||||
usort( $this->submenus, function( $a, $b ) {
|
||||
return $a['index'] - $b['index'];
|
||||
} );
|
||||
|
||||
foreach ( $this->submenus as $index => $submenu_item ) {
|
||||
$submenu_args = [
|
||||
$args['menu_slug'],
|
||||
$submenu_item['page_title'],
|
||||
$submenu_item['menu_title'],
|
||||
$submenu_item['capability'],
|
||||
$submenu_item['menu_slug'],
|
||||
$submenu_item['function'],
|
||||
];
|
||||
|
||||
if ( 0 === $submenu_item['index'] ) {
|
||||
$submenu_args[] = 0;
|
||||
}
|
||||
|
||||
add_submenu_page( ...$submenu_args );
|
||||
|
||||
if ( ! empty( $submenu_item['class'] ) ) {
|
||||
global $submenu;
|
||||
|
||||
$submenu[ $args['menu_slug'] ][ $index + 1 ][4] = $submenu_item['class']; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function init_args() {
|
||||
$default_args = [
|
||||
'function' => null,
|
||||
'icon_url' => null,
|
||||
'position' => null,
|
||||
];
|
||||
|
||||
$this->args = array_merge( $default_args, $this->get_init_args() );
|
||||
}
|
||||
|
||||
private function init_options() {
|
||||
$default_options = [
|
||||
'separator' => false,
|
||||
];
|
||||
|
||||
$this->options = array_merge( $default_options, $this->get_init_options() );
|
||||
}
|
||||
|
||||
private function add_menu_separator() {
|
||||
global $menu;
|
||||
|
||||
$slug = $this->args['menu_slug'];
|
||||
|
||||
$menu[] = [ '', 'read', 'separator-' . $slug, '', 'wp-menu-separator ' . $slug ]; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
}
|
||||
|
||||
private function rearrange_menu_separator( $menu_order ) {
|
||||
// Initialize our custom order array.
|
||||
$custom_menu_order = [];
|
||||
|
||||
$slug = $this->args['menu_slug'];
|
||||
|
||||
$separator_name = 'separator-' . $slug;
|
||||
|
||||
// Get the index of our custom separator.
|
||||
$custom_separator = array_search( $separator_name, $menu_order, true );
|
||||
|
||||
// Loop through menu order and do some rearranging.
|
||||
foreach ( $menu_order as $item ) {
|
||||
if ( $slug === $item ) {
|
||||
$custom_menu_order[] = $separator_name;
|
||||
$custom_menu_order[] = $item;
|
||||
|
||||
unset( $menu_order[ $custom_separator ] );
|
||||
} elseif ( $separator_name !== $item ) {
|
||||
$custom_menu_order[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
// Return order.
|
||||
return $custom_menu_order;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\Menu\Interfaces;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
interface Admin_Menu_Item_Has_Position {
|
||||
public function get_position();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\Menu\Interfaces;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
interface Admin_Menu_Item_With_Page extends Admin_Menu_Item {
|
||||
public function get_page_title();
|
||||
|
||||
public function render();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\Menu\Interfaces;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
interface Admin_Menu_Item {
|
||||
public function get_capability();
|
||||
|
||||
public function get_label();
|
||||
|
||||
public function get_parent_slug();
|
||||
|
||||
public function is_visible();
|
||||
}
|
||||
79
wp-content/plugins/elementor/core/admin/menu/main.php
Normal file
79
wp-content/plugins/elementor/core/admin/menu/main.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\Menu;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
use Elementor\Tools;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Main extends Base {
|
||||
|
||||
protected function get_init_args() {
|
||||
return [
|
||||
'page_title' => esc_html__( 'Elementor', 'elementor' ),
|
||||
'menu_title' => esc_html__( 'Elementor', 'elementor' ),
|
||||
'capability' => 'manage_options',
|
||||
'menu_slug' => 'elementor',
|
||||
'function' => [ Plugin::$instance->settings, 'display_settings_page' ],
|
||||
'position' => 58.5,
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_init_options() {
|
||||
return [
|
||||
'separator' => true,
|
||||
];
|
||||
}
|
||||
|
||||
protected function register_default_submenus() {
|
||||
$this->add_submenu( [
|
||||
'page_title' => esc_html_x( 'Templates', 'Template Library', 'elementor' ),
|
||||
'menu_title' => esc_html_x( 'Templates', 'Template Library', 'elementor' ),
|
||||
'menu_slug' => Source_Local::ADMIN_MENU_SLUG,
|
||||
'index' => 0,
|
||||
] );
|
||||
|
||||
$this->add_submenu( [
|
||||
'menu_title' => esc_html__( 'Help', 'elementor' ),
|
||||
'menu_slug' => 'go_knowledge_base_site',
|
||||
'function' => [ Plugin::$instance->settings, 'handle_external_redirects' ],
|
||||
'index' => 150,
|
||||
] );
|
||||
}
|
||||
|
||||
protected function register() {
|
||||
parent::register();
|
||||
|
||||
$this->rearrange_elementor_submenu();
|
||||
}
|
||||
|
||||
private function rearrange_elementor_submenu() {
|
||||
global $submenu;
|
||||
|
||||
$elementor_menu_slug = $this->get_args( 'menu_slug' );
|
||||
|
||||
$elementor_submenu_old_index = null;
|
||||
|
||||
$tools_submenu_index = null;
|
||||
|
||||
foreach ( $submenu[ $elementor_menu_slug ] as $index => $submenu_item ) {
|
||||
if ( $elementor_menu_slug === $submenu_item[2] ) {
|
||||
$elementor_submenu_old_index = $index;
|
||||
} elseif ( Tools::PAGE_ID === $submenu_item[2] ) {
|
||||
$tools_submenu_index = $index;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$elementor_submenu = array_splice( $submenu[ $elementor_menu_slug ], $elementor_submenu_old_index, 1 );
|
||||
|
||||
$elementor_submenu[0][0] = esc_html__( 'Settings', 'elementor' );
|
||||
|
||||
array_splice( $submenu[ $elementor_menu_slug ], $tools_submenu_index, 0, $elementor_submenu );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Admin\Notices;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Base_Notice {
|
||||
/**
|
||||
* Determine if the notice should be printed or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function should_print();
|
||||
|
||||
/**
|
||||
* Returns the config of the notice itself.
|
||||
* based on that config the notice will be printed.
|
||||
*
|
||||
* @see \Elementor\Core\Admin\Admin_Notices::admin_notices
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function get_config();
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Admin\Notices;
|
||||
|
||||
use Elementor\User;
|
||||
use Elementor\Settings;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Elementor_Dev_Notice extends Base_Notice {
|
||||
/**
|
||||
* Notice ID.
|
||||
*/
|
||||
const ID = 'elementor_dev_promote';
|
||||
|
||||
/**
|
||||
* Plugin slug to install.
|
||||
*/
|
||||
const PLUGIN_SLUG = 'elementor-beta';
|
||||
|
||||
/**
|
||||
* Plugin name.
|
||||
*/
|
||||
const PLUGIN_NAME = 'elementor-beta/elementor-beta.php';
|
||||
|
||||
/**
|
||||
* Holds the plugins names.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $plugins = [];
|
||||
|
||||
/**
|
||||
* If one of those plugin is installed it will show the notice.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $promotion_plugins = [
|
||||
'woocommerce-beta-tester/woocommerce-beta-tester.php',
|
||||
'wp-jquery-update-test/wp-jquery-update-test.php',
|
||||
'wordpress-beta-tester/wp-beta-tester.php',
|
||||
'gutenberg/gutenberg.php',
|
||||
];
|
||||
|
||||
/**
|
||||
* If one of those options is enabled it will show the notice.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $promotion_options = [
|
||||
'elementor_beta',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function should_print() {
|
||||
return current_user_can( 'install_plugins' ) &&
|
||||
! User::is_user_notice_viewed( static::ID ) &&
|
||||
! $this->is_elementor_dev_installed() &&
|
||||
! $this->is_install_screen() &&
|
||||
(
|
||||
$this->is_promotion_plugins_installed() ||
|
||||
$this->is_promotion_options_enabled()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get_config() {
|
||||
return [
|
||||
'id' => static::ID,
|
||||
'title' => esc_html__( 'Elementor Developer Edition', 'elementor' ),
|
||||
'description' => __(
|
||||
'Get a sneak peek at our in progress development versions, and help us improve Elementor to perfection. Developer Edition releases contain experimental functionality for testing purposes.',
|
||||
'elementor'
|
||||
),
|
||||
'button' => [
|
||||
'text' => esc_html__( 'Install & Activate', 'elementor' ),
|
||||
'url' => wp_nonce_url(
|
||||
self_admin_url( 'update.php?action=install-plugin&plugin=' . static::PLUGIN_SLUG ),
|
||||
'install-plugin_' . static::PLUGIN_SLUG
|
||||
),
|
||||
'type' => 'cta',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the plugins names.
|
||||
*
|
||||
* This method is protected so it can be mocked in tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_plugins() {
|
||||
if ( ! $this->plugins ) {
|
||||
$this->plugins = array_keys( get_plugins() );
|
||||
}
|
||||
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if elementor dev is installed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_elementor_dev_installed() {
|
||||
return in_array( static::PLUGIN_NAME, $this->get_plugins(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the admin screen is install screen.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_install_screen() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( ! $screen ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'update' === $screen->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is one of the promotion plugins is installed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_promotion_plugins_installed() {
|
||||
return array_reduce( $this->promotion_plugins, function ( $should_show_notice, $plugin_name ) {
|
||||
if ( $should_show_notice ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array( $plugin_name, $this->get_plugins(), true );
|
||||
}, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is one of the promotion options is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_promotion_options_enabled() {
|
||||
return array_reduce( $this->promotion_options, function ( $should_show_notice, $option ) {
|
||||
if ( $should_show_notice ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'yes' === get_option( $option, 'no' );
|
||||
}, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current page is elementor settings page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_elementor_setting_page() {
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
return $current_screen && 'toplevel_page_' . Settings::PAGE_ID === $current_screen->id;
|
||||
}
|
||||
}
|
||||
106
wp-content/plugins/elementor/core/admin/ui/components/button.php
Normal file
106
wp-content/plugins/elementor/core/admin/ui/components/button.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Admin\UI\Components;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Button extends Base_Object {
|
||||
|
||||
private $options;
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'admin-button';
|
||||
}
|
||||
|
||||
public function print_button() {
|
||||
$options = $this->get_options();
|
||||
|
||||
if ( empty( $options['text'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$html_tag = ! empty( $options['url'] ) ? 'a' : 'button';
|
||||
$before = '';
|
||||
$icon = '';
|
||||
$attributes = [];
|
||||
|
||||
if ( ! empty( $options['icon'] ) ) {
|
||||
$icon = '<i class="' . esc_attr( $options['icon'] ) . '"></i>';
|
||||
}
|
||||
|
||||
$classes = $options['classes'];
|
||||
|
||||
$default_classes = $this->get_default_options( 'classes' );
|
||||
|
||||
$classes = array_merge( $classes, $default_classes );
|
||||
|
||||
if ( ! empty( $options['type'] ) ) {
|
||||
$classes[] = 'e-button--' . $options['type'];
|
||||
}
|
||||
|
||||
if ( ! empty( $options['variant'] ) ) {
|
||||
$classes[] = 'e-button--' . $options['variant'];
|
||||
}
|
||||
|
||||
if ( ! empty( $options['before'] ) ) {
|
||||
$before = '<span>' . wp_kses_post( $options['before'] ) . '</span>';
|
||||
}
|
||||
|
||||
if ( ! empty( $options['url'] ) ) {
|
||||
$attributes['href'] = $options['url'];
|
||||
if ( $options['new_tab'] ) {
|
||||
$attributes['target'] = '_blank';
|
||||
}
|
||||
}
|
||||
|
||||
$attributes['class'] = $classes;
|
||||
|
||||
$html = $before . '<' . $html_tag . ' ' . Utils::render_html_attributes( $attributes ) . '>';
|
||||
$html .= $icon;
|
||||
$html .= '<span>' . sanitize_text_field( $options['text'] ) . '</span>';
|
||||
$html .= '</' . $html_tag . '>';
|
||||
|
||||
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $option Optional default is null
|
||||
* @return array|mixed
|
||||
*/
|
||||
private function get_options( $option = null ) {
|
||||
return $this->get_items( $this->options, $option );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $option
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_options( $option = null ) {
|
||||
$default_options = [
|
||||
'classes' => [ 'e-button' ],
|
||||
'icon' => '',
|
||||
'new_tab' => false,
|
||||
'text' => '',
|
||||
'type' => '',
|
||||
'url' => '',
|
||||
'variant' => '',
|
||||
'before' => '',
|
||||
];
|
||||
|
||||
if ( null !== $option && -1 !== in_array( $option, $default_options ) ) {
|
||||
return $default_options[ $option ];
|
||||
}
|
||||
|
||||
return $default_options;
|
||||
}
|
||||
|
||||
public function __construct( array $options ) {
|
||||
$this->options = $this->merge_properties( $this->get_default_options(), $options );
|
||||
}
|
||||
}
|
||||
20
wp-content/plugins/elementor/core/app/app.php
Normal file
20
wp-content/plugins/elementor/core/app/app.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Elementor\Core\App;
|
||||
|
||||
use Elementor\Core\Base\App as BaseApp;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* This App class was introduced for backwards compatibility with 3rd parties.
|
||||
*/
|
||||
class App extends BaseApp {
|
||||
|
||||
const PAGE_ID = 'elementor-app';
|
||||
|
||||
public function get_name() {
|
||||
return 'app-bc';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace Elementor\App\Modules\ImportExport;
|
||||
|
||||
use Elementor\App\Modules\ImportExport\Module as Import_Export_Module;
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* This App class exists for backwards compatibility with 3rd parties.
|
||||
*
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
const VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
public $import;
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'import-export-bc';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Elementor\Core\App\Modules\KitLibrary\Connect;
|
||||
|
||||
use Elementor\App\Modules\KitLibrary\Connect\Kit_Library as Kit_Library_Connect;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* This App class exists for backwards compatibility with 3rd parties.
|
||||
*
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
class Kit_Library extends Library {
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
public function is_connected() {
|
||||
/** @var Kit_Library_Connect $kit_library */
|
||||
$kit_library = Plugin::$instance->common->get_component( 'connect' )->get_app( 'kit-library' );
|
||||
|
||||
return $kit_library && $kit_library->is_connected();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Elementor\Core\App\Modules\KitLibrary;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* This App class exists for backwards compatibility with 3rd parties.
|
||||
*
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
const VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'kit-library-bc';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Elementor\Core\App\Modules\Onboarding;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* This App class exists for backwards compatibility with 3rd parties.
|
||||
*
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
const VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* @deprecated 3.8.0
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'onboarding-bc';
|
||||
}
|
||||
}
|
||||
64
wp-content/plugins/elementor/core/base/app.php
Normal file
64
wp-content/plugins/elementor/core/base/app.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Base App
|
||||
*
|
||||
* Base app utility class that provides shared functionality of apps.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
abstract class App extends Module {
|
||||
|
||||
/**
|
||||
* Print config.
|
||||
*
|
||||
* Used to print the app and its components settings as a JavaScript object.
|
||||
*
|
||||
* @param string $handle Optional
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @since 2.6.0 added the `$handle` parameter
|
||||
* @access protected
|
||||
*/
|
||||
final protected function print_config( $handle = null ) {
|
||||
$name = $this->get_name();
|
||||
|
||||
$js_var = 'elementor' . str_replace( ' ', '', ucwords( str_replace( '-', ' ', $name ) ) ) . 'Config';
|
||||
|
||||
$config = $this->get_settings() + $this->get_components_config();
|
||||
|
||||
if ( ! $handle ) {
|
||||
$handle = 'elementor-' . $name;
|
||||
}
|
||||
|
||||
Utils::print_js_config( $handle, $js_var, $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get components config.
|
||||
*
|
||||
* Retrieves the app components settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_components_config() {
|
||||
$settings = [];
|
||||
|
||||
foreach ( $this->get_components() as $id => $instance ) {
|
||||
$settings[ $id ] = $instance->get_settings();
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\BackgroundProcess;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/A5hleyRich/wp-background-processing GPL v2.0
|
||||
*
|
||||
* WP Async Request
|
||||
*
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract WP_Async_Request class.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
abstract class WP_Async_Request {
|
||||
|
||||
/**
|
||||
* Prefix
|
||||
*
|
||||
* (default value: 'wp')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $prefix = 'wp';
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* (default value: 'async_request')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $action = 'async_request';
|
||||
|
||||
/**
|
||||
* Identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* Data
|
||||
*
|
||||
* (default value: array())
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Initiate new async request
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->identifier = $this->prefix . '_' . $this->action;
|
||||
|
||||
add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
|
||||
add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data used during the request
|
||||
*
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function data( $data ) {
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the async request
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function dispatch() {
|
||||
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
|
||||
$args = $this->get_post_args();
|
||||
|
||||
return wp_remote_post( esc_url_raw( $url ), $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_query_args() {
|
||||
if ( property_exists( $this, 'query_args' ) ) {
|
||||
return $this->query_args;
|
||||
}
|
||||
|
||||
return array(
|
||||
'action' => $this->identifier,
|
||||
'nonce' => wp_create_nonce( $this->identifier ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_query_url() {
|
||||
if ( property_exists( $this, 'query_url' ) ) {
|
||||
return $this->query_url;
|
||||
}
|
||||
|
||||
return admin_url( 'admin-ajax.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_post_args() {
|
||||
if ( property_exists( $this, 'post_args' ) ) {
|
||||
return $this->post_args;
|
||||
}
|
||||
|
||||
return array(
|
||||
'timeout' => 0.01,
|
||||
'blocking' => false,
|
||||
'body' => $this->data,
|
||||
'cookies' => $_COOKIE,
|
||||
/** This filter is documented in wp-includes/class-wp-http-streams.php */
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe handle
|
||||
*
|
||||
* Check for correct nonce and pass to handler.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
session_write_close();
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*
|
||||
* Override this method to perform any actions required
|
||||
* during the async request.
|
||||
*/
|
||||
abstract protected function handle();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,521 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\BackgroundProcess;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/A5hleyRich/wp-background-processing GPL v2.0
|
||||
*
|
||||
* WP Background Process
|
||||
*
|
||||
* @package WP-Background-Processing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract WP_Background_Process class.
|
||||
*
|
||||
* @abstract
|
||||
* @extends WP_Async_Request
|
||||
*/
|
||||
abstract class WP_Background_Process extends WP_Async_Request {
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* (default value: 'background_process')
|
||||
*
|
||||
* @var string
|
||||
* @access protected
|
||||
*/
|
||||
protected $action = 'background_process';
|
||||
|
||||
/**
|
||||
* Start time of current process.
|
||||
*
|
||||
* (default value: 0)
|
||||
*
|
||||
* @var int
|
||||
* @access protected
|
||||
*/
|
||||
protected $start_time = 0;
|
||||
|
||||
/**
|
||||
* Cron_hook_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_hook_identifier;
|
||||
|
||||
/**
|
||||
* Cron_interval_identifier
|
||||
*
|
||||
* @var mixed
|
||||
* @access protected
|
||||
*/
|
||||
protected $cron_interval_identifier;
|
||||
|
||||
/**
|
||||
* Initiate new background process
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->cron_hook_identifier = $this->identifier . '_cron';
|
||||
$this->cron_interval_identifier = $this->identifier . '_cron_interval';
|
||||
|
||||
add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
|
||||
add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch
|
||||
*
|
||||
* @access public
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function dispatch() {
|
||||
// Schedule the cron healthcheck.
|
||||
$this->schedule_event();
|
||||
|
||||
// Perform remote post.
|
||||
return parent::dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to queue
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push_to_queue( $data ) {
|
||||
$this->data[] = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save queue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save() {
|
||||
$key = $this->generate_key();
|
||||
|
||||
if ( ! empty( $this->data ) ) {
|
||||
update_site_option( $key, $this->data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update queue
|
||||
*
|
||||
* @param string $key Key.
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function update( $key, $data ) {
|
||||
if ( ! empty( $data ) ) {
|
||||
update_site_option( $key, $data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete queue
|
||||
*
|
||||
* @param string $key Key.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function delete( $key ) {
|
||||
delete_site_option( $key );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate key
|
||||
*
|
||||
* Generates a unique key based on microtime. Queue items are
|
||||
* given a unique key so that they can be merged upon save.
|
||||
*
|
||||
* @param int $length Length.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_key( $length = 64 ) {
|
||||
$unique = md5( microtime() . rand() );
|
||||
$prepend = $this->identifier . '_batch_';
|
||||
|
||||
return substr( $prepend . $unique, 0, $length );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe process queue
|
||||
*
|
||||
* Checks whether data exists within the queue and that
|
||||
* the process is not already running.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing
|
||||
session_write_close();
|
||||
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is queue empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_queue_empty() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// Can't use placeholders for table/column names, it will be wrapped by a single quote (') instead of a backquote (`).
|
||||
$count = $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT COUNT(*)
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
", $key ) );
|
||||
// phpcs:enable
|
||||
|
||||
return ( $count > 0 ) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is process running
|
||||
*
|
||||
* Check whether the current process is already running
|
||||
* in a background process.
|
||||
*/
|
||||
protected function is_process_running() {
|
||||
if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
|
||||
// Process already running.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock process
|
||||
*
|
||||
* Lock the process so that multiple instances can't run simultaneously.
|
||||
* Override if applicable, but the duration should be greater than that
|
||||
* defined in the time_exceeded() method.
|
||||
*/
|
||||
protected function lock_process() {
|
||||
$this->start_time = time(); // Set start time of current process.
|
||||
|
||||
$lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
|
||||
$lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
|
||||
|
||||
set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock process
|
||||
*
|
||||
* Unlock the process so that other instances can spawn.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function unlock_process() {
|
||||
delete_site_transient( $this->identifier . '_process_lock' );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch
|
||||
*
|
||||
* @return \stdClass Return the first batch from the queue
|
||||
*/
|
||||
protected function get_batch() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
$key_column = 'option_id';
|
||||
$value_column = 'option_value';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
$key_column = 'meta_id';
|
||||
$value_column = 'meta_value';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// Can't use placeholders for table/column names, it will be wrapped by a single quote (') instead of a backquote (`).
|
||||
$query = $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT *
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
ORDER BY {$key_column} ASC
|
||||
LIMIT 1
|
||||
", $key ) );
|
||||
// phpcs:enable
|
||||
|
||||
$batch = new \stdClass();
|
||||
$batch->key = $query->$column;
|
||||
$batch->data = maybe_unserialize( $query->$value_column );
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*
|
||||
* Pass each queue item to the task handler, while remaining
|
||||
* within server memory and time limit constraints.
|
||||
*/
|
||||
protected function handle() {
|
||||
$this->lock_process();
|
||||
|
||||
do {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
foreach ( $batch->data as $key => $value ) {
|
||||
$task = $this->task( $value );
|
||||
|
||||
if ( false !== $task ) {
|
||||
$batch->data[ $key ] = $task;
|
||||
} else {
|
||||
unset( $batch->data[ $key ] );
|
||||
}
|
||||
|
||||
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
|
||||
// Batch limits reached.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete current batch.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
} else {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
|
||||
|
||||
$this->unlock_process();
|
||||
|
||||
// Start next batch or complete process.
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->dispatch();
|
||||
} else {
|
||||
$this->complete();
|
||||
}
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory exceeded
|
||||
*
|
||||
* Ensures the batch process never exceeds 90%
|
||||
* of the maximum WordPress memory.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function memory_exceeded() {
|
||||
$memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
|
||||
$current_memory = memory_get_usage( true );
|
||||
$return = false;
|
||||
|
||||
if ( $current_memory >= $memory_limit ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
return apply_filters( $this->identifier . '_memory_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_memory_limit() {
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$memory_limit = ini_get( 'memory_limit' );
|
||||
} else {
|
||||
// Sensible default.
|
||||
$memory_limit = '128M';
|
||||
}
|
||||
|
||||
if ( ! $memory_limit || -1 === intval( $memory_limit ) ) {
|
||||
// Unlimited, set to 32GB.
|
||||
$memory_limit = '32000M';
|
||||
}
|
||||
|
||||
return intval( $memory_limit ) * 1024 * 1024;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time exceeded.
|
||||
*
|
||||
* Ensures the batch never exceeds a sensible time limit.
|
||||
* A timeout limit of 30s is common on shared hosting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function time_exceeded() {
|
||||
$finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
|
||||
$return = false;
|
||||
|
||||
if ( time() >= $finish ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
return apply_filters( $this->identifier . '_time_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete.
|
||||
*
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
// Unschedule the cron healthcheck.
|
||||
$this->clear_scheduled_event();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron healthcheck
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $schedules Schedules.
|
||||
* @return mixed
|
||||
*/
|
||||
public function schedule_cron_healthcheck( $schedules ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
|
||||
|
||||
if ( property_exists( $this, 'cron_interval' ) ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
|
||||
}
|
||||
|
||||
// Adds every 5 minutes to the existing schedules.
|
||||
$schedules[ $this->identifier . '_cron_interval' ] = array(
|
||||
'interval' => MINUTE_IN_SECONDS * $interval,
|
||||
'display' => sprintf(
|
||||
/* translators: %d: Interval in minutes. */
|
||||
esc_html__( 'Every %d minutes', 'elementor' ),
|
||||
$interval,
|
||||
),
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cron healthcheck
|
||||
*
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
$this->clear_scheduled_event();
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule event
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear scheduled event
|
||||
*/
|
||||
protected function clear_scheduled_event() {
|
||||
$timestamp = wp_next_scheduled( $this->cron_hook_identifier );
|
||||
|
||||
if ( $timestamp ) {
|
||||
wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel Process
|
||||
*
|
||||
* Stop processing queue items, clear cronjob and delete batch.
|
||||
*
|
||||
*/
|
||||
public function cancel_process() {
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
$this->delete( $batch->key );
|
||||
|
||||
wp_clear_scheduled_hook( $this->cron_hook_identifier );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Task
|
||||
*
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
* in the next pass through. Or, return false to remove the
|
||||
* item from the queue.
|
||||
*
|
||||
* @param mixed $item Queue item to iterate over.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function task( $item );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Background_Task_Manager extends BaseModule {
|
||||
/**
|
||||
* @var Background_Task
|
||||
*/
|
||||
protected $task_runner;
|
||||
|
||||
abstract public function get_action();
|
||||
abstract public function get_plugin_name();
|
||||
abstract public function get_plugin_label();
|
||||
abstract public function get_task_runner_class();
|
||||
abstract public function get_query_limit();
|
||||
|
||||
abstract protected function start_run();
|
||||
|
||||
public function on_runner_start() {
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
$logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Started' );
|
||||
}
|
||||
|
||||
public function on_runner_complete( $did_tasks = false ) {
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
$logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Completed' );
|
||||
}
|
||||
|
||||
public function get_task_runner() {
|
||||
if ( empty( $this->task_runner ) ) {
|
||||
$class_name = $this->get_task_runner_class();
|
||||
$this->task_runner = new $class_name( $this );
|
||||
}
|
||||
|
||||
return $this->task_runner;
|
||||
}
|
||||
|
||||
// TODO: Replace with a db settings system.
|
||||
protected function add_flag( $flag ) {
|
||||
add_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag, 1 );
|
||||
}
|
||||
|
||||
protected function get_flag( $flag ) {
|
||||
return get_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag );
|
||||
}
|
||||
|
||||
protected function delete_flag( $flag ) {
|
||||
delete_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag );
|
||||
}
|
||||
|
||||
protected function get_start_action_url() {
|
||||
return wp_nonce_url( add_query_arg( $this->get_action(), 'run' ), $this->get_action() . 'run' );
|
||||
}
|
||||
|
||||
protected function get_continue_action_url() {
|
||||
return wp_nonce_url( add_query_arg( $this->get_action(), 'continue' ), $this->get_action() . 'continue' );
|
||||
}
|
||||
|
||||
private function continue_run() {
|
||||
$runner = $this->get_task_runner();
|
||||
$runner->continue_run();
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
if ( empty( $_GET[ $this->get_action() ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin::$instance->init_common();
|
||||
|
||||
if ( 'run' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'run' ) ) {
|
||||
$this->start_run();
|
||||
}
|
||||
|
||||
if ( 'continue' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'continue' ) ) {
|
||||
$this->continue_run();
|
||||
}
|
||||
|
||||
wp_safe_redirect( remove_query_arg( [ $this->get_action(), '_wpnonce' ] ) );
|
||||
die;
|
||||
}
|
||||
}
|
||||
385
wp-content/plugins/elementor/core/base/background-task.php
Normal file
385
wp-content/plugins/elementor/core/base/background-task.php
Normal file
@@ -0,0 +1,385 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Core\Base\BackgroundProcess\WP_Background_Process;
|
||||
|
||||
/**
|
||||
* Based on https://github.com/woocommerce/woocommerce/blob/master/includes/abstracts/class-wc-background-process.php
|
||||
* & https://github.com/woocommerce/woocommerce/blob/master/includes/class-wc-background-updater.php
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* WC_Background_Process class.
|
||||
*/
|
||||
abstract class Background_Task extends WP_Background_Process {
|
||||
protected $current_item;
|
||||
|
||||
/**
|
||||
* Dispatch updater.
|
||||
*
|
||||
* Updater will still run via cron job if this fails for any reason.
|
||||
*/
|
||||
public function dispatch() {
|
||||
$dispatched = parent::dispatch();
|
||||
|
||||
if ( is_wp_error( $dispatched ) ) {
|
||||
wp_die( esc_html( $dispatched ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function query_col( $sql ) {
|
||||
global $wpdb;
|
||||
|
||||
// Add Calc.
|
||||
$item = $this->get_current_item();
|
||||
if ( empty( $item['total'] ) ) {
|
||||
$sql = preg_replace( '/^SELECT/', 'SELECT SQL_CALC_FOUND_ROWS', $sql );
|
||||
}
|
||||
|
||||
// Add offset & limit.
|
||||
$sql = preg_replace( '/;$/', '', $sql );
|
||||
$sql .= ' LIMIT %d, %d;';
|
||||
|
||||
$results = $wpdb->get_col( $wpdb->prepare( $sql, $this->get_current_offset(), $this->get_limit() ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
if ( ! empty( $results ) ) {
|
||||
$this->set_total();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function should_run_again( $updated_rows ) {
|
||||
return count( $updated_rows ) === $this->get_limit();
|
||||
}
|
||||
|
||||
public function get_current_offset() {
|
||||
$limit = $this->get_limit();
|
||||
return ( $this->current_item['iterate_num'] - 1 ) * $limit;
|
||||
}
|
||||
|
||||
public function get_limit() {
|
||||
return $this->manager->get_query_limit();
|
||||
}
|
||||
|
||||
public function set_total() {
|
||||
global $wpdb;
|
||||
|
||||
if ( empty( $this->current_item['total'] ) ) {
|
||||
$total_rows = $wpdb->get_var( 'SELECT FOUND_ROWS();' );
|
||||
$total_iterates = ceil( $total_rows / $this->get_limit() );
|
||||
$this->current_item['total'] = $total_iterates;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete
|
||||
*
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
$this->manager->on_runner_complete( true );
|
||||
|
||||
parent::complete();
|
||||
}
|
||||
|
||||
public function continue_run() {
|
||||
// Used to fire an action added in WP_Background_Process::_construct() that calls WP_Background_Process::handle_cron_healthcheck().
|
||||
// This method will make sure the database updates are executed even if cron is disabled. Nothing will happen if the updates are already running.
|
||||
do_action( $this->cron_hook_identifier );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_current_item() {
|
||||
return $this->current_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch.
|
||||
*
|
||||
* @return \stdClass Return the first batch from the queue.
|
||||
*/
|
||||
protected function get_batch() {
|
||||
$batch = parent::get_batch();
|
||||
$batch->data = array_filter( (array) $batch->data );
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cron healthcheck
|
||||
*
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
$this->clear_scheduled_event();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule fallback event.
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the updater running?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_running() {
|
||||
return false === $this->is_queue_empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the batch limit has been exceeded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function batch_limit_exceeded() {
|
||||
return $this->time_exceeded() || $this->memory_exceeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle.
|
||||
*
|
||||
* Pass each queue item to the task handler, while remaining
|
||||
* within server memory and time limit constraints.
|
||||
*/
|
||||
protected function handle() {
|
||||
$this->manager->on_runner_start();
|
||||
|
||||
$this->lock_process();
|
||||
|
||||
do {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
foreach ( $batch->data as $key => $value ) {
|
||||
$task = $this->task( $value );
|
||||
|
||||
if ( false !== $task ) {
|
||||
$batch->data[ $key ] = $task;
|
||||
} else {
|
||||
unset( $batch->data[ $key ] );
|
||||
}
|
||||
|
||||
if ( $this->batch_limit_exceeded() ) {
|
||||
// Batch limits reached.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete current batch.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
} else {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
} while ( ! $this->batch_limit_exceeded() && ! $this->is_queue_empty() );
|
||||
|
||||
$this->unlock_process();
|
||||
|
||||
// Start next batch or complete process.
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->dispatch();
|
||||
} else {
|
||||
$this->complete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the protected `is_process_running` method as a public method.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_process_locked() {
|
||||
return $this->is_process_running();
|
||||
}
|
||||
|
||||
public function handle_immediately( $callbacks ) {
|
||||
$this->manager->on_runner_start();
|
||||
|
||||
$this->lock_process();
|
||||
|
||||
foreach ( $callbacks as $callback ) {
|
||||
$item = [
|
||||
'callback' => $callback,
|
||||
];
|
||||
|
||||
do {
|
||||
$item = $this->task( $item );
|
||||
} while ( $item );
|
||||
}
|
||||
|
||||
$this->unlock_process();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task
|
||||
*
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
* in the next pass through. Or, return false to remove the
|
||||
* item from the queue.
|
||||
*
|
||||
* @param array $item
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
protected function task( $item ) {
|
||||
$result = false;
|
||||
|
||||
if ( ! isset( $item['iterate_num'] ) ) {
|
||||
$item['iterate_num'] = 1;
|
||||
}
|
||||
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
$callback = $this->format_callback_log( $item );
|
||||
|
||||
if ( is_callable( $item['callback'] ) ) {
|
||||
$progress = '';
|
||||
|
||||
if ( 1 < $item['iterate_num'] ) {
|
||||
if ( empty( $item['total'] ) ) {
|
||||
$progress = sprintf( '(x%s)', $item['iterate_num'] );
|
||||
} else {
|
||||
$percent = ceil( $item['iterate_num'] / ( $item['total'] / 100 ) );
|
||||
$progress = sprintf( '(%s of %s, %s%%)', $item['iterate_num'], $item['total'], $percent );
|
||||
}
|
||||
}
|
||||
|
||||
$logger->info( sprintf( '%s Start %s', $callback, $progress ) );
|
||||
|
||||
$this->current_item = $item;
|
||||
|
||||
$result = (bool) call_user_func( $item['callback'], $this );
|
||||
|
||||
// get back the updated item.
|
||||
$item = $this->current_item;
|
||||
$this->current_item = null;
|
||||
|
||||
if ( $result ) {
|
||||
if ( empty( $item['total'] ) ) {
|
||||
$logger->info( sprintf( '%s callback needs to run again', $callback ) );
|
||||
} elseif ( 1 === $item['iterate_num'] ) {
|
||||
$logger->info( sprintf( '%s callback needs to run more %d times', $callback, $item['total'] - $item['iterate_num'] ) );
|
||||
}
|
||||
|
||||
$item['iterate_num']++;
|
||||
} else {
|
||||
$logger->info( sprintf( '%s Finished', $callback ) );
|
||||
}
|
||||
} else {
|
||||
$logger->notice( sprintf( 'Could not find %s callback', $callback ) );
|
||||
}
|
||||
|
||||
return $result ? $item : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron healthcheck.
|
||||
*
|
||||
* @param array $schedules Schedules.
|
||||
* @return array
|
||||
*/
|
||||
public function schedule_cron_healthcheck( $schedules ) {
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
|
||||
|
||||
// Adds every 5 minutes to the existing schedules.
|
||||
$schedules[ $this->identifier . '_cron_interval' ] = array(
|
||||
'interval' => MINUTE_IN_SECONDS * $interval,
|
||||
'display' => sprintf(
|
||||
/* translators: %d: Interval in minutes. */
|
||||
esc_html__( 'Every %d minutes', 'elementor' ),
|
||||
$interval
|
||||
),
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the batch limit has been exceeded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_memory_exceeded() {
|
||||
return $this->memory_exceeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all batches.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function delete_all_batches() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
$wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE {$column} LIKE %s", $key ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill process.
|
||||
*
|
||||
* Stop processing queue items, clear cronjob and delete all batches.
|
||||
*/
|
||||
public function kill_process() {
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->delete_all_batches();
|
||||
wp_clear_scheduled_hook( $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
public function set_current_item( $item ) {
|
||||
$this->current_item = $item;
|
||||
}
|
||||
|
||||
protected function format_callback_log( $item ) {
|
||||
return implode( '::', (array) $item['callback'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @var \Elementor\Core\Base\Background_Task_Manager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
public function __construct( $manager ) {
|
||||
$this->manager = $manager;
|
||||
// Uses unique prefix per blog so each blog has separate queue.
|
||||
$this->prefix = 'elementor_' . get_current_blog_id();
|
||||
$this->action = $this->manager->get_action();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
193
wp-content/plugins/elementor/core/base/base-object.php
Normal file
193
wp-content/plugins/elementor/core/base/base-object.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Object
|
||||
*
|
||||
* Base class that provides basic settings handling functionality.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class Base_Object {
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
* Holds the object settings.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* Get Settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $setting Optional. The key of the requested setting. Default is null.
|
||||
*
|
||||
* @return mixed An array of all settings, or a single value if `$setting` was specified.
|
||||
*/
|
||||
final public function get_settings( $setting = null ) {
|
||||
$this->ensure_settings();
|
||||
|
||||
return self::get_items( $this->settings, $setting );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array|string $key If key is an array, the settings are overwritten by that array. Otherwise, the
|
||||
* settings of the key will be set to the given `$value` param.
|
||||
*
|
||||
* @param mixed $value Optional. Default is null.
|
||||
*/
|
||||
final public function set_settings( $key, $value = null ) {
|
||||
$this->ensure_settings();
|
||||
|
||||
if ( is_array( $key ) ) {
|
||||
$this->settings = $key;
|
||||
} else {
|
||||
$this->settings[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete setting.
|
||||
*
|
||||
* Deletes the settings array or a specific key of the settings array if `$key` is specified.
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $key Optional. Default is null.
|
||||
*/
|
||||
public function delete_setting( $key = null ) {
|
||||
if ( $key ) {
|
||||
unset( $this->settings[ $key ] );
|
||||
} else {
|
||||
$this->settings = [];
|
||||
}
|
||||
}
|
||||
|
||||
final public function merge_properties( array $default_props, array $custom_props, array $allowed_props_keys = [] ) {
|
||||
$props = array_replace_recursive( $default_props, $custom_props );
|
||||
|
||||
if ( $allowed_props_keys ) {
|
||||
$props = array_intersect_key( $props, array_flip( $allowed_props_keys ) );
|
||||
}
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items.
|
||||
*
|
||||
* Utility method that receives an array with a needle and returns all the
|
||||
* items that match the needle. If needle is not defined the entire haystack
|
||||
* will be returned.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
* @static
|
||||
*
|
||||
* @param array $haystack An array of items.
|
||||
* @param string $needle Optional. Needle. Default is null.
|
||||
*
|
||||
* @return mixed The whole haystack or the needle from the haystack when requested.
|
||||
*/
|
||||
final protected static function get_items( array $haystack, $needle = null ) {
|
||||
if ( $needle ) {
|
||||
return isset( $haystack[ $needle ] ) ? $haystack[ $needle ] : null;
|
||||
}
|
||||
|
||||
return $haystack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* Used to define the default/initial settings of the object. Inheriting classes may implement this method to define
|
||||
* their own default/initial settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure settings.
|
||||
*
|
||||
* Ensures that the `$settings` member is initialized
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*/
|
||||
private function ensure_settings() {
|
||||
if ( null === $this->settings ) {
|
||||
$this->settings = $this->get_init_settings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Has Own Method
|
||||
*
|
||||
* Used for check whether the method passed as a parameter was declared in the current instance or inherited.
|
||||
* If a base_class_name is passed, it checks whether the method was declared in that class. If the method's
|
||||
* declaring class is the class passed as $base_class_name, it returns false. Otherwise (method was NOT declared
|
||||
* in $base_class_name), it returns true.
|
||||
*
|
||||
* Example #1 - only $method_name is passed:
|
||||
* The initial declaration of `register_controls()` happens in the `Controls_Stack` class. However, all
|
||||
* widgets which have their own controls declare this function as well, overriding the original
|
||||
* declaration. If `has_own_method()` would be called by a Widget's class which implements `register_controls()`,
|
||||
* with 'register_controls' passed as the first parameter - `has_own_method()` will return true. If the Widget
|
||||
* does not declare `register_controls()`, `has_own_method()` will return false.
|
||||
*
|
||||
* Example #2 - both $method_name and $base_class_name are passed
|
||||
* In this example, the widget class inherits from a base class `Widget_Base`, and the base implements
|
||||
* `register_controls()` to add certain controls to all widgets inheriting from it. `has_own_method()` is called by
|
||||
* the widget, with the string 'register_controls' passed as the first parameter, and 'Elementor\Widget_Base' (its full name
|
||||
* including the namespace) passed as the second parameter. If the widget class implements `register_controls()`,
|
||||
* `has_own_method` will return true. If the widget class DOESN'T implement `register_controls()`, it will return
|
||||
* false (because `Widget_Base` is the declaring class for `register_controls()`, and not the class that called
|
||||
* `has_own_method()`).
|
||||
*
|
||||
* @since 3.1.0
|
||||
*
|
||||
* @param string $method_name
|
||||
* @param string $base_class_name
|
||||
*
|
||||
* @return bool True if the method was declared by the current instance, False if it was inherited.
|
||||
*/
|
||||
public function has_own_method( $method_name, $base_class_name = null ) {
|
||||
try {
|
||||
$reflection_method = new \ReflectionMethod( $this, $method_name );
|
||||
|
||||
// If a ReflectionMethod is successfully created, get its declaring class.
|
||||
$declaring_class = $reflection_method->getDeclaringClass();
|
||||
} catch ( \Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $base_class_name ) {
|
||||
return $base_class_name !== $declaring_class->name;
|
||||
}
|
||||
|
||||
return get_called_class() === $declaring_class->name;
|
||||
}
|
||||
}
|
||||
242
wp-content/plugins/elementor/core/base/db-upgrades-manager.php
Normal file
242
wp-content/plugins/elementor/core/base/db-upgrades-manager.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Core\Admin\Admin_Notices;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class DB_Upgrades_Manager extends Background_Task_Manager {
|
||||
protected $current_version = null;
|
||||
protected $query_limit = 100;
|
||||
|
||||
abstract public function get_new_version();
|
||||
abstract public function get_version_option_name();
|
||||
abstract public function get_upgrades_class();
|
||||
abstract public function get_updater_label();
|
||||
|
||||
public function get_task_runner_class() {
|
||||
return 'Elementor\Core\Upgrade\Updater';
|
||||
}
|
||||
|
||||
public function get_query_limit() {
|
||||
return $this->query_limit;
|
||||
}
|
||||
|
||||
public function set_query_limit( $limit ) {
|
||||
$this->query_limit = $limit;
|
||||
}
|
||||
|
||||
public function get_current_version() {
|
||||
if ( null === $this->current_version ) {
|
||||
$this->current_version = get_option( $this->get_version_option_name() );
|
||||
}
|
||||
|
||||
return $this->current_version;
|
||||
}
|
||||
|
||||
public function should_upgrade() {
|
||||
$current_version = $this->get_current_version();
|
||||
|
||||
// It's a new install.
|
||||
if ( ! $current_version ) {
|
||||
$this->update_db_version();
|
||||
return false;
|
||||
}
|
||||
|
||||
return version_compare( $this->get_new_version(), $current_version, '>' );
|
||||
}
|
||||
|
||||
public function on_runner_start() {
|
||||
parent::on_runner_start();
|
||||
|
||||
if ( ! defined( 'IS_ELEMENTOR_UPGRADE' ) ) {
|
||||
define( 'IS_ELEMENTOR_UPGRADE', true );
|
||||
}
|
||||
}
|
||||
|
||||
public function on_runner_complete( $did_tasks = false ) {
|
||||
$logger = Plugin::$instance->logger->get_logger();
|
||||
|
||||
$logger->info( 'Elementor data updater process has been completed.', [
|
||||
'meta' => [
|
||||
'plugin' => $this->get_plugin_label(),
|
||||
'from' => $this->current_version,
|
||||
'to' => $this->get_new_version(),
|
||||
],
|
||||
] );
|
||||
|
||||
$this->clear_cache();
|
||||
|
||||
$this->update_db_version();
|
||||
|
||||
if ( $did_tasks ) {
|
||||
$this->add_flag( 'completed' );
|
||||
}
|
||||
}
|
||||
|
||||
protected function clear_cache() {
|
||||
Plugin::$instance->files_manager->clear_cache();
|
||||
}
|
||||
|
||||
public function admin_notice_start_upgrade() {
|
||||
/**
|
||||
* @var Admin_Notices $admin_notices
|
||||
*/
|
||||
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
|
||||
|
||||
$options = [
|
||||
'title' => $this->get_updater_label(),
|
||||
'description' => esc_html__( 'Your site database needs to be updated to the latest version.', 'elementor' ),
|
||||
'type' => 'error',
|
||||
'icon' => false,
|
||||
'button' => [
|
||||
'text' => esc_html__( 'Update Now', 'elementor' ),
|
||||
'url' => $this->get_start_action_url(),
|
||||
'class' => 'e-button e-button--cta',
|
||||
],
|
||||
];
|
||||
|
||||
$admin_notices->print_admin_notice( $options );
|
||||
}
|
||||
|
||||
public function admin_notice_upgrade_is_running() {
|
||||
/**
|
||||
* @var Admin_Notices $admin_notices
|
||||
*/
|
||||
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
|
||||
|
||||
$options = [
|
||||
'title' => $this->get_updater_label(),
|
||||
'description' => esc_html__( 'Database update process is running in the background. Taking a while?', 'elementor' ),
|
||||
'type' => 'warning',
|
||||
'icon' => false,
|
||||
'button' => [
|
||||
'text' => esc_html__( 'Click here to run it now', 'elementor' ),
|
||||
'url' => $this->get_continue_action_url(),
|
||||
'class' => 'e-button e-button--primary',
|
||||
],
|
||||
];
|
||||
|
||||
$admin_notices->print_admin_notice( $options );
|
||||
}
|
||||
|
||||
public function admin_notice_upgrade_is_completed() {
|
||||
$this->delete_flag( 'completed' );
|
||||
|
||||
$message = esc_html__( 'The database update process is now complete. Thank you for updating to the latest version!', 'elementor' );
|
||||
|
||||
/**
|
||||
* @var Admin_Notices $admin_notices
|
||||
*/
|
||||
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
|
||||
|
||||
$options = [
|
||||
'description' => '<b>' . $this->get_updater_label() . '</b> - ' . $message,
|
||||
'type' => 'success',
|
||||
'icon' => false,
|
||||
];
|
||||
|
||||
$admin_notices->print_admin_notice( $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* @access protected
|
||||
*/
|
||||
protected function start_run() {
|
||||
$updater = $this->get_task_runner();
|
||||
|
||||
if ( $updater->is_running() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$upgrade_callbacks = $this->get_upgrade_callbacks();
|
||||
|
||||
if ( empty( $upgrade_callbacks ) ) {
|
||||
$this->on_runner_complete();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->clear_cache();
|
||||
|
||||
foreach ( $upgrade_callbacks as $callback ) {
|
||||
$updater->push_to_queue( [
|
||||
'callback' => $callback,
|
||||
] );
|
||||
}
|
||||
|
||||
$updater->save()->dispatch();
|
||||
|
||||
Plugin::$instance->logger->get_logger()->info( 'Elementor data updater process has been queued.', [
|
||||
'meta' => [
|
||||
'plugin' => $this->get_plugin_label(),
|
||||
'from' => $this->current_version,
|
||||
'to' => $this->get_new_version(),
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
protected function update_db_version() {
|
||||
update_option( $this->get_version_option_name(), $this->get_new_version() );
|
||||
}
|
||||
|
||||
public function get_upgrade_callbacks() {
|
||||
$prefix = '_v_';
|
||||
$upgrades_class = $this->get_upgrades_class();
|
||||
$upgrades_reflection = new \ReflectionClass( $upgrades_class );
|
||||
|
||||
$callbacks = [];
|
||||
|
||||
foreach ( $upgrades_reflection->getMethods() as $method ) {
|
||||
$method_name = $method->getName();
|
||||
|
||||
if ( '_on_each_version' === $method_name ) {
|
||||
$callbacks[] = [ $upgrades_class, $method_name ];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( false === strpos( $method_name, $prefix ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! preg_match_all( "/$prefix(\d+_\d+_\d+)/", $method_name, $matches ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method_version = str_replace( '_', '.', $matches[1][0] );
|
||||
|
||||
if ( ! version_compare( $method_version, $this->current_version, '>' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$callbacks[] = [ $upgrades_class, $method_name ];
|
||||
}
|
||||
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
// If upgrade is completed - show the notice only for admins.
|
||||
// Note: in this case `should_upgrade` returns false, because it's already upgraded.
|
||||
if ( is_admin() && current_user_can( 'update_plugins' ) && $this->get_flag( 'completed' ) ) {
|
||||
add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_completed' ] );
|
||||
}
|
||||
|
||||
if ( ! $this->should_upgrade() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$updater = $this->get_task_runner();
|
||||
|
||||
$this->start_run();
|
||||
|
||||
if ( $updater->is_running() && current_user_can( 'update_plugins' ) ) {
|
||||
add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_running' ] );
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
2066
wp-content/plugins/elementor/core/base/document.php
Normal file
2066
wp-content/plugins/elementor/core/base/document.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\Elements_Iteration_Actions;
|
||||
|
||||
use Elementor\Conditions;
|
||||
use Elementor\Element_Base;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Assets extends Base {
|
||||
const ASSETS_META_KEY = '_elementor_page_assets';
|
||||
|
||||
// Default value must be empty.
|
||||
private $page_assets;
|
||||
|
||||
// Default value must be empty.
|
||||
private $saved_page_assets;
|
||||
|
||||
public function element_action( Element_Base $element_data ) {
|
||||
$settings = $element_data->get_active_settings();
|
||||
$controls = $element_data->get_controls();
|
||||
|
||||
$element_assets = $this->get_assets( $settings, $controls );
|
||||
|
||||
if ( $element_assets ) {
|
||||
$this->update_page_assets( $element_assets );
|
||||
}
|
||||
}
|
||||
|
||||
public function is_action_needed() {
|
||||
// No need to evaluate in preview mode, will be made in the saving process.
|
||||
if ( Plugin::$instance->preview->is_preview_mode() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$page_assets = $this->get_saved_page_assets();
|
||||
|
||||
// When $page_assets is array it means that the assets registration has already been made at least once.
|
||||
if ( is_array( $page_assets ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function after_elements_iteration() {
|
||||
// In case that the page assets value is empty, it should still be saved as an empty array as an indication that at lease one iteration has occurred.
|
||||
if ( ! is_array( $this->page_assets ) ) {
|
||||
$this->page_assets = [];
|
||||
}
|
||||
|
||||
$this->get_document_assets();
|
||||
|
||||
// Saving the page assets data.
|
||||
$this->document->update_meta( self::ASSETS_META_KEY, $this->page_assets );
|
||||
|
||||
if ( 'render' === $this->mode && $this->page_assets ) {
|
||||
Plugin::$instance->assets_loader->enable_assets( $this->page_assets );
|
||||
}
|
||||
}
|
||||
|
||||
private function get_saved_page_assets( $force_meta_fetch = false ) {
|
||||
if ( ! is_array( $this->saved_page_assets ) || $force_meta_fetch ) {
|
||||
$this->saved_page_assets = $this->document->get_meta( self::ASSETS_META_KEY );
|
||||
}
|
||||
|
||||
return $this->saved_page_assets;
|
||||
}
|
||||
|
||||
private function update_page_assets( $new_assets ) {
|
||||
if ( ! is_array( $this->page_assets ) ) {
|
||||
$this->page_assets = [];
|
||||
}
|
||||
|
||||
foreach ( $new_assets as $assets_type => $assets_type_data ) {
|
||||
if ( ! isset( $this->page_assets[ $assets_type ] ) ) {
|
||||
$this->page_assets[ $assets_type ] = [];
|
||||
}
|
||||
|
||||
foreach ( $assets_type_data as $asset_name ) {
|
||||
if ( ! in_array( $asset_name, $this->page_assets[ $assets_type ], true ) ) {
|
||||
$this->page_assets[ $assets_type ][] = $asset_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_assets( $settings, $controls ) {
|
||||
$assets = [];
|
||||
|
||||
foreach ( $settings as $setting_key => $setting ) {
|
||||
if ( ! isset( $controls[ $setting_key ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$control = $controls[ $setting_key ];
|
||||
|
||||
// Enabling assets loading from the registered control fields.
|
||||
if ( ! empty( $control['assets'] ) ) {
|
||||
foreach ( $control['assets'] as $assets_type => $dependencies ) {
|
||||
foreach ( $dependencies as $dependency ) {
|
||||
if ( ! empty( $dependency['conditions'] ) ) {
|
||||
$is_condition_fulfilled = Conditions::check( $dependency['conditions'], $settings );
|
||||
|
||||
if ( ! $is_condition_fulfilled ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $assets[ $assets_type ] ) ) {
|
||||
$assets[ $assets_type ] = [];
|
||||
}
|
||||
|
||||
$assets[ $assets_type ][] = $dependency['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enabling assets loading from the control object.
|
||||
$control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
|
||||
|
||||
$control_conditional_assets = $control_obj::get_assets( $setting );
|
||||
|
||||
if ( $control_conditional_assets ) {
|
||||
foreach ( $control_conditional_assets as $assets_type => $dependencies ) {
|
||||
foreach ( $dependencies as $dependency ) {
|
||||
if ( ! isset( $assets[ $assets_type ] ) ) {
|
||||
$assets[ $assets_type ] = [];
|
||||
}
|
||||
|
||||
$assets[ $assets_type ][] = $dependency;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $assets;
|
||||
}
|
||||
|
||||
private function get_document_assets() {
|
||||
$document_id = $this->document->get_post()->ID;
|
||||
|
||||
// Getting the document instance in order to get the most updated settings.
|
||||
$updated_document = Plugin::$instance->documents->get( $document_id, false );
|
||||
|
||||
$document_settings = $updated_document->get_settings();
|
||||
|
||||
$document_controls = $this->document->get_controls();
|
||||
|
||||
$document_assets = $this->get_assets( $document_settings, $document_controls );
|
||||
|
||||
if ( $document_assets ) {
|
||||
$this->update_page_assets( $document_assets );
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct( $document ) {
|
||||
parent::__construct( $document );
|
||||
|
||||
// No need to enable assets in preview mode, all assets will be loaded by default by the assets loader.
|
||||
if ( Plugin::$instance->preview->is_preview_mode() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page_assets = $this->get_saved_page_assets();
|
||||
|
||||
// If $page_assets is not empty then enabling the assets for loading.
|
||||
if ( $page_assets ) {
|
||||
Plugin::$instance->assets_loader->enable_assets( $page_assets );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\Elements_Iteration_Actions;
|
||||
|
||||
use Elementor\Element_Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base {
|
||||
/**
|
||||
* The current document that the Base class instance was created from.
|
||||
*/
|
||||
protected $document;
|
||||
|
||||
/**
|
||||
* Indicates if the methods are being triggered on page save or at render time (value will be either 'save' or 'render').
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mode = '';
|
||||
|
||||
/**
|
||||
* Is Action Needed.
|
||||
*
|
||||
* Runs only at runtime and used as a flag to determine if all methods should run on page render.
|
||||
* If returns false, all methods will run only on page save.
|
||||
* If returns true, all methods will run on both page render and on save.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function is_action_needed();
|
||||
|
||||
/**
|
||||
* Unique Element Action.
|
||||
*
|
||||
* Will be triggered for each unique page element - section / column / widget unique type (heading, icon etc.).
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unique_element_action( Element_Base $element_data ) {}
|
||||
|
||||
/**
|
||||
* Element Action.
|
||||
*
|
||||
* Will be triggered for each page element - section / column / widget.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function element_action( Element_Base $element_data ) {}
|
||||
|
||||
/**
|
||||
* After Elements Iteration.
|
||||
*
|
||||
* Will be triggered after all page elements iteration has ended.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function after_elements_iteration() {}
|
||||
|
||||
public function set_mode( $mode ) {
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
public function __construct( $document ) {
|
||||
$this->document = $document;
|
||||
}
|
||||
}
|
||||
338
wp-content/plugins/elementor/core/base/module.php
Normal file
338
wp-content/plugins/elementor/core/base/module.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor module.
|
||||
*
|
||||
* An abstract class that provides the needed properties and methods to
|
||||
* manage and handle modules in inheriting classes.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Module extends Base_Object {
|
||||
|
||||
/**
|
||||
* Module class reflection.
|
||||
*
|
||||
* Holds the information about a class.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access private
|
||||
*
|
||||
* @var \ReflectionClass
|
||||
*/
|
||||
private $reflection;
|
||||
|
||||
/**
|
||||
* Module components.
|
||||
*
|
||||
* Holds the module components.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $components = [];
|
||||
|
||||
/**
|
||||
* Module instance.
|
||||
*
|
||||
* Holds the module instance.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access protected
|
||||
*
|
||||
* @var Module
|
||||
*/
|
||||
protected static $_instances = [];
|
||||
|
||||
/**
|
||||
* Get module name.
|
||||
*
|
||||
* Retrieve the module name.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
* @abstract
|
||||
*
|
||||
* @return string Module name.
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
/**
|
||||
* Instance.
|
||||
*
|
||||
* Ensures only one instance of the module class is loaded or can be loaded.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return Module An instance of the class.
|
||||
*/
|
||||
public static function instance() {
|
||||
$class_name = static::class_name();
|
||||
|
||||
if ( empty( static::$_instances[ $class_name ] ) ) {
|
||||
static::$_instances[ $class_name ] = new static();
|
||||
}
|
||||
|
||||
return static::$_instances[ $class_name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function is_active() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class name.
|
||||
*
|
||||
* Retrieve the name of the class.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function class_name() {
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
public static function get_experimental_data() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone.
|
||||
*
|
||||
* Disable class cloning and throw an error on object clone.
|
||||
*
|
||||
* The whole idea of the singleton design pattern is that there is a single
|
||||
* object. Therefore, we don't want the object to be cloned.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*/
|
||||
public function __clone() {
|
||||
_doing_it_wrong(
|
||||
__FUNCTION__,
|
||||
sprintf( 'Cloning instances of the singleton "%s" class is forbidden.', get_class( $this ) ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
'1.0.0'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wakeup.
|
||||
*
|
||||
* Disable unserializing of the class.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*/
|
||||
public function __wakeup() {
|
||||
_doing_it_wrong(
|
||||
__FUNCTION__,
|
||||
sprintf( 'Unserializing instances of the singleton "%s" class is forbidden.', get_class( $this ) ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
'1.0.0'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_reflection() {
|
||||
if ( null === $this->reflection ) {
|
||||
$this->reflection = new \ReflectionClass( $this );
|
||||
}
|
||||
|
||||
return $this->reflection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add module component.
|
||||
*
|
||||
* Add new component to the current module.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $id Component ID.
|
||||
* @param mixed $instance An instance of the component.
|
||||
*/
|
||||
public function add_component( $id, $instance ) {
|
||||
$this->components[ $id ] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
* @return Module[]
|
||||
*/
|
||||
public function get_components() {
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module component.
|
||||
*
|
||||
* Retrieve the module component.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $id Component ID.
|
||||
*
|
||||
* @return mixed An instance of the component, or `false` if the component
|
||||
* doesn't exist.
|
||||
*/
|
||||
public function get_component( $id ) {
|
||||
if ( isset( $this->components[ $id ] ) ) {
|
||||
return $this->components[ $id ];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets url.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @param string $file_name
|
||||
* @param string $file_extension
|
||||
* @param string $relative_url Optional. Default is null.
|
||||
* @param string $add_min_suffix Optional. Default is 'default'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function get_assets_url( $file_name, $file_extension, $relative_url = null, $add_min_suffix = 'default' ) {
|
||||
static $is_test_mode = null;
|
||||
|
||||
if ( null === $is_test_mode ) {
|
||||
$is_test_mode = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_TESTS;
|
||||
}
|
||||
|
||||
if ( ! $relative_url ) {
|
||||
$relative_url = $this->get_assets_relative_url() . $file_extension . '/';
|
||||
}
|
||||
|
||||
$url = $this->get_assets_base_url() . $relative_url . $file_name;
|
||||
|
||||
if ( 'default' === $add_min_suffix ) {
|
||||
$add_min_suffix = ! $is_test_mode;
|
||||
}
|
||||
|
||||
if ( $add_min_suffix ) {
|
||||
$url .= '.min';
|
||||
}
|
||||
|
||||
return $url . '.' . $file_extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get js assets url
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @param string $file_name
|
||||
* @param string $relative_url Optional. Default is null.
|
||||
* @param string $add_min_suffix Optional. Default is 'default'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function get_js_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default' ) {
|
||||
return $this->get_assets_url( $file_name, 'js', $relative_url, $add_min_suffix );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get css assets url
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @param string $file_name
|
||||
* @param string $relative_url Optional. Default is null.
|
||||
* @param string $add_min_suffix Optional. Default is 'default'.
|
||||
* @param bool $add_direction_suffix Optional. Default is `false`
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final protected function get_css_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default', $add_direction_suffix = false ) {
|
||||
static $direction_suffix = null;
|
||||
|
||||
if ( ! $direction_suffix ) {
|
||||
$direction_suffix = is_rtl() ? '-rtl' : '';
|
||||
}
|
||||
|
||||
if ( $add_direction_suffix ) {
|
||||
$file_name .= $direction_suffix;
|
||||
}
|
||||
|
||||
return $this->get_assets_url( $file_name, 'css', $relative_url, $add_min_suffix );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets base url
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_assets_base_url() {
|
||||
return ELEMENTOR_URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets relative url
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_assets_relative_url() {
|
||||
return 'assets/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the module's associated widgets.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_widgets() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the module related widgets.
|
||||
*/
|
||||
public function init_widgets() {
|
||||
$widget_manager = Plugin::instance()->widgets_manager;
|
||||
|
||||
foreach ( $this->get_widgets() as $widget ) {
|
||||
$class_name = $this->get_reflection()->getNamespaceName() . '\Widgets\\' . $widget;
|
||||
|
||||
$widget_manager->register( new $class_name() );
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'elementor/widgets/register', [ $this, 'init_widgets' ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base\Providers;
|
||||
|
||||
class Social_Network_Provider {
|
||||
|
||||
private static array $social_networks = [];
|
||||
|
||||
public const FACEBOOK = 'Facebook';
|
||||
public const TWITTER = 'X (Twitter)';
|
||||
public const INSTAGRAM = 'Instagram';
|
||||
public const LINKEDIN = 'LinkedIn';
|
||||
public const PINTEREST = 'Pinterest';
|
||||
public const YOUTUBE = 'YouTube';
|
||||
public const TIKTOK = 'TikTok';
|
||||
public const WHATSAPP = 'WhatsApp';
|
||||
public const APPLEMUSIC = 'Apple Music';
|
||||
public const SPOTIFY = 'Spotify';
|
||||
public const SOUNDCLOUD = 'SoundCloud';
|
||||
public const BEHANCE = 'Behance';
|
||||
public const DRIBBBLE = 'Dribbble';
|
||||
public const VIMEO = 'Vimeo';
|
||||
public const WAZE = 'Waze';
|
||||
public const MESSENGER = 'Messenger';
|
||||
public const TELEPHONE = 'Telephone';
|
||||
public const EMAIL = 'Email';
|
||||
public const URL = 'Url';
|
||||
public const FILE_DOWNLOAD = 'File Download';
|
||||
public const SMS = 'SMS';
|
||||
public const VIBER = 'VIBER';
|
||||
public const SKYPE = 'Skype';
|
||||
public const VCF = 'Save contact (vCard)';
|
||||
|
||||
public static function get_social_networks_icons(): array {
|
||||
static::init_social_networks_array_if_empty();
|
||||
|
||||
static $icons = [];
|
||||
|
||||
if ( empty( $icons ) ) {
|
||||
foreach ( static::$social_networks as $network => $data ) {
|
||||
$icons[ $network ] = $data['icon'];
|
||||
}
|
||||
}
|
||||
|
||||
return $icons;
|
||||
}
|
||||
|
||||
public static function get_icon_mapping( string $platform ): string {
|
||||
static::init_social_networks_array_if_empty();
|
||||
|
||||
if ( isset( self::$social_networks[ $platform ]['icon'] ) ) {
|
||||
return self::$social_networks[ $platform ]['icon'];
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
public static function get_name_mapping( string $platform ): string {
|
||||
static::init_social_networks_array_if_empty();
|
||||
|
||||
if ( isset( self::$social_networks[ $platform ]['name'] ) ) {
|
||||
return self::$social_networks[ $platform ]['name'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function get_social_networks_text( $providers = [] ): array {
|
||||
static::init_social_networks_array_if_empty();
|
||||
|
||||
static $texts = [];
|
||||
|
||||
if ( empty( $texts ) ) {
|
||||
foreach ( static::$social_networks as $network => $data ) {
|
||||
$texts[ $network ] = $data['text'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( $providers ) {
|
||||
return array_intersect_key( $texts, array_flip( $providers ) );
|
||||
}
|
||||
|
||||
return $texts;
|
||||
}
|
||||
|
||||
private static function init_social_networks_array_if_empty(): void {
|
||||
if ( ! empty( static::$social_networks ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
static::$social_networks[ static::VCF ] = [
|
||||
'text' => esc_html__( 'Save contact (vCard)', 'elementor' ),
|
||||
'icon' => 'fab fa-outlook',
|
||||
'name' => 'vcf',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::FACEBOOK ] = [
|
||||
'text' => esc_html__( 'Facebook', 'elementor' ),
|
||||
'icon' => 'fab fa-facebook',
|
||||
'name' => 'facebook',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::TWITTER ] = [
|
||||
'text' => esc_html__( 'X (Twitter)', 'elementor' ),
|
||||
'icon' => 'fab fa-x-twitter',
|
||||
'name' => 'x-twitter',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::INSTAGRAM ] = [
|
||||
'text' => esc_html__( 'Instagram', 'elementor' ),
|
||||
'icon' => 'fab fa-instagram',
|
||||
'name' => 'instagram',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::LINKEDIN ] = [
|
||||
'text' => esc_html__( 'LinkedIn', 'elementor' ),
|
||||
'icon' => 'fab fa-linkedin-in',
|
||||
'name' => 'linkedin',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::PINTEREST ] = [
|
||||
'text' => esc_html__( 'Pinterest', 'elementor' ),
|
||||
'icon' => 'fab fa-pinterest',
|
||||
'name' => 'pinterest',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::YOUTUBE ] = [
|
||||
'text' => esc_html__( 'YouTube', 'elementor' ),
|
||||
'icon' => 'fab fa-youtube',
|
||||
'name' => 'youtube',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::TIKTOK ] = [
|
||||
'text' => esc_html__( 'TikTok', 'elementor' ),
|
||||
'icon' => 'fab fa-tiktok',
|
||||
'name' => 'tiktok',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::WHATSAPP ] = [
|
||||
'text' => esc_html__( 'WhatsApp', 'elementor' ),
|
||||
'icon' => 'fab fa-whatsapp',
|
||||
'name' => 'whatsapp',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::APPLEMUSIC ] = [
|
||||
'text' => esc_html__( 'Apple Music', 'elementor' ),
|
||||
'icon' => 'fa fa-music',
|
||||
'name' => 'apple-music',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::SPOTIFY ] = [
|
||||
'text' => esc_html__( 'Spotify', 'elementor' ),
|
||||
'icon' => 'fab fa-spotify',
|
||||
'name' => 'spotify',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::SOUNDCLOUD ] = [
|
||||
'text' => esc_html__( 'SoundCloud', 'elementor' ),
|
||||
'icon' => 'fab fa-soundcloud',
|
||||
'name' => 'soundcloud',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::BEHANCE ] = [
|
||||
'text' => esc_html__( 'Behance', 'elementor' ),
|
||||
'icon' => 'fab fa-behance',
|
||||
'name' => 'behance',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::DRIBBBLE ] = [
|
||||
'text' => esc_html__( 'Dribbble', 'elementor' ),
|
||||
'icon' => 'fab fa-dribbble',
|
||||
'name' => 'dribble',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::VIMEO ] = [
|
||||
'text' => esc_html__( 'Vimeo', 'elementor' ),
|
||||
'icon' => 'fab fa-vimeo-v',
|
||||
'name' => 'vimeo',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::WAZE ] = [
|
||||
'text' => esc_html__( 'Waze', 'elementor' ),
|
||||
'icon' => 'fab fa-waze',
|
||||
'name' => 'waze',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::MESSENGER ] = [
|
||||
'text' => esc_html__( 'Messenger', 'elementor' ),
|
||||
'icon' => 'fab fa-facebook-messenger',
|
||||
'name' => 'messenger',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::TELEPHONE ] = [
|
||||
'text' => esc_html__( 'Telephone', 'elementor' ),
|
||||
'icon' => 'fas fa-phone-alt',
|
||||
'name' => 'phone',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::EMAIL ] = [
|
||||
'text' => esc_html__( 'Email', 'elementor' ),
|
||||
'icon' => 'fas fa-envelope',
|
||||
'name' => 'email',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::URL ] = [
|
||||
'text' => esc_html__( 'URL', 'elementor' ),
|
||||
'icon' => 'fas fa-globe',
|
||||
'name' => 'url',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::FILE_DOWNLOAD ] = [
|
||||
'text' => esc_html__( 'File Download', 'elementor' ),
|
||||
'icon' => 'fas fa-download',
|
||||
'name' => 'download',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::SMS ] = [
|
||||
'text' => esc_html__( 'SMS', 'elementor' ),
|
||||
'icon' => 'fas fa-sms',
|
||||
'name' => 'sms',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::VIBER ] = [
|
||||
'text' => esc_html__( 'Viber', 'elementor' ),
|
||||
'icon' => 'fab fa-viber',
|
||||
'name' => 'viber',
|
||||
];
|
||||
|
||||
static::$social_networks[ static::SKYPE ] = [
|
||||
'text' => esc_html__( 'Skype', 'elementor' ),
|
||||
'icon' => 'fab fa-skype',
|
||||
'name' => 'skype',
|
||||
];
|
||||
}
|
||||
|
||||
public static function build_messenger_link( string $username ) {
|
||||
return 'https://m.me/' . $username;
|
||||
}
|
||||
|
||||
public static function build_email_link( array $data, string $prefix ) {
|
||||
$email = $data[ $prefix . '_mail' ] ?? '';
|
||||
$subject = $data[ $prefix . '_mail_subject' ] ?? '';
|
||||
$body = $data[ $prefix . '_mail_body' ] ?? '';
|
||||
|
||||
if ( ! $email ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$link = 'mailto:' . $email;
|
||||
|
||||
if ( $subject ) {
|
||||
$link .= '?subject=' . $subject;
|
||||
}
|
||||
|
||||
if ( $body ) {
|
||||
$link .= $subject ? '&' : '?';
|
||||
$link .= 'body=' . $body;
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
|
||||
public static function build_viber_link( string $action, string $number ) {
|
||||
if ( empty( $number ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return add_query_arg( [
|
||||
'number' => urlencode( $number ),
|
||||
], 'viber://' . $action );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Base\Traits;
|
||||
|
||||
use Elementor\Controls_Manager;
|
||||
use Elementor\Modules\FloatingButtons\Control\Hover_Animation_Floating_Buttons;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Shapes;
|
||||
use Elementor\Utils;
|
||||
|
||||
trait Shared_Widget_Controls_Trait {
|
||||
|
||||
protected $border_width_range = [
|
||||
'min' => 0,
|
||||
'max' => 10,
|
||||
'step' => 1,
|
||||
];
|
||||
|
||||
protected function add_html_tag_control( string $name, string $default = 'h2' ): void {
|
||||
$this->add_control(
|
||||
$name,
|
||||
[
|
||||
'label' => esc_html__( 'HTML Tag', 'elementor' ),
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => [
|
||||
'h1' => 'H1',
|
||||
'h2' => 'H2',
|
||||
'h3' => 'H3',
|
||||
'h4' => 'H4',
|
||||
'h5' => 'H5',
|
||||
'h6' => 'H6',
|
||||
'div' => 'div',
|
||||
'span' => 'span',
|
||||
'p' => 'p',
|
||||
],
|
||||
'default' => $default,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any child arrays where all properties are empty
|
||||
*/
|
||||
protected function clean_array(
|
||||
$input_array = []
|
||||
) {
|
||||
$output_array = array_filter( $input_array, function( $sub_array ) {
|
||||
// Use array_filter on the sub array
|
||||
$filtered_sub_array = array_filter( $sub_array, function( $val ) {
|
||||
// Filter out empty or null values
|
||||
return ! is_null( $val ) && '' !== $val;
|
||||
} );
|
||||
// A non-empty result means the sub array contains some non-empty value(s)
|
||||
return ! empty( $filtered_sub_array );
|
||||
} );
|
||||
return $output_array;
|
||||
}
|
||||
|
||||
protected function get_link_attributes(
|
||||
$link = [],
|
||||
$other_attributes = []
|
||||
) {
|
||||
$url_attrs = [];
|
||||
$rel_string = '';
|
||||
|
||||
if ( ! empty( $link['url'] ) ) {
|
||||
$url_attrs['href'] = esc_url( $link['url'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $link['is_external'] ) ) {
|
||||
$url_attrs['target'] = '_blank';
|
||||
$rel_string .= 'noopener ';
|
||||
}
|
||||
|
||||
if ( ! empty( $link['nofollow'] ) ) {
|
||||
$rel_string .= 'nofollow ';
|
||||
}
|
||||
|
||||
if ( ! empty( $rel_string ) ) {
|
||||
$url_attrs['rel'] = $rel_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - we deliberately merge $other_attributes second
|
||||
* to allow overriding default attributes values such as a more formatted href
|
||||
*/
|
||||
$url_combined_attrs = array_merge(
|
||||
$url_attrs,
|
||||
$other_attributes,
|
||||
Utils::parse_custom_attributes( $link['custom_attributes'] ?? '' ),
|
||||
);
|
||||
|
||||
return $url_combined_attrs;
|
||||
}
|
||||
|
||||
protected function add_icons_per_row_control(
|
||||
string $name = 'icons_per_row',
|
||||
$options = [
|
||||
'2' => '2',
|
||||
'3' => '3',
|
||||
],
|
||||
string $default = '3',
|
||||
$label = '',
|
||||
$selector_custom_property = '--e-link-in-bio-icon-columns'
|
||||
): void {
|
||||
if ( ! $label ) {
|
||||
$label = esc_html__( 'Icons Per Row', 'elementor' );
|
||||
}
|
||||
$this->add_control(
|
||||
$name,
|
||||
[
|
||||
'label' => $label,
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => $options,
|
||||
'default' => $default,
|
||||
'render_type' => 'template',
|
||||
'selectors' => [
|
||||
'{{WRAPPER}} .e-link-in-bio' => $selector_custom_property . ': {{VALUE}};',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function add_slider_control(
|
||||
string $name,
|
||||
array $args = []
|
||||
): void {
|
||||
$default_args = [
|
||||
'type' => Controls_Manager::SLIDER,
|
||||
'default' => [
|
||||
'unit' => 'px',
|
||||
],
|
||||
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
|
||||
'range' => [
|
||||
'px' => [
|
||||
'min' => 0,
|
||||
'max' => 100,
|
||||
'step' => 1,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->add_control(
|
||||
$name,
|
||||
array_merge_recursive( $default_args, $args )
|
||||
);
|
||||
}
|
||||
|
||||
protected function add_borders_control(
|
||||
string $prefix,
|
||||
array $show_border_args = [],
|
||||
array $border_width_args = [],
|
||||
array $border_color_args = []
|
||||
): void {
|
||||
$show_border = [
|
||||
'label' => esc_html__( 'Border', 'elementor' ),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => esc_html__( 'Yes', 'elementor' ),
|
||||
'label_off' => esc_html__( 'No', 'elementor' ),
|
||||
'return_value' => 'yes',
|
||||
'default' => '',
|
||||
];
|
||||
|
||||
$this->add_control(
|
||||
$prefix . '_show_border',
|
||||
array_merge( $show_border, $show_border_args )
|
||||
);
|
||||
|
||||
$condition = [
|
||||
$prefix . '_show_border' => 'yes',
|
||||
];
|
||||
|
||||
if ( isset( $border_width_args['condition'] ) ) {
|
||||
$condition = array_merge( $condition, $border_width_args['condition'] );
|
||||
unset( $border_width_args['condition'] );
|
||||
}
|
||||
|
||||
$border_width = [
|
||||
'label' => esc_html__( 'Border Width', 'elementor' ) . ' (px)',
|
||||
'type' => Controls_Manager::SLIDER,
|
||||
'size_units' => [ 'px' ],
|
||||
'range' => [
|
||||
'px' => $this->border_width_range,
|
||||
],
|
||||
'condition' => $condition,
|
||||
'default' => [
|
||||
'unit' => 'px',
|
||||
'size' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
$this->add_responsive_control(
|
||||
$prefix . '_border_width',
|
||||
array_merge( $border_width, $border_width_args ),
|
||||
);
|
||||
|
||||
$condition = [
|
||||
$prefix . '_show_border' => 'yes',
|
||||
];
|
||||
|
||||
if ( isset( $border_color_args['condition'] ) ) {
|
||||
$condition = array_merge( $condition, $border_color_args['condition'] );
|
||||
unset( $border_color_args['condition'] );
|
||||
}
|
||||
|
||||
$border_color = [
|
||||
'label' => esc_html__( 'Border Color', 'elementor' ),
|
||||
'type' => Controls_Manager::COLOR,
|
||||
'condition' => $condition,
|
||||
'default' => '#000000',
|
||||
];
|
||||
|
||||
$this->add_control(
|
||||
$prefix . '_border_color',
|
||||
array_merge( $border_color, $border_color_args )
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_shape_divider( $side = 'bottom' ) {
|
||||
$settings = $this->settings;
|
||||
$base_setting_key = "identity_section_style_cover_divider_$side";
|
||||
$file_name = $settings[ $base_setting_key ];
|
||||
|
||||
if ( empty( $file_name ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$negative = ! empty( $settings[ $base_setting_key . '_negative' ] );
|
||||
$shape_path = Shapes::get_shape_path( $file_name, $negative );
|
||||
|
||||
if ( ! is_file( $shape_path ) || ! is_readable( $shape_path ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'negative' => $negative,
|
||||
'svg' => Utils::file_get_contents( $shape_path ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function print_shape_divider( $side = 'bottom' ) {
|
||||
$shape_divider = $this->get_shape_divider( $side );
|
||||
|
||||
if ( empty( $shape_divider ) ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div
|
||||
class="elementor-shape elementor-shape-<?php echo esc_attr( $side ); ?>"
|
||||
data-negative="<?php
|
||||
echo esc_attr( $shape_divider['negative'] ? 'true' : 'false' );
|
||||
?>"
|
||||
>
|
||||
<?php
|
||||
// PHPCS - The file content is being read from a strict file path structure.
|
||||
echo $shape_divider['svg']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
protected function get_configured_breakpoints( $add_desktop = 'true' ) {
|
||||
$active_devices = Plugin::$instance->breakpoints->get_active_devices_list( [ 'reverse' => true ] );
|
||||
$active_breakpoint_instances = Plugin::$instance->breakpoints->get_active_breakpoints();
|
||||
|
||||
$devices_options = [];
|
||||
|
||||
foreach ( $active_devices as $device_key ) {
|
||||
$device_label = 'desktop' === $device_key ? esc_html__( 'Desktop', 'elementor' ) : $active_breakpoint_instances[ $device_key ]->get_label();
|
||||
$devices_options[ $device_key ] = $device_label;
|
||||
}
|
||||
|
||||
return [
|
||||
'active_devices' => $active_devices,
|
||||
'devices_options' => $devices_options,
|
||||
];
|
||||
}
|
||||
|
||||
protected function add_hover_animation_control(
|
||||
string $name,
|
||||
array $args = []
|
||||
): void {
|
||||
|
||||
$this->add_control(
|
||||
$name,
|
||||
array_merge(
|
||||
[
|
||||
'label' => esc_html__( 'Hover Animation', 'elementor' ),
|
||||
'type' => Hover_Animation_Floating_Buttons::TYPE,
|
||||
'frontend_available' => true,
|
||||
'default' => 'grow',
|
||||
],
|
||||
$args
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Behaviors\Interfaces;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
interface Lock_Behavior {
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked();
|
||||
|
||||
/**
|
||||
* @return array {
|
||||
*
|
||||
* @type bool $is_locked
|
||||
*
|
||||
* @type array $badge {
|
||||
* @type string $icon
|
||||
* @type string $text
|
||||
* }
|
||||
*
|
||||
* @type array $content {
|
||||
* @type string $heading
|
||||
* @type string $description
|
||||
* }
|
||||
*
|
||||
* @type array $button {
|
||||
* @type string $text
|
||||
* @type string $url
|
||||
* }
|
||||
*
|
||||
* }
|
||||
*/
|
||||
public function get_config();
|
||||
}
|
||||
154
wp-content/plugins/elementor/core/breakpoints/breakpoint.php
Normal file
154
wp-content/plugins/elementor/core/breakpoints/breakpoint.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Breakpoints;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Breakpoint extends Base_Object {
|
||||
|
||||
private $name;
|
||||
private $label;
|
||||
private $default_value;
|
||||
private $db_key;
|
||||
private $value;
|
||||
private $is_custom;
|
||||
private $direction = 'max';
|
||||
private $is_enabled = false;
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Enabled
|
||||
*
|
||||
* Check if the breakpoint is enabled or not. The breakpoint instance receives this data from
|
||||
* the Breakpoints Manager.
|
||||
*
|
||||
* @return bool $is_enabled class variable
|
||||
*/
|
||||
public function is_enabled() {
|
||||
return $this->is_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Label
|
||||
*
|
||||
* Retrieve the breakpoint label.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return string $label class variable
|
||||
*/
|
||||
public function get_label() {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Value
|
||||
*
|
||||
* Retrieve the saved breakpoint value.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return int $value class variable
|
||||
*/
|
||||
public function get_value() {
|
||||
if ( ! $this->value ) {
|
||||
$this->init_value();
|
||||
}
|
||||
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Custom
|
||||
*
|
||||
* Check if the breakpoint's value is a custom or default value.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return bool $is_custom class variable
|
||||
*/
|
||||
public function is_custom() {
|
||||
if ( ! $this->is_custom ) {
|
||||
$this->get_value();
|
||||
}
|
||||
|
||||
return $this->is_custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Default Value
|
||||
*
|
||||
* Returns the Breakpoint's default value.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return int $default_value class variable
|
||||
*/
|
||||
public function get_default_value() {
|
||||
return $this->default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Direction
|
||||
*
|
||||
* Returns the Breakpoint's direction ('min'/'max').
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return string $direction class variable
|
||||
*/
|
||||
public function get_direction() {
|
||||
return $this->direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Value
|
||||
*
|
||||
* Set the `$value` class variable and the `$is_custom` class variable.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return int $value class variable
|
||||
*/
|
||||
private function init_value() {
|
||||
$cached_value = Plugin::$instance->kits_manager->get_current_settings( $this->db_key );
|
||||
|
||||
if ( $cached_value ) {
|
||||
$this->value = (int) $cached_value;
|
||||
|
||||
$this->is_custom = $this->value !== $this->default_value;
|
||||
} else {
|
||||
$this->value = $this->default_value;
|
||||
|
||||
$this->is_custom = false;
|
||||
}
|
||||
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function __construct( $args ) {
|
||||
$this->name = $args['name'];
|
||||
$this->label = $args['label'];
|
||||
// Used for CSS generation
|
||||
$this->db_key = Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX . $args['name'];
|
||||
$this->direction = $args['direction'];
|
||||
$this->is_enabled = $args['is_enabled'];
|
||||
$this->default_value = $args['default_value'];
|
||||
}
|
||||
}
|
||||
537
wp-content/plugins/elementor/core/breakpoints/manager.php
Normal file
537
wp-content/plugins/elementor/core/breakpoints/manager.php
Normal file
@@ -0,0 +1,537 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Breakpoints;
|
||||
|
||||
use Elementor\Core\Base\Module;
|
||||
use Elementor\Core\Kits\Documents\Tabs\Settings_Layout;
|
||||
use Elementor\Core\Responsive\Files\Frontend;
|
||||
use Elementor\Modules\DevTools\Deprecation;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Manager extends Module {
|
||||
|
||||
const BREAKPOINT_SETTING_PREFIX = 'viewport_';
|
||||
const BREAKPOINT_KEY_MOBILE = 'mobile';
|
||||
const BREAKPOINT_KEY_MOBILE_EXTRA = 'mobile_extra';
|
||||
const BREAKPOINT_KEY_TABLET = 'tablet';
|
||||
const BREAKPOINT_KEY_TABLET_EXTRA = 'tablet_extra';
|
||||
const BREAKPOINT_KEY_LAPTOP = 'laptop';
|
||||
const BREAKPOINT_KEY_DESKTOP = 'desktop';
|
||||
const BREAKPOINT_KEY_WIDESCREEN = 'widescreen';
|
||||
|
||||
/**
|
||||
* Breakpoints
|
||||
*
|
||||
* An array containing instances of the all of the system's available breakpoints.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @access private
|
||||
*
|
||||
* @var Breakpoint[]
|
||||
*/
|
||||
private $breakpoints;
|
||||
|
||||
/**
|
||||
* Active Breakpoints
|
||||
*
|
||||
* An array containing instances of the enabled breakpoints.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @access private
|
||||
*
|
||||
* @var Breakpoint[]
|
||||
*/
|
||||
private $active_breakpoints;
|
||||
|
||||
/**
|
||||
* Responsive Control Duplication Mode.
|
||||
*
|
||||
* Determines the current responsive control generation mode.
|
||||
* Options are:
|
||||
* -- 'on': Responsive controls are duplicated in `add_responsive_control()`.
|
||||
* -- 'off': Responsive controls are NOT duplicated in `add_responsive_control()`.
|
||||
* -- 'dynamic': Responsive controls are only duplicated if their config contains `'dynamic' => 'active' => true`.
|
||||
*
|
||||
* When generating Post CSS, the mode is set to 'on'. When generating Dynamic CSS, the mode is set to 'dynamic'.
|
||||
*
|
||||
* default value is 'off'.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @access private
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $responsive_control_duplication_mode = 'off';
|
||||
|
||||
private $icons_map;
|
||||
|
||||
/**
|
||||
* Has Custom Breakpoints
|
||||
*
|
||||
* A flag that holds a cached value that indicates if there are active custom-breakpoints.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @access private
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $has_custom_breakpoints;
|
||||
|
||||
public function get_name() {
|
||||
return 'breakpoints';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Breakpoints
|
||||
*
|
||||
* Retrieve the array containing instances of all breakpoints existing in the system, or a single breakpoint if a
|
||||
* name is passed.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @param $breakpoint_name
|
||||
* @return Breakpoint[]|Breakpoint
|
||||
*/
|
||||
public function get_breakpoints( $breakpoint_name = null ) {
|
||||
if ( ! $this->breakpoints ) {
|
||||
$this->init_breakpoints();
|
||||
}
|
||||
return self::get_items( $this->breakpoints, $breakpoint_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Active Breakpoints
|
||||
*
|
||||
* Retrieve the array of --enabled-- breakpoints, or a single breakpoint if a name is passed.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @param string|null $breakpoint_name
|
||||
* @return Breakpoint[]|Breakpoint
|
||||
*/
|
||||
public function get_active_breakpoints( $breakpoint_name = null ) {
|
||||
if ( ! $this->active_breakpoints ) {
|
||||
$this->init_active_breakpoints();
|
||||
}
|
||||
|
||||
return self::get_items( $this->active_breakpoints, $breakpoint_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Active Devices List
|
||||
*
|
||||
* Retrieve an array containing the keys of all active devices, including 'desktop'.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @param array $args
|
||||
* @return array
|
||||
*/
|
||||
public function get_active_devices_list( $args = [] ) {
|
||||
$default_args = [
|
||||
'add_desktop' => true,
|
||||
'reverse' => false,
|
||||
'desktop_first' => false,
|
||||
];
|
||||
|
||||
$args = array_merge( $default_args, $args );
|
||||
|
||||
$active_devices = array_keys( Plugin::$instance->breakpoints->get_active_breakpoints() );
|
||||
|
||||
if ( $args['add_desktop'] ) {
|
||||
// Insert the 'desktop' device in the correct position.
|
||||
if ( ! $args['desktop_first'] && in_array( 'widescreen', $active_devices, true ) ) {
|
||||
$widescreen_index = array_search( 'widescreen', $active_devices, true );
|
||||
|
||||
array_splice( $active_devices, $widescreen_index, 0, [ 'desktop' ] );
|
||||
} else {
|
||||
$active_devices[] = 'desktop';
|
||||
}
|
||||
}
|
||||
|
||||
if ( $args['reverse'] ) {
|
||||
$active_devices = array_reverse( $active_devices );
|
||||
}
|
||||
|
||||
return $active_devices;
|
||||
}
|
||||
|
||||
/** Has Custom Breakpoints
|
||||
*
|
||||
* Checks whether there are currently custom breakpoints saved in the database.
|
||||
* Returns true if there are breakpoint values saved in the active kit.
|
||||
* If the user has activated any additional custom breakpoints (mobile extra, tablet extra, laptop, widescreen) -
|
||||
* that is considered as having custom breakpoints.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_custom_breakpoints() {
|
||||
if ( isset( $this->has_custom_breakpoints ) ) {
|
||||
return $this->has_custom_breakpoints;
|
||||
}
|
||||
|
||||
$breakpoints = $this->get_active_breakpoints();
|
||||
|
||||
$additional_breakpoints = [
|
||||
self::BREAKPOINT_KEY_MOBILE_EXTRA,
|
||||
self::BREAKPOINT_KEY_TABLET_EXTRA,
|
||||
self::BREAKPOINT_KEY_LAPTOP,
|
||||
self::BREAKPOINT_KEY_WIDESCREEN,
|
||||
];
|
||||
|
||||
foreach ( $breakpoints as $breakpoint_name => $breakpoint ) {
|
||||
if ( in_array( $breakpoint_name, $additional_breakpoints, true ) ) {
|
||||
$this->has_custom_breakpoints = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var Breakpoint $breakpoint */
|
||||
if ( $breakpoint->is_custom() ) {
|
||||
$this->has_custom_breakpoints = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->has_custom_breakpoints = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Device Min Breakpoint
|
||||
*
|
||||
* For a given device, return the minimum possible breakpoint. Except for the cases of mobile and widescreen
|
||||
* devices, A device's min breakpoint is determined by the previous device's max breakpoint + 1px.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @param string $device_name
|
||||
* @return int the min breakpoint of the passed device
|
||||
*/
|
||||
public function get_device_min_breakpoint( $device_name ) {
|
||||
if ( 'desktop' === $device_name ) {
|
||||
return $this->get_desktop_min_point();
|
||||
}
|
||||
|
||||
$active_breakpoints = $this->get_active_breakpoints();
|
||||
$current_device_breakpoint = $active_breakpoints[ $device_name ];
|
||||
|
||||
// Since this method is called multiple times, usage of class variables is to memory and processing time.
|
||||
// Get only the keys for active breakpoints.
|
||||
$breakpoint_keys = array_keys( $active_breakpoints );
|
||||
|
||||
if ( $breakpoint_keys[0] === $device_name ) {
|
||||
// For the lowest breakpoint, the min point is always 320.
|
||||
$min_breakpoint = 320;
|
||||
} elseif ( 'min' === $current_device_breakpoint->get_direction() ) {
|
||||
// 'min-width' breakpoints only have a minimum point. The breakpoint value itself the device min point.
|
||||
$min_breakpoint = $current_device_breakpoint->get_value();
|
||||
} else {
|
||||
// This block handles all other devices.
|
||||
$device_name_index = array_search( $device_name, $breakpoint_keys, true );
|
||||
|
||||
$previous_index = $device_name_index - 1;
|
||||
$previous_breakpoint_key = $breakpoint_keys[ $previous_index ];
|
||||
/** @var Breakpoint $previous_breakpoint */
|
||||
$previous_breakpoint = $active_breakpoints[ $previous_breakpoint_key ];
|
||||
|
||||
$min_breakpoint = $previous_breakpoint->get_value() + 1;
|
||||
}
|
||||
|
||||
return $min_breakpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Desktop Min Breakpoint
|
||||
*
|
||||
* Returns the minimum possible breakpoint for the default (desktop) device.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*
|
||||
* @return int the min breakpoint of the passed device
|
||||
*/
|
||||
public function get_desktop_min_point() {
|
||||
$active_breakpoints = $this->get_active_breakpoints();
|
||||
$desktop_previous_device = $this->get_desktop_previous_device_key();
|
||||
|
||||
return $active_breakpoints[ $desktop_previous_device ]->get_value() + 1;
|
||||
}
|
||||
|
||||
public function refresh() {
|
||||
unset( $this->has_custom_breakpoints );
|
||||
|
||||
$this->init_breakpoints();
|
||||
$this->init_active_breakpoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Responsive Icons Classes Map
|
||||
*
|
||||
* If a $device parameter is passed, this method retrieves the device's icon class list (the ones attached to the `<i>`
|
||||
* element). If no parameter is passed, it returns an array of devices containing each device's icon class list.
|
||||
*
|
||||
* This method was created because 'mobile_extra' and 'tablet_extra' breakpoint icons need to be tilted by 90
|
||||
* degrees, and this tilt is achieved in CSS via the class `eicon-tilted`.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function get_responsive_icons_classes_map( $device = null ) {
|
||||
if ( ! $this->icons_map ) {
|
||||
$this->icons_map = [
|
||||
'mobile' => 'eicon-device-mobile',
|
||||
'mobile_extra' => 'eicon-device-mobile eicon-tilted',
|
||||
'tablet' => 'eicon-device-tablet',
|
||||
'tablet_extra' => 'eicon-device-tablet eicon-tilted',
|
||||
'laptop' => 'eicon-device-laptop',
|
||||
'desktop' => 'eicon-device-desktop',
|
||||
'widescreen' => 'eicon-device-wide',
|
||||
];
|
||||
}
|
||||
|
||||
return self::get_items( $this->icons_map, $device );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Default Config
|
||||
*
|
||||
* Retrieve the default breakpoints config array. The 'selector' property is used for CSS generation (the
|
||||
* Stylesheet::add_device() method).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_default_config() {
|
||||
return [
|
||||
self::BREAKPOINT_KEY_MOBILE => [
|
||||
'label' => esc_html__( 'Mobile Portrait', 'elementor' ),
|
||||
'default_value' => 767,
|
||||
'direction' => 'max',
|
||||
],
|
||||
self::BREAKPOINT_KEY_MOBILE_EXTRA => [
|
||||
'label' => esc_html__( 'Mobile Landscape', 'elementor' ),
|
||||
'default_value' => 880,
|
||||
'direction' => 'max',
|
||||
],
|
||||
self::BREAKPOINT_KEY_TABLET => [
|
||||
'label' => esc_html__( 'Tablet Portrait', 'elementor' ),
|
||||
'default_value' => 1024,
|
||||
'direction' => 'max',
|
||||
],
|
||||
self::BREAKPOINT_KEY_TABLET_EXTRA => [
|
||||
'label' => esc_html__( 'Tablet Landscape', 'elementor' ),
|
||||
'default_value' => 1200,
|
||||
'direction' => 'max',
|
||||
],
|
||||
self::BREAKPOINT_KEY_LAPTOP => [
|
||||
'label' => esc_html__( 'Laptop', 'elementor' ),
|
||||
'default_value' => 1366,
|
||||
'direction' => 'max',
|
||||
],
|
||||
self::BREAKPOINT_KEY_WIDESCREEN => [
|
||||
'label' => esc_html__( 'Widescreen', 'elementor' ),
|
||||
'default_value' => 2400,
|
||||
'direction' => 'min',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Breakpoints Config
|
||||
*
|
||||
* Iterates over an array of all of the system's breakpoints (both active and inactive), queries each breakpoint's
|
||||
* class instance, and generates an array containing data on each breakpoint: its label, current value, direction
|
||||
* ('min'/'max') and whether it is enabled or not.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_breakpoints_config() {
|
||||
$breakpoints = $this->get_breakpoints();
|
||||
|
||||
$config = [];
|
||||
|
||||
foreach ( $breakpoints as $breakpoint_name => $breakpoint ) {
|
||||
$config[ $breakpoint_name ] = [
|
||||
'label' => $breakpoint->get_label(),
|
||||
'value' => $breakpoint->get_value(),
|
||||
'default_value' => $breakpoint->get_default_value(),
|
||||
'direction' => $breakpoint->get_direction(),
|
||||
'is_enabled' => $breakpoint->is_enabled(),
|
||||
];
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Responsive Control Duplication Mode
|
||||
*
|
||||
* Retrieve the value of the $responsive_control_duplication_mode private class variable.
|
||||
* See the variable's PHPDoc for details.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_responsive_control_duplication_mode() {
|
||||
return $this->responsive_control_duplication_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Responsive Control Duplication Mode
|
||||
*
|
||||
* Sets the value of the $responsive_control_duplication_mode private class variable.
|
||||
* See the variable's PHPDoc for details.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @access public
|
||||
* @param string $mode
|
||||
*/
|
||||
public function set_responsive_control_duplication_mode( $mode ) {
|
||||
$this->responsive_control_duplication_mode = $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Stylesheet Templates Path
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function get_stylesheet_templates_path() {
|
||||
return ELEMENTOR_ASSETS_PATH . 'css/templates/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile Stylesheet Templates
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function compile_stylesheet_templates() {
|
||||
foreach ( self::get_stylesheet_templates() as $file_name => $template_path ) {
|
||||
$file = new Frontend( $file_name, $template_path );
|
||||
|
||||
$file->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init Breakpoints
|
||||
*
|
||||
* Creates the breakpoints array, containing instances of each breakpoint. Returns an array of ALL breakpoints,
|
||||
* both enabled and disabled.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function init_breakpoints() {
|
||||
$breakpoints = [];
|
||||
|
||||
$setting_prefix = self::BREAKPOINT_SETTING_PREFIX;
|
||||
|
||||
$active_breakpoint_keys = [
|
||||
$setting_prefix . self::BREAKPOINT_KEY_MOBILE,
|
||||
$setting_prefix . self::BREAKPOINT_KEY_TABLET,
|
||||
];
|
||||
|
||||
if ( Plugin::$instance->experiments->is_feature_active( 'additional_custom_breakpoints' ) ) {
|
||||
$kit_active_id = Plugin::$instance->kits_manager->get_active_id();
|
||||
// Get the breakpoint settings saved in the kit directly from the DB to avoid initializing the kit too early.
|
||||
$raw_kit_settings = get_post_meta( $kit_active_id, '_elementor_page_settings', true );
|
||||
|
||||
// If there is an existing kit with an active breakpoints value saved, use it.
|
||||
if ( isset( $raw_kit_settings[ Settings_Layout::ACTIVE_BREAKPOINTS_CONTROL_ID ] ) ) {
|
||||
$active_breakpoint_keys = $raw_kit_settings[ Settings_Layout::ACTIVE_BREAKPOINTS_CONTROL_ID ];
|
||||
}
|
||||
}
|
||||
|
||||
$default_config = self::get_default_config();
|
||||
|
||||
foreach ( $default_config as $breakpoint_name => $breakpoint_config ) {
|
||||
$args = [ 'name' => $breakpoint_name ] + $breakpoint_config;
|
||||
|
||||
// Make sure the two default breakpoints (mobile, tablet) are always enabled.
|
||||
if ( self::BREAKPOINT_KEY_MOBILE === $breakpoint_name || self::BREAKPOINT_KEY_TABLET === $breakpoint_name ) {
|
||||
// Make sure the default Mobile and Tablet breakpoints are always enabled.
|
||||
$args['is_enabled'] = true;
|
||||
} else {
|
||||
// If the breakpoint is in the active breakpoints array, make sure it's instantiated as enabled.
|
||||
$args['is_enabled'] = in_array( $setting_prefix . $breakpoint_name, $active_breakpoint_keys, true );
|
||||
}
|
||||
|
||||
$breakpoints[ $breakpoint_name ] = new Breakpoint( $args );
|
||||
}
|
||||
|
||||
$this->breakpoints = $breakpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init Active Breakpoints
|
||||
*
|
||||
* Create/Refresh the array of --enabled-- breakpoints.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function init_active_breakpoints() {
|
||||
$this->active_breakpoints = array_filter( $this->get_breakpoints(), function( $breakpoint ) {
|
||||
/** @var Breakpoint $breakpoint */
|
||||
return $breakpoint->is_enabled();
|
||||
} );
|
||||
}
|
||||
|
||||
private function get_desktop_previous_device_key() {
|
||||
$config_array_keys = array_keys( $this->get_active_breakpoints() );
|
||||
$num_of_devices = count( $config_array_keys );
|
||||
|
||||
// If the widescreen breakpoint is active, the device that's previous to desktop is the last one before
|
||||
// widescreen.
|
||||
if ( self::BREAKPOINT_KEY_WIDESCREEN === $config_array_keys[ $num_of_devices - 1 ] ) {
|
||||
$desktop_previous_device = $config_array_keys[ $num_of_devices - 2 ];
|
||||
} else {
|
||||
// If the widescreen breakpoint isn't active, we just take the last device returned by the config.
|
||||
$desktop_previous_device = $config_array_keys[ $num_of_devices - 1 ];
|
||||
}
|
||||
|
||||
return $desktop_previous_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Stylesheet Templates
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
private static function get_stylesheet_templates() {
|
||||
$templates_paths = glob( self::get_stylesheet_templates_path() . '*.css' );
|
||||
|
||||
$templates = [];
|
||||
|
||||
foreach ( $templates_paths as $template_path ) {
|
||||
$file_name = 'custom-' . basename( $template_path );
|
||||
|
||||
$templates[ $file_name ] = $template_path;
|
||||
}
|
||||
|
||||
$deprecated_hook = 'elementor/core/responsive/get_stylesheet_templates';
|
||||
$replacement_hook = 'elementor/core/breakpoints/get_stylesheet_template';
|
||||
|
||||
/**
|
||||
* @type Deprecation $deprecation_module
|
||||
*/
|
||||
$deprecation_module = Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation;
|
||||
|
||||
// TODO: REMOVE THIS DEPRECATED HOOK IN ELEMENTOR v3.10.0/v4.0.0
|
||||
$templates = $deprecation_module->apply_deprecated_filter( $deprecated_hook, [ $templates ], '3.2.0', $replacement_hook );
|
||||
|
||||
return apply_filters( $replacement_hook, $templates );
|
||||
}
|
||||
}
|
||||
290
wp-content/plugins/elementor/core/common/app.php
Normal file
290
wp-content/plugins/elementor/core/common/app.php
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common;
|
||||
|
||||
use Elementor\Core\Base\App as BaseApp;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Core\Common\Modules\Finder\Module as Finder;
|
||||
use Elementor\Core\Common\Modules\Connect\Module as Connect;
|
||||
use Elementor\Core\Common\Modules\EventTracker\Module as Event_Tracker;
|
||||
use Elementor\Core\Files\Uploads_Manager;
|
||||
use Elementor\Core\Settings\Manager as SettingsManager;
|
||||
use Elementor\Icons_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* App
|
||||
*
|
||||
* Elementor's common app that groups shared functionality, components and configuration
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class App extends BaseApp {
|
||||
|
||||
private $templates = [];
|
||||
|
||||
/**
|
||||
* App constructor.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->add_default_templates();
|
||||
|
||||
add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'register_scripts' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] );
|
||||
|
||||
add_action( 'elementor/editor/before_enqueue_styles', [ $this, 'register_styles' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'register_styles' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'register_styles' ], 9 );
|
||||
|
||||
add_action( 'elementor/editor/footer', [ $this, 'print_templates' ] );
|
||||
add_action( 'admin_footer', [ $this, 'print_templates' ] );
|
||||
add_action( 'wp_footer', [ $this, 'print_templates' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Init components
|
||||
*
|
||||
* Initializing common components.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function init_components() {
|
||||
$this->add_component( 'ajax', new Ajax() );
|
||||
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
if ( ! is_customize_preview() ) {
|
||||
$this->add_component( 'finder', new Finder() );
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_component( 'connect', new Connect() );
|
||||
|
||||
$this->add_component( 'event-tracker', new Event_Tracker() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* Retrieve the app name.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string Common app name.
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'common';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register scripts.
|
||||
*
|
||||
* Register common scripts.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_scripts() {
|
||||
wp_register_script(
|
||||
'elementor-common-modules',
|
||||
$this->get_js_assets_url( 'common-modules' ),
|
||||
[],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'backbone-marionette',
|
||||
$this->get_js_assets_url( 'backbone.marionette', 'assets/lib/backbone/' ),
|
||||
[
|
||||
'backbone',
|
||||
],
|
||||
'2.4.5.e1',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'backbone-radio',
|
||||
$this->get_js_assets_url( 'backbone.radio', 'assets/lib/backbone/' ),
|
||||
[
|
||||
'backbone',
|
||||
],
|
||||
'1.0.4',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'elementor-dialog',
|
||||
$this->get_js_assets_url( 'dialog', 'assets/lib/dialog/' ),
|
||||
[
|
||||
'jquery-ui-position',
|
||||
],
|
||||
'4.9.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'elementor-common',
|
||||
$this->get_js_assets_url( 'common' ),
|
||||
[
|
||||
'jquery',
|
||||
'jquery-ui-draggable',
|
||||
'backbone-marionette',
|
||||
'backbone-radio',
|
||||
'elementor-common-modules',
|
||||
'elementor-web-cli',
|
||||
'elementor-dialog',
|
||||
'wp-api-request',
|
||||
'elementor-dev-tools',
|
||||
],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_set_script_translations( 'elementor-common', 'elementor' );
|
||||
|
||||
$this->print_config();
|
||||
|
||||
// Used for external plugins.
|
||||
do_action( 'elementor/common/after_register_scripts', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register styles.
|
||||
*
|
||||
* Register common styles.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_styles() {
|
||||
wp_register_style(
|
||||
'elementor-icons',
|
||||
$this->get_css_assets_url( 'elementor-icons', 'assets/lib/eicons/css/' ),
|
||||
[],
|
||||
Icons_Manager::ELEMENTOR_ICONS_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'elementor-common',
|
||||
$this->get_css_assets_url( 'common', null, 'default', true ),
|
||||
[
|
||||
'elementor-icons',
|
||||
],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'e-theme-ui-light',
|
||||
$this->get_css_assets_url( 'theme-light' ),
|
||||
[],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add template.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $template Can be either a link to template file or template
|
||||
* HTML content.
|
||||
* @param string $type Optional. Whether to handle the template as path
|
||||
* or text. Default is `path`.
|
||||
*/
|
||||
public function add_template( $template, $type = 'path' ) {
|
||||
if ( 'path' === $type ) {
|
||||
ob_start();
|
||||
|
||||
include $template;
|
||||
|
||||
$template = ob_get_clean();
|
||||
}
|
||||
|
||||
$this->templates[] = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print Templates
|
||||
*
|
||||
* Prints all registered templates.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function print_templates() {
|
||||
foreach ( $this->templates as $template ) {
|
||||
echo $template; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* Define the default/initial settings of the common app.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
$active_experimental_features = Plugin::$instance->experiments->get_active_features();
|
||||
|
||||
$active_experimental_features = array_fill_keys( array_keys( $active_experimental_features ), true );
|
||||
|
||||
$config = [
|
||||
'version' => ELEMENTOR_VERSION,
|
||||
'isRTL' => is_rtl(),
|
||||
'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
|
||||
'isElementorDebug' => ( defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG ),
|
||||
'activeModules' => array_keys( $this->get_components() ),
|
||||
'experimentalFeatures' => $active_experimental_features,
|
||||
'urls' => [
|
||||
'assets' => ELEMENTOR_ASSETS_URL,
|
||||
'rest' => get_rest_url(),
|
||||
],
|
||||
'filesUpload' => [
|
||||
'unfilteredFiles' => Uploads_Manager::are_unfiltered_uploads_enabled(),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Localize common settings.
|
||||
*
|
||||
* Filters the editor localized settings.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $config Common configuration.
|
||||
*/
|
||||
return apply_filters( 'elementor/common/localize_settings', $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default templates.
|
||||
*
|
||||
* Register common app default templates.
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*/
|
||||
private function add_default_templates() {
|
||||
$default_templates = [
|
||||
'includes/editor-templates/library-layout.php',
|
||||
];
|
||||
|
||||
foreach ( $default_templates as $template ) {
|
||||
$this->add_template( ELEMENTOR_PATH . $template );
|
||||
}
|
||||
}
|
||||
}
|
||||
325
wp-content/plugins/elementor/core/common/modules/ajax/module.php
Normal file
325
wp-content/plugins/elementor/core/common/modules/ajax/module.php
Normal file
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Ajax;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Utils\Exceptions;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor ajax manager.
|
||||
*
|
||||
* Elementor ajax manager handler class is responsible for handling Elementor
|
||||
* ajax requests, ajax responses and registering actions applied on them.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
const NONCE_KEY = 'elementor_ajax';
|
||||
|
||||
/**
|
||||
* Ajax actions.
|
||||
*
|
||||
* Holds all the register ajax action.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ajax_actions = [];
|
||||
|
||||
/**
|
||||
* Ajax requests.
|
||||
*
|
||||
* Holds all the register ajax requests.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $requests = [];
|
||||
|
||||
/**
|
||||
* Ajax response data.
|
||||
*
|
||||
* Holds all the response data for all the ajax requests.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $response_data = [];
|
||||
|
||||
/**
|
||||
* Current ajax action ID.
|
||||
*
|
||||
* Holds all the ID for the current ajax action.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $current_action_id = null;
|
||||
|
||||
/**
|
||||
* Ajax manager constructor.
|
||||
*
|
||||
* Initializing Elementor ajax manager.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_elementor_ajax', [ $this, 'handle_ajax_request' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module name.
|
||||
*
|
||||
* Retrieve the module name.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*
|
||||
* @return string Module name.
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'ajax';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ajax action.
|
||||
*
|
||||
* Add new actions for a specific ajax request and the callback function to
|
||||
* be handle the response.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $tag Ajax request name/tag.
|
||||
* @param callable $callback The callback function.
|
||||
*/
|
||||
public function register_ajax_action( $tag, $callback ) {
|
||||
if ( ! did_action( 'elementor/ajax/register_actions' ) ) {
|
||||
_doing_it_wrong( __METHOD__, esc_html( sprintf( 'Use `%s` hook to register ajax action.', 'elementor/ajax/register_actions' ) ), '2.0.0' );
|
||||
}
|
||||
|
||||
$this->ajax_actions[ $tag ] = compact( 'tag', 'callback' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ajax request.
|
||||
*
|
||||
* Verify ajax nonce, and run all the registered actions for this request.
|
||||
*
|
||||
* Fired by `wp_ajax_elementor_ajax` action.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function handle_ajax_request() {
|
||||
if ( ! $this->verify_request_nonce() ) {
|
||||
$this->add_response_data( false, esc_html__( 'Token Expired.', 'elementor' ) )
|
||||
->send_error( Exceptions::UNAUTHORIZED );
|
||||
}
|
||||
|
||||
$editor_post_id = 0;
|
||||
|
||||
if ( ! empty( $_REQUEST['editor_post_id'] ) ) {
|
||||
$editor_post_id = absint( $_REQUEST['editor_post_id'] );
|
||||
|
||||
Plugin::$instance->db->switch_to_post( $editor_post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ajax actions.
|
||||
*
|
||||
* Fires when an ajax request is received and verified.
|
||||
*
|
||||
* Used to register new ajax action handles.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param self $this An instance of ajax manager.
|
||||
*/
|
||||
do_action( 'elementor/ajax/register_actions', $this );
|
||||
|
||||
if ( ! empty( $_REQUEST['actions'] ) ) {
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, each action should sanitize its own data.
|
||||
$this->requests = json_decode( wp_unslash( $_REQUEST['actions'] ), true );
|
||||
}
|
||||
|
||||
foreach ( $this->requests as $id => $action_data ) {
|
||||
$this->current_action_id = $id;
|
||||
|
||||
if ( ! isset( $this->ajax_actions[ $action_data['action'] ] ) ) {
|
||||
$this->add_response_data( false, esc_html__( 'Action not found.', 'elementor' ), Exceptions::BAD_REQUEST );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $editor_post_id ) {
|
||||
$action_data['data']['editor_post_id'] = $editor_post_id;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $action_data['data'] ?? [];
|
||||
$results = call_user_func( $this->ajax_actions[ $action_data['action'] ]['callback'], $data, $this );
|
||||
|
||||
if ( false === $results ) {
|
||||
$this->add_response_data( false );
|
||||
} else {
|
||||
$this->add_response_data( true, $results );
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
$this->add_response_data( false, $e->getMessage(), $e->getCode() );
|
||||
}
|
||||
}
|
||||
|
||||
$this->current_action_id = null;
|
||||
|
||||
$this->send_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current action data.
|
||||
*
|
||||
* Retrieve the data for the current ajax request.
|
||||
*
|
||||
* @since 2.0.1
|
||||
* @access public
|
||||
*
|
||||
* @return bool|mixed Ajax request data if action exist, False otherwise.
|
||||
*/
|
||||
public function get_current_action_data() {
|
||||
if ( ! $this->current_action_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->requests[ $this->current_action_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create nonce.
|
||||
*
|
||||
* Creates a cryptographic token to
|
||||
* give the user an access to Elementor ajax actions.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string The nonce token.
|
||||
*/
|
||||
public function create_nonce() {
|
||||
return wp_create_nonce( self::NONCE_KEY );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify request nonce.
|
||||
*
|
||||
* Whether the request nonce verified or not.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return bool True if request nonce verified, False otherwise.
|
||||
*/
|
||||
public function verify_request_nonce() {
|
||||
return wp_verify_nonce( Utils::get_super_global_value( $_REQUEST, '_nonce' ), self::NONCE_KEY );
|
||||
}
|
||||
|
||||
protected function get_init_settings() {
|
||||
return [
|
||||
'url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => $this->create_nonce(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax success response.
|
||||
*
|
||||
* Send a JSON response data back to the ajax request, indicating success.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*/
|
||||
private function send_success() {
|
||||
$response = [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'responses' => $this->response_data,
|
||||
],
|
||||
];
|
||||
|
||||
$json = wp_json_encode( $response );
|
||||
|
||||
while ( ob_get_status() ) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
if ( function_exists( 'gzencode' ) ) {
|
||||
$response = gzencode( $json );
|
||||
|
||||
header( 'Content-Type: application/json; charset=utf-8' );
|
||||
header( 'Content-Encoding: gzip' );
|
||||
header( 'Content-Length: ' . strlen( $response ) );
|
||||
|
||||
echo $response; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} else {
|
||||
echo $json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
wp_die( '', '', [ 'response' => null ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax failure response.
|
||||
*
|
||||
* Send a JSON response data back to the ajax request, indicating failure.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @param null $code
|
||||
*/
|
||||
private function send_error( $code = null ) {
|
||||
wp_send_json_error( [
|
||||
'responses' => $this->response_data,
|
||||
], $code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add response data.
|
||||
*
|
||||
* Add new response data to the array of all the ajax requests.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @param bool $success True if the requests returned successfully, False
|
||||
* otherwise.
|
||||
* @param mixed $data Optional. Response data. Default is null.
|
||||
*
|
||||
* @param int $code Optional. Response code. Default is 200.
|
||||
*
|
||||
* @return Module An instance of ajax manager.
|
||||
*/
|
||||
private function add_response_data( $success, $data = null, $code = 200 ) {
|
||||
$this->response_data[ $this->current_action_id ] = [
|
||||
'success' => $success,
|
||||
'code' => $code,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Admin_Menu_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Settings;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Admin {
|
||||
|
||||
const PAGE_ID = 'elementor-connect';
|
||||
|
||||
public static $url = '';
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_admin_menu( Admin_Menu_Manager $admin_menu ) {
|
||||
$admin_menu->register( static::PAGE_ID, new Connect_Menu_Item() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function on_load_page() {
|
||||
if ( isset( $_GET['action'], $_GET['app'] ) ) {
|
||||
$manager = Plugin::$instance->common->get_component( 'connect' );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$app_slug = Utils::get_super_global_value( $_GET, 'app' );
|
||||
$app = $manager->get_app( $app_slug );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$action = Utils::get_super_global_value( $_GET, 'action' );
|
||||
|
||||
$nonce_action = $app_slug . $action;
|
||||
|
||||
if ( ! $app ) {
|
||||
wp_die( 'Unknown app: ' . esc_attr( $app_slug ) );
|
||||
}
|
||||
|
||||
if ( ! wp_verify_nonce( Utils::get_super_global_value( $_GET, 'nonce' ), $nonce_action ) ) {
|
||||
wp_die( 'Invalid Nonce', 'Invalid Nonce', [
|
||||
'back_link' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
$method = 'action_' . $action;
|
||||
|
||||
if ( method_exists( $app, $method ) ) {
|
||||
call_user_func( [ $app, $method ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
self::$url = admin_url( 'admin.php?page=' . self::PAGE_ID );
|
||||
|
||||
add_action( 'elementor/admin/menu/register', [ $this, 'register_admin_menu' ] );
|
||||
|
||||
add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) {
|
||||
if ( ! empty( $hooks[ static::PAGE_ID ] ) ) {
|
||||
add_action( 'load-' . $hooks[ static::PAGE_ID ], [ $this, 'on_load_page' ] );
|
||||
}
|
||||
}, 10, 2 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
use Elementor\Core\Admin\Admin_Notices;
|
||||
use Elementor\Core\Common\Modules\Connect\Admin;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Core\Utils\Http;
|
||||
use Elementor\Core\Utils\Str;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tracker;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base_App {
|
||||
|
||||
const OPTION_NAME_PREFIX = 'elementor_connect_';
|
||||
|
||||
const OPTION_CONNECT_SITE_KEY = self::OPTION_NAME_PREFIX . 'site_key';
|
||||
|
||||
const SITE_URL = 'https://my.elementor.com/connect/v1';
|
||||
|
||||
const API_URL = 'https://my.elementor.com/api/connect/v1';
|
||||
|
||||
const HTTP_RETURN_TYPE_OBJECT = 'object';
|
||||
const HTTP_RETURN_TYPE_ARRAY = 'array';
|
||||
|
||||
protected $data = [];
|
||||
|
||||
protected $auth_mode = '';
|
||||
|
||||
/**
|
||||
* @var Http
|
||||
*/
|
||||
protected $http;
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
* @abstract
|
||||
* TODO: make it public.
|
||||
*/
|
||||
abstract protected function get_slug();
|
||||
|
||||
/**
|
||||
* @since 2.8.0
|
||||
* @access public
|
||||
* TODO: make it abstract.
|
||||
*/
|
||||
public function get_title() {
|
||||
return $this->get_slug();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
* @abstract
|
||||
*/
|
||||
abstract protected function update_settings();
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function get_class_name() {
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @abstract
|
||||
*/
|
||||
public function render_admin_widget() {
|
||||
// PHPCS - the method get_title return a plain string.
|
||||
echo '<h2>' . $this->get_title() . '</h2>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( $this->is_connected() ) {
|
||||
$remote_user = $this->get( 'user' );
|
||||
$title = sprintf(
|
||||
/* translators: %s: Remote user. */
|
||||
esc_html__( 'Connected as %s', 'elementor' ),
|
||||
'<strong>' . esc_html( $remote_user->email ) . '</strong>'
|
||||
);
|
||||
$label = esc_html__( 'Disconnect', 'elementor' );
|
||||
$url = $this->get_admin_url( 'disconnect' );
|
||||
$attr = '';
|
||||
|
||||
echo sprintf(
|
||||
'%s <a %s href="%s">%s</a>',
|
||||
// PHPCS - the variable $title is already escaped above.
|
||||
$title, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
// PHPCS - the variable $attr is a plain string.
|
||||
$attr, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
esc_attr( $url ),
|
||||
esc_html( $label )
|
||||
);
|
||||
} else {
|
||||
echo 'Not Connected';
|
||||
}
|
||||
|
||||
echo '<hr>';
|
||||
|
||||
$this->print_app_info();
|
||||
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
printf( '<div><a href="%s">%s</a></div>', esc_url( $this->get_admin_url( 'reset' ) ), esc_html__( 'Reset Data', 'elementor' ) );
|
||||
}
|
||||
|
||||
echo '<hr>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_option_name() {
|
||||
return static::OPTION_NAME_PREFIX . $this->get_slug();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function admin_notice() {
|
||||
$notices = $this->get( 'notices' );
|
||||
|
||||
if ( ! $notices ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->print_notices( $notices );
|
||||
|
||||
$this->delete( 'notices' );
|
||||
}
|
||||
|
||||
|
||||
public function get_app_token_from_cli_token( $cli_token ) {
|
||||
$response = $this->request( 'get_app_token_from_cli_token', [
|
||||
'cli_token' => $cli_token,
|
||||
] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
// PHPCS - the variable $response does not contain a user input value.
|
||||
wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
// Use state as usual.
|
||||
$_REQUEST['state'] = $this->get( 'state' );
|
||||
$_REQUEST['code'] = $response->code;
|
||||
}
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_authorize() {
|
||||
if ( $this->is_connected() ) {
|
||||
$this->add_notice( esc_html__( 'Already connected.', 'elementor' ), 'info' );
|
||||
$this->redirect_to_admin_page();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_client_id();
|
||||
$this->set_request_state();
|
||||
|
||||
$this->redirect_to_remote_authorize_url();
|
||||
}
|
||||
|
||||
public function action_reset() {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
delete_option( static::OPTION_CONNECT_SITE_KEY );
|
||||
delete_option( 'elementor_remote_info_library' );
|
||||
}
|
||||
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_get_token() {
|
||||
if ( $this->is_connected() ) {
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
//phpcs:ignore WordPress.Security.NonceVerification.Recommended - The user as been authorized before in 'connect'.
|
||||
$state = Utils::get_super_global_value( $_REQUEST, 'state' );
|
||||
|
||||
if ( $state !== $this->get( 'state' ) ) {
|
||||
$this->add_notice( 'Get Token: Invalid Request.', 'error' );
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
$response = $this->request( 'get_token', [
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => Utils::get_super_global_value( $_REQUEST, 'code' ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
'redirect_uri' => rawurlencode( $this->get_admin_url( 'get_token' ) ),
|
||||
'client_id' => $this->get( 'client_id' ),
|
||||
] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$notice = 'Cannot Get Token:' . $response->get_error_message();
|
||||
$this->add_notice( $notice, 'error' );
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
$this->delete( 'state' );
|
||||
$this->set( (array) $response );
|
||||
|
||||
if ( ! empty( $response->data_share_opted_in ) && current_user_can( 'manage_options' ) ) {
|
||||
Tracker::set_opt_in( true );
|
||||
}
|
||||
|
||||
$this->after_connect();
|
||||
|
||||
// Add the notice *after* the method `after_connect`, so an app can redirect without the notice.
|
||||
$this->add_notice( esc_html__( 'Connected successfully.', 'elementor' ) );
|
||||
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_disconnect() {
|
||||
if ( $this->is_connected() ) {
|
||||
$this->disconnect();
|
||||
$this->add_notice( esc_html__( 'Disconnected successfully.', 'elementor' ) );
|
||||
}
|
||||
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_reconnect() {
|
||||
$this->disconnect();
|
||||
|
||||
$this->action_authorize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_admin_url( $action, $params = [] ) {
|
||||
$params = [
|
||||
'app' => $this->get_slug(),
|
||||
'action' => $action,
|
||||
'nonce' => wp_create_nonce( $this->get_slug() . $action ),
|
||||
] + $params;
|
||||
|
||||
$admin_url = Str::encode_idn_url( get_admin_url() );
|
||||
$admin_url .= 'admin.php?page=' . Admin::PAGE_ID;
|
||||
|
||||
return add_query_arg( $params, $admin_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function is_connected() {
|
||||
return (bool) $this->get( 'access_token' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init() {}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init_data() {}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function after_connect() {}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get( $key, $default = null ) {
|
||||
$this->init_data();
|
||||
|
||||
return isset( $this->data[ $key ] ) ? $this->data[ $key ] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function set( $key, $value = null ) {
|
||||
$this->init_data();
|
||||
|
||||
if ( is_array( $key ) ) {
|
||||
$this->data = array_replace_recursive( $this->data, $key );
|
||||
} else {
|
||||
$this->data[ $key ] = $value;
|
||||
}
|
||||
|
||||
$this->update_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function delete( $key = null ) {
|
||||
$this->init_data();
|
||||
|
||||
if ( $key ) {
|
||||
unset( $this->data[ $key ] );
|
||||
} else {
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
$this->update_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function add( $key, $value, $default = '' ) {
|
||||
$new_value = $this->get( $key, $default );
|
||||
|
||||
if ( is_array( $new_value ) ) {
|
||||
$new_value[] = $value;
|
||||
} elseif ( is_string( $new_value ) ) {
|
||||
$new_value .= $value;
|
||||
} elseif ( is_numeric( $new_value ) ) {
|
||||
$new_value += $value;
|
||||
}
|
||||
|
||||
$this->set( $key, $new_value );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function add_notice( $content, $type = 'success' ) {
|
||||
$this->add( 'notices', compact( 'content', 'type' ), [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $action
|
||||
* @param array $request_body
|
||||
* @param false $as_array
|
||||
*
|
||||
* @return mixed|\WP_Error
|
||||
*/
|
||||
protected function request( $action, $request_body = [], $as_array = false ) {
|
||||
$request_body = $this->get_connect_info() + $request_body;
|
||||
|
||||
return $this->http_request(
|
||||
'POST',
|
||||
$action,
|
||||
[
|
||||
'timeout' => 25,
|
||||
'body' => $request_body,
|
||||
'headers' => $this->is_connected() ?
|
||||
[ 'X-Elementor-Signature' => $this->generate_signature( $request_body ) ] :
|
||||
[],
|
||||
],
|
||||
[
|
||||
'return_type' => $as_array ? static::HTTP_RETURN_TYPE_ARRAY : static::HTTP_RETURN_TYPE_OBJECT,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Base Connect Info
|
||||
*
|
||||
* Returns an array of connect info.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_base_connect_info() {
|
||||
return [
|
||||
'app' => $this->get_slug(),
|
||||
'access_token' => $this->get( 'access_token' ),
|
||||
'client_id' => $this->get( 'client_id' ),
|
||||
'local_id' => get_current_user_id(),
|
||||
'site_key' => $this->get_site_key(),
|
||||
'home_url' => trailingslashit( home_url() ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the connect information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_connect_info() {
|
||||
$connect_info = $this->get_base_connect_info();
|
||||
|
||||
$additional_info = [];
|
||||
|
||||
/**
|
||||
* Additional connect info.
|
||||
*
|
||||
* Filters the connection information when connecting to Elementor servers.
|
||||
* This hook can be used to add more information or add more data.
|
||||
*
|
||||
* @param array $additional_info Additional connecting information array.
|
||||
* @param Base_App $this The base app instance.
|
||||
*/
|
||||
$additional_info = apply_filters( 'elementor/connect/additional-connect-info', $additional_info, $this );
|
||||
|
||||
return array_merge( $connect_info, $additional_info );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $endpoint
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function generate_authentication_headers( $endpoint ) {
|
||||
$connect_info = ( new Collection( $this->get_connect_info() ) )
|
||||
->map_with_keys( function ( $value, $key ) {
|
||||
// For bc `get_connect_info` returns the connect info with underscore,
|
||||
// headers with underscore are not valid, so all the keys with underscore will be replaced to hyphen.
|
||||
return [ str_replace( '_', '-', $key ) => $value ];
|
||||
} )
|
||||
->replace_recursive( [ 'endpoint' => $endpoint ] )
|
||||
->sort_keys();
|
||||
|
||||
return $connect_info
|
||||
->merge( [ 'X-Elementor-Signature' => $this->generate_signature( $connect_info->all() ) ] )
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an http request
|
||||
*
|
||||
* @param $method
|
||||
* @param $endpoint
|
||||
* @param array $args
|
||||
* @param array $options
|
||||
*
|
||||
* @return mixed|\WP_Error
|
||||
*/
|
||||
protected function http_request( $method, $endpoint, $args = [], $options = [] ) {
|
||||
$options = wp_parse_args( $options, [
|
||||
'return_type' => static::HTTP_RETURN_TYPE_OBJECT,
|
||||
] );
|
||||
|
||||
$args = array_replace_recursive( [
|
||||
'headers' => $this->is_connected() ? $this->generate_authentication_headers( $endpoint ) : [],
|
||||
'method' => $method,
|
||||
'timeout' => 10,
|
||||
], $args );
|
||||
|
||||
$response = $this->http->request_with_fallback(
|
||||
$this->get_generated_urls( $endpoint ),
|
||||
$args
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
// PHPCS - the variable $response does not contain a user input value.
|
||||
wp_die( $response, [ 'back_link' => true ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( ! $response_code ) {
|
||||
return new \WP_Error( 500, 'No Response' );
|
||||
}
|
||||
|
||||
// Server sent a success message without content.
|
||||
if ( 'null' === $body ) {
|
||||
$body = true;
|
||||
}
|
||||
|
||||
$body = json_decode( $body, static::HTTP_RETURN_TYPE_ARRAY === $options['return_type'] );
|
||||
|
||||
if ( false === $body ) {
|
||||
return new \WP_Error( 422, 'Wrong Server Response' );
|
||||
}
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
// In case $as_array = true.
|
||||
$body = (object) $body;
|
||||
|
||||
$message = isset( $body->message ) ? $body->message : wp_remote_retrieve_response_message( $response );
|
||||
$code = (int) ( isset( $body->code ) ? $body->code : $response_code );
|
||||
|
||||
if ( ! $code ) {
|
||||
$code = $response_code;
|
||||
}
|
||||
|
||||
if ( 401 === $code ) {
|
||||
$this->delete();
|
||||
|
||||
$should_retry = ! in_array( $this->auth_mode, [ 'xhr', 'cli' ], true );
|
||||
|
||||
if ( $should_retry ) {
|
||||
$this->action_authorize();
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $options['with_error_data'] ) && true === $options['with_error_data'] ) {
|
||||
return new \WP_Error( $code, $message, $body );
|
||||
}
|
||||
|
||||
return new \WP_Error( $code, $message );
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a signature for the http request
|
||||
*
|
||||
* @param array $payload
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
private function generate_signature( $payload = [] ) {
|
||||
return hash_hmac(
|
||||
'sha256',
|
||||
wp_json_encode( $payload, JSON_NUMERIC_CHECK ),
|
||||
$this->get( 'access_token_secret' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_api_url() {
|
||||
return static::API_URL . '/' . $this->get_slug();
|
||||
}
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_remote_site_url() {
|
||||
return static::SITE_URL . '/' . $this->get_slug();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_remote_authorize_url() {
|
||||
$redirect_uri = $this->get_auth_redirect_uri();
|
||||
|
||||
$allowed_query_params_to_propagate = [
|
||||
'utm_source',
|
||||
'utm_medium',
|
||||
'utm_campaign',
|
||||
'utm_term',
|
||||
'utm_content',
|
||||
'source',
|
||||
'screen_hint',
|
||||
];
|
||||
|
||||
$query_params = ( new Collection( $_GET ) ) // phpcs:ignore
|
||||
->only( $allowed_query_params_to_propagate )
|
||||
->merge( [
|
||||
'action' => 'authorize',
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->get( 'client_id' ),
|
||||
'auth_secret' => $this->get( 'auth_secret' ),
|
||||
'state' => $this->get( 'state' ),
|
||||
'redirect_uri' => rawurlencode( $redirect_uri ),
|
||||
'may_share_data' => current_user_can( 'manage_options' ) && ! Tracker::is_allow_track(),
|
||||
'reconnect_nonce' => wp_create_nonce( $this->get_slug() . 'reconnect' ),
|
||||
] );
|
||||
|
||||
return add_query_arg( $query_params->all(), $this->get_remote_site_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function redirect_to_admin_page( $url = '' ) {
|
||||
if ( ! $url ) {
|
||||
$url = Admin::$url;
|
||||
}
|
||||
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'popup':
|
||||
$this->print_popup_close_script( $url );
|
||||
break;
|
||||
|
||||
case 'cli':
|
||||
$this->admin_notice();
|
||||
die;
|
||||
|
||||
default:
|
||||
wp_safe_redirect( $url );
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function set_client_id() {
|
||||
$source = Utils::get_super_global_value( $_REQUEST, 'source' ) ?? ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
$response = $this->request(
|
||||
'get_client_id',
|
||||
[
|
||||
'source' => esc_attr( $source ),
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
// PHPCS - the variable $response does not contain a user input value.
|
||||
wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$this->set( 'client_id', $response->client_id );
|
||||
$this->set( 'auth_secret', $response->auth_secret );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function set_request_state() {
|
||||
$this->set( 'state', wp_generate_password( 12, false ) );
|
||||
}
|
||||
|
||||
protected function get_popup_success_event_data() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function print_popup_close_script( $url ) {
|
||||
$data = $this->get_popup_success_event_data();
|
||||
|
||||
?>
|
||||
<script>
|
||||
if ( opener && opener !== window ) {
|
||||
opener.jQuery( 'body' ).trigger(
|
||||
'elementor/connect/success/<?php echo esc_attr( Utils::get_super_global_value( $_REQUEST, 'callback_id' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. ?>',
|
||||
<?php echo wp_json_encode( $data ); ?>
|
||||
);
|
||||
|
||||
window.close();
|
||||
opener.focus();
|
||||
} else {
|
||||
location = '<?php echo esc_url( $url ); ?>';
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function disconnect() {
|
||||
if ( $this->is_connected() ) {
|
||||
// Try update the server, but not needed to handle errors.
|
||||
$this->request( 'disconnect' );
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
public function get_site_key() {
|
||||
$site_key = get_option( static::OPTION_CONNECT_SITE_KEY );
|
||||
|
||||
if ( ! $site_key ) {
|
||||
$site_key = md5( uniqid( wp_generate_password() ) );
|
||||
update_option( static::OPTION_CONNECT_SITE_KEY, $site_key );
|
||||
}
|
||||
|
||||
return $site_key;
|
||||
}
|
||||
|
||||
protected function redirect_to_remote_authorize_url() {
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'cli':
|
||||
$this->get_app_token_from_cli_token( Utils::get_super_global_value( $_REQUEST, 'token' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
return;
|
||||
default:
|
||||
wp_redirect( $this->get_remote_authorize_url() ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Safe redirect is used here.
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
protected function get_auth_redirect_uri() {
|
||||
$redirect_uri = $this->get_admin_url( 'get_token' );
|
||||
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'popup':
|
||||
$redirect_uri = add_query_arg( [
|
||||
'mode' => 'popup',
|
||||
'callback_id' => esc_attr( Utils::get_super_global_value( $_REQUEST, 'callback_id' ) ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
], $redirect_uri );
|
||||
break;
|
||||
}
|
||||
|
||||
return $redirect_uri;
|
||||
}
|
||||
|
||||
|
||||
protected function print_notices( $notices ) {
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'cli':
|
||||
foreach ( $notices as $notice ) {
|
||||
printf( '[%s] %s', wp_kses_post( $notice['type'] ), wp_kses_post( $notice['content'] ) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/**
|
||||
* @var Admin_Notices $admin_notices
|
||||
*/
|
||||
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
|
||||
|
||||
foreach ( $notices as $notice ) {
|
||||
$options = [
|
||||
'description' => wp_kses_post( wpautop( $notice['content'] ) ),
|
||||
'type' => $notice['type'],
|
||||
'icon' => false,
|
||||
];
|
||||
|
||||
$admin_notices->print_admin_notice( $options );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function get_app_info() {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function print_app_info() {
|
||||
$app_info = $this->get_app_info();
|
||||
|
||||
foreach ( $app_info as $key => $item ) {
|
||||
if ( $item['value'] ) {
|
||||
$status = 'Exist';
|
||||
$color = 'green';
|
||||
} else {
|
||||
$status = 'Empty';
|
||||
$color = 'red';
|
||||
}
|
||||
|
||||
// PHPCS - the values of $item['label'], $color, $status are plain strings.
|
||||
printf( '%s: <strong style="color:%s">%s</strong><br>', $item['label'], $color, $status ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function get_generated_urls( $endpoint ) {
|
||||
$base_urls = $this->get_api_url();
|
||||
|
||||
if ( ! is_array( $base_urls ) ) {
|
||||
$base_urls = [ $base_urls ];
|
||||
}
|
||||
|
||||
return array_map( function ( $base_url ) use ( $endpoint ) {
|
||||
return trailingslashit( $base_url ) . $endpoint;
|
||||
}, $base_urls );
|
||||
}
|
||||
|
||||
private function init_auth_mode() {
|
||||
$is_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
|
||||
$is_ajax = wp_doing_ajax();
|
||||
|
||||
if ( $is_rest || $is_ajax ) {
|
||||
// Set default to 'xhr' if rest or ajax request.
|
||||
$this->set_auth_mode( 'xhr' );
|
||||
}
|
||||
|
||||
$mode = Utils::get_super_global_value( $_REQUEST, 'mode' ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
|
||||
if ( $mode ) {
|
||||
$allowed_auth_modes = [
|
||||
'popup',
|
||||
];
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
$allowed_auth_modes[] = 'cli';
|
||||
}
|
||||
|
||||
if ( in_array( $mode, $allowed_auth_modes, true ) ) {
|
||||
$this->set_auth_mode( $mode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function set_auth_mode( $mode ) {
|
||||
$this->auth_mode = $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_notices', [ $this, 'admin_notice' ] );
|
||||
|
||||
$this->init_auth_mode();
|
||||
|
||||
$this->http = new Http();
|
||||
|
||||
/**
|
||||
* Allow extended apps to customize the __construct without call parent::__construct.
|
||||
*/
|
||||
$this->init();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base_User_App extends Base_App {
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function update_settings() {
|
||||
update_user_option( get_current_user_id(), $this->get_option_name(), $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init_data() {
|
||||
$this->data = get_user_option( $this->get_option_name() );
|
||||
|
||||
if ( ! $this->data ) {
|
||||
$this->data = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Common_App extends Base_User_App {
|
||||
const OPTION_CONNECT_COMMON_DATA_KEY = self::OPTION_NAME_PREFIX . 'common_data';
|
||||
|
||||
protected static $common_data = null;
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_option_name() {
|
||||
return static::OPTION_NAME_PREFIX . 'common_data';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init_data() {
|
||||
if ( is_null( self::$common_data ) ) {
|
||||
self::$common_data = get_user_option( static::get_option_name() );
|
||||
|
||||
if ( ! self::$common_data ) {
|
||||
self::$common_data = [];
|
||||
};
|
||||
}
|
||||
|
||||
$this->data = & self::$common_data;
|
||||
}
|
||||
|
||||
public function action_reset() {
|
||||
delete_user_option( get_current_user_id(), static::OPTION_CONNECT_COMMON_DATA_KEY );
|
||||
|
||||
parent::action_reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Connect extends Common_App {
|
||||
|
||||
public function get_title() {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
protected function get_slug() {
|
||||
return 'connect';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function render_admin_widget() {}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
use Elementor\Api;
|
||||
use Elementor\User;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Library extends Common_App {
|
||||
|
||||
public function get_title() {
|
||||
return esc_html__( 'Library', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_slug() {
|
||||
return 'library';
|
||||
}
|
||||
|
||||
public function get_template_content( $id ) {
|
||||
if ( ! $this->is_connected() ) {
|
||||
return new \WP_Error( '401', esc_html__( 'Connecting to the Library failed. Please try reloading the page and try again', 'elementor' ) );
|
||||
}
|
||||
|
||||
$body_args = [
|
||||
'id' => $id,
|
||||
|
||||
// Which API version is used.
|
||||
'api_version' => ELEMENTOR_VERSION,
|
||||
// Which language to return.
|
||||
'site_lang' => get_bloginfo( 'language' ),
|
||||
];
|
||||
|
||||
/**
|
||||
* API: Template body args.
|
||||
*
|
||||
* Filters the body arguments send with the GET request when fetching the content.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $body_args Body arguments.
|
||||
*/
|
||||
$body_args = apply_filters( 'elementor/api/get_templates/body_args', $body_args );
|
||||
|
||||
$template_content = $this->request( 'get_template_content', $body_args, true );
|
||||
|
||||
if ( is_wp_error( $template_content ) && 401 === $template_content->get_error_code() ) {
|
||||
// Normalize 401 message
|
||||
return new \WP_Error( 401, esc_html__( 'Connecting to the Library failed. Please try reloading the page and try again', 'elementor' ) );
|
||||
}
|
||||
|
||||
return $template_content;
|
||||
}
|
||||
|
||||
public function localize_settings( $settings ) {
|
||||
$is_connected = $this->is_connected();
|
||||
|
||||
/** @var ConnectModule $connect */
|
||||
$connect = Plugin::$instance->common->get_component( 'connect' );
|
||||
|
||||
return array_replace_recursive( $settings, [
|
||||
'library_connect' => [
|
||||
'is_connected' => $is_connected,
|
||||
'subscription_plans' => $connect->get_subscription_plans( 'template-library' ),
|
||||
// TODO: Remove `base_access_level`.
|
||||
'base_access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'base_access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
'current_access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'current_access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
public function library_connect_popup_seen() {
|
||||
User::set_introduction_viewed( [
|
||||
'introductionKey' => 'library_connect',
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Elementor\Core\Common\Modules\Ajax\Module $ajax_manager
|
||||
*/
|
||||
public function register_ajax_actions( $ajax_manager ) {
|
||||
$ajax_manager->register_ajax_action( 'library_connect_popup_seen', [ $this, 'library_connect_popup_seen' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* After Connect
|
||||
*
|
||||
* After Connecting to the library, re-fetch the library data to get it up to date.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected function after_connect() {
|
||||
Api::get_library_data( true );
|
||||
}
|
||||
|
||||
protected function get_app_info() {
|
||||
return [
|
||||
'user_common_data' => [
|
||||
'label' => 'User Common Data',
|
||||
'value' => get_user_option( $this->get_option_name(), get_current_user_id() ),
|
||||
],
|
||||
'connect_site_key' => [
|
||||
'label' => 'Site Key',
|
||||
'value' => get_option( self::OPTION_CONNECT_SITE_KEY ),
|
||||
],
|
||||
'remote_info_library' => [
|
||||
'label' => 'Remote Library Info',
|
||||
'value' => get_option( 'elementor_remote_info_library' ),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_popup_success_event_data() {
|
||||
return [
|
||||
'access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
];
|
||||
}
|
||||
|
||||
protected function init() {
|
||||
add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
|
||||
add_filter( 'elementor/common/localize_settings', [ $this, 'localize_settings' ] );
|
||||
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Interfaces\Admin_Menu_Item_With_Page;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Base_App;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Settings;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Connect_Menu_Item implements Admin_Menu_Item_With_Page {
|
||||
|
||||
public function is_visible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_parent_slug() {
|
||||
return Settings::PAGE_ID;
|
||||
}
|
||||
|
||||
public function get_label() {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_page_title() {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_capability() {
|
||||
return 'edit_posts';
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$apps = Plugin::$instance->common->get_component( 'connect' )->get_apps();
|
||||
?>
|
||||
<style>
|
||||
.elementor-connect-app-wrapper{
|
||||
margin-bottom: 50px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<div class="wrap">
|
||||
<?php
|
||||
|
||||
/** @var Base_App $app */
|
||||
foreach ( $apps as $app ) {
|
||||
echo '<div class="elementor-connect-app-wrapper">';
|
||||
$app->render_admin_widget();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
?>
|
||||
</div><!-- /.wrap -->
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Base_App;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Common_App;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Connect;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
use WP_User_Query;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Module extends BaseModule {
|
||||
const ACCESS_LEVEL_CORE = 0;
|
||||
const ACCESS_LEVEL_PRO = 1;
|
||||
const ACCESS_LEVEL_EXPERT = 20;
|
||||
|
||||
const ACCESS_TIER_FREE = 'free';
|
||||
const ACCESS_TIER_ESSENTIAL = 'essential';
|
||||
const ACCESS_TIER_ESSENTIAL_OCT_2023 = 'essential-oct2023';
|
||||
const ACCESS_TIER_ADVANCED = 'advanced';
|
||||
const ACCESS_TIER_EXPERT = 'expert';
|
||||
const ACCESS_TIER_AGENCY = 'agency';
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'connect';
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $registered_apps = [];
|
||||
|
||||
/**
|
||||
* Apps Instances.
|
||||
*
|
||||
* Holds the list of all the apps instances.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @var Base_App[]
|
||||
*/
|
||||
protected $apps = [];
|
||||
|
||||
/**
|
||||
* Registered apps categories.
|
||||
*
|
||||
* Holds the list of all the registered apps categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $categories = [];
|
||||
|
||||
protected $admin_page;
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->registered_apps = [
|
||||
'connect' => Connect::get_class_name(),
|
||||
'library' => Library::get_class_name(),
|
||||
];
|
||||
|
||||
// When using REST API the parent module is construct after the action 'elementor/init'
|
||||
// so this part of code make sure to register the module "apps".
|
||||
if ( did_action( 'elementor/init' ) ) {
|
||||
$this->init();
|
||||
} else {
|
||||
// Note: The priority 11 is for allowing plugins to add their register callback on elementor init.
|
||||
add_action( 'elementor/init', [ $this, 'init' ], 11 );
|
||||
}
|
||||
|
||||
add_filter( 'elementor/tracker/send_tracking_data_params', function ( $params ) {
|
||||
return $this->add_tracking_data( $params );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register default apps.
|
||||
*
|
||||
* Registers the default apps.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function init() {
|
||||
if ( is_admin() ) {
|
||||
$this->admin_page = new Admin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Elementor apps.
|
||||
*
|
||||
* Fires after Elementor registers the default apps.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @param self $this The apps manager instance.
|
||||
*/
|
||||
do_action( 'elementor/connect/apps/register', $this );
|
||||
|
||||
foreach ( $this->registered_apps as $slug => $class ) {
|
||||
$this->apps[ $slug ] = new $class();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.0
|
||||
*/
|
||||
public function localize_settings() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' );
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register app.
|
||||
*
|
||||
* Registers an app.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $slug App slug.
|
||||
* @param string $class App full class name.
|
||||
*
|
||||
* @return self The updated apps manager instance.
|
||||
*/
|
||||
public function register_app( $slug, $class ) {
|
||||
$this->registered_apps[ $slug ] = $class;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get app instance.
|
||||
*
|
||||
* Retrieve the app instance.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param $slug
|
||||
*
|
||||
* @return Base_App|null
|
||||
*/
|
||||
public function get_app( $slug ) {
|
||||
if ( isset( $this->apps[ $slug ] ) ) {
|
||||
return $this->apps[ $slug ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
* @return Base_App[]
|
||||
*/
|
||||
public function get_apps() {
|
||||
return $this->apps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_category( $slug, $args ) {
|
||||
$this->categories[ $slug ] = $args;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_categories() {
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $context Where this subscription plan should be shown.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscription_plans( $context = '' ) {
|
||||
$base_url = Utils::has_pro() ? 'https://my.elementor.com/upgrade-subscription' : 'https://elementor.com/pro';
|
||||
$promotion_url = $base_url . '/?utm_source=' . $context . '&utm_medium=wp-dash&utm_campaign=gopro';
|
||||
|
||||
return [
|
||||
static::ACCESS_TIER_FREE => [
|
||||
'label' => null,
|
||||
'promotion_url' => null,
|
||||
'color' => null,
|
||||
],
|
||||
static::ACCESS_TIER_ESSENTIAL => [
|
||||
'label' => 'Pro',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_ESSENTIAL_OCT_2023 => [
|
||||
'label' => 'Advanced', // Should be the same label as "Advanced".
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_ADVANCED => [
|
||||
'label' => 'Advanced',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_EXPERT => [
|
||||
'label' => 'Expert',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_AGENCY => [
|
||||
'label' => 'Agency',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function add_tracking_data( $params ) {
|
||||
$users = [];
|
||||
|
||||
$users_query = new WP_User_Query( [
|
||||
'count_total' => false, // Disable SQL_CALC_FOUND_ROWS.
|
||||
'meta_query' => [
|
||||
'key' => Common_App::OPTION_CONNECT_COMMON_DATA_KEY,
|
||||
'compare' => 'EXISTS',
|
||||
],
|
||||
] );
|
||||
|
||||
foreach ( $users_query->get_results() as $user ) {
|
||||
$connect_common_data = get_user_option( Common_App::OPTION_CONNECT_COMMON_DATA_KEY, $user->ID );
|
||||
|
||||
if ( $connect_common_data ) {
|
||||
$users [] = [
|
||||
'id' => $user->ID,
|
||||
'email' => $connect_common_data['user']->email,
|
||||
'roles' => implode( ', ', $user->roles ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$params['usages'][ $this->get_name() ] = [
|
||||
'site_key' => get_option( Base_App::OPTION_CONNECT_SITE_KEY ),
|
||||
'count' => count( $users ),
|
||||
'users' => $users,
|
||||
];
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker\Data;
|
||||
|
||||
use Elementor\Core\Common\Modules\EventTracker\DB as Events_DB_Manager;
|
||||
use Elementor\Plugin;
|
||||
use WP_REST_Server;
|
||||
use Elementor\Data\V2\Base\Controller as Controller_Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Controller extends Controller_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'send-event';
|
||||
}
|
||||
|
||||
public function register_endpoints() {
|
||||
$this->index_endpoint->register_items_route( \WP_REST_Server::CREATABLE, [
|
||||
'event_data' => [
|
||||
'description' => 'All the recorded event data in JSON format',
|
||||
'type' => 'object',
|
||||
'required' => true,
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Permissions Callback
|
||||
*
|
||||
* This endpoint should only accept POST requests, and currently we only track site administrator actions.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param \WP_REST_Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function get_permission_callback( $request ) {
|
||||
if ( WP_REST_Server::CREATABLE !== $request->get_method() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Items
|
||||
*
|
||||
* Receives a request for adding an event data entry into the database. If the request contains event data, this
|
||||
* method initiates creation of a database entry with the event data in the Events DB table.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param \WP_REST_Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function create_items( $request ) {
|
||||
$request_body = $request->get_json_params();
|
||||
|
||||
if ( empty( $request_body['event_data'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var Events_DB_Manager $event_tracker_db_manager */
|
||||
$event_tracker_db_manager = Plugin::$instance->common
|
||||
->get_component( 'event-tracker' )
|
||||
->get_component( 'events-db' );
|
||||
|
||||
$event_tracker_db_manager->create_entry( $request_body['event_data'] );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Common_App;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class DB extends Base_Object {
|
||||
|
||||
/**
|
||||
* @var \wpdb
|
||||
*/
|
||||
private $wpdb;
|
||||
|
||||
const TABLE_NAME = 'e_events';
|
||||
const DB_VERSION_OPTION_KEY = 'elementor_events_db_version';
|
||||
const CURRENT_DB_VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* Get Table Name
|
||||
*
|
||||
* Returns the Events database table's name with the `wpdb` prefix.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_table_name() {
|
||||
return $this->wpdb->prefix . self::TABLE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare Database for Entry
|
||||
*
|
||||
* The events database should have a limit of up to 1000 event entries stored daily.
|
||||
* Before adding a new entry to the database, we make sure that the limit of 1000 events is not reached.
|
||||
* If there are 1000 or more entries in the DB, we delete the earliest-inserted entry before inserting a new one.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public function prepare_db_for_entry() {
|
||||
$events = $this->get_event_ids_from_db();
|
||||
|
||||
if ( 1000 <= count( $events ) ) {
|
||||
$event_ids = [];
|
||||
|
||||
foreach ( $events as $event ) {
|
||||
$event_ids[] = $event->id;
|
||||
}
|
||||
|
||||
// Sort the array by entry ID
|
||||
array_multisort( $event_ids, SORT_ASC, $events );
|
||||
|
||||
// Delete the smallest ID (which is the earliest DB entry)
|
||||
$this->wpdb->delete( $this->get_table_name(), [ 'ID' => $events[0]->id ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Entry
|
||||
*
|
||||
* Adds an event entry to the database.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public function create_entry( $event_data ) {
|
||||
$this->prepare_db_for_entry();
|
||||
|
||||
$connect = Plugin::$instance->common->get_component( 'connect' );
|
||||
/** @var Library $library */
|
||||
$library = $connect->get_apps()['library'];
|
||||
|
||||
if ( ! isset( $event_data['details'] ) ) {
|
||||
$event_data['details'] = [];
|
||||
}
|
||||
|
||||
if ( $library->is_connected() ) {
|
||||
$user_connect_data = get_user_option( Common_App::OPTION_CONNECT_COMMON_DATA_KEY );
|
||||
|
||||
// Add the user's client ID to the event.
|
||||
$event_data['details']['client_id'] = $user_connect_data['client_id'];
|
||||
}
|
||||
|
||||
$event_data['details'] = json_encode( $event_data['details'] );
|
||||
|
||||
$entry = [
|
||||
'event_data' => wp_json_encode( $event_data ),
|
||||
'created_at' => $event_data['ts'],
|
||||
];
|
||||
|
||||
$this->wpdb->insert( $this->get_table_name(), $entry );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Event IDs From DB
|
||||
*
|
||||
* Fetches the IDs of all events saved in the database.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array|object|null
|
||||
*/
|
||||
public function get_event_ids_from_db() {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
return $this->wpdb->get_results( "SELECT id FROM {$this->get_table_name()}" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Table
|
||||
*
|
||||
* Empties the contents of the Events DB table.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public static function reset_table() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . self::TABLE_NAME;
|
||||
|
||||
// Delete all content of the table.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$wpdb->query( "TRUNCATE TABLE {$table_name}" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Table
|
||||
*
|
||||
* Creates the `wp_e_events` database table.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $query to that looks for the Events table in the DB. Used for checking if table was created.
|
||||
*/
|
||||
private function create_table( $query ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
|
||||
$table_name = $this->get_table_name();
|
||||
$charset_collate = $this->wpdb->get_charset_collate();
|
||||
|
||||
$e_events_table = "CREATE TABLE `{$table_name}` (
|
||||
id bigint(20) unsigned auto_increment primary key,
|
||||
event_data text null,
|
||||
created_at datetime not null
|
||||
) {$charset_collate};";
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->wpdb->query( $e_events_table );
|
||||
|
||||
// Check if table was created successfully.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
if ( $this->wpdb->get_var( $query ) === $table_name ) {
|
||||
update_option( self::DB_VERSION_OPTION_KEY, self::CURRENT_DB_VERSION, false );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Indexes
|
||||
*
|
||||
* Adds an index to the events table for the creation date column.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
private function add_indexes() {
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->wpdb->query( 'ALTER TABLE ' . $this->get_table_name() . '
|
||||
ADD INDEX `created_at_index` (`created_at`)
|
||||
' );
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
global $wpdb;
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
// Check if table exists. If not, create it.
|
||||
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $this->get_table_name() ) );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
if ( $wpdb->get_var( $query ) !== $this->get_table_name() ) {
|
||||
$this->create_table( $query );
|
||||
$this->add_indexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\EventTracker\Data\Controller;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tracker;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Event Tracker Module Class
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
public function get_name() {
|
||||
return 'event-tracker';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* @since 3.6.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
return [
|
||||
'isUserDataShared' => Tracker::is_allow_track(),
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
// Initialize Events Database Table
|
||||
$this->add_component( 'events-db', new DB() );
|
||||
|
||||
// Handle User Data Deletion/Export requests.
|
||||
new Personal_Data();
|
||||
|
||||
Plugin::$instance->data_manager_v2->register_controller( new Controller() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Core\Common\Modules\EventTracker\DB as Events_DB_Manager;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Personal_Data extends Base_Object {
|
||||
|
||||
const WP_KEY = 'elementor-event-tracker';
|
||||
|
||||
/**
|
||||
* Get Title
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_title() {
|
||||
return esc_html__( 'Elementor Event Tracker', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase all the submissions related to specific email.
|
||||
*
|
||||
* Since event data is saved globally per site and not per user, we remove all saved events from the DB upon a
|
||||
* user's data deletion request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function erase_data() {
|
||||
// Get number of events saved in the DB.
|
||||
/** @var Events_DB_Manager $event_tracker_db_manager */
|
||||
$event_tracker_db_manager = Plugin::$instance->common
|
||||
->get_component( 'event-tracker' )
|
||||
->get_component( 'events-db' );
|
||||
|
||||
$events = $event_tracker_db_manager->get_event_ids_from_db();
|
||||
$events_count = count( $events );
|
||||
|
||||
DB::reset_table();
|
||||
|
||||
// Validate table deleted
|
||||
$updated_events = $event_tracker_db_manager->get_event_ids_from_db();
|
||||
$updated_events_count = count( $updated_events );
|
||||
|
||||
return [
|
||||
'items_removed' => $events_count - $updated_events_count,
|
||||
'items_retained' => 0,
|
||||
'messages' => [],
|
||||
'done' => 0 === $updated_events_count,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add eraser to the list of erasers.
|
||||
*
|
||||
* @param $erasers
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
private function add_eraser( $erasers ) {
|
||||
return $erasers + [
|
||||
self::WP_KEY => [
|
||||
'eraser_friendly_name' => $this->get_title(),
|
||||
'callback' => function () {
|
||||
return $this->erase_data();
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Personal_Data constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'wp_privacy_personal_data_erasers', function ( $exporters ) {
|
||||
return $this->add_eraser( $exporters );
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Category
|
||||
*
|
||||
* Base class for Elementor Finder categories.
|
||||
*/
|
||||
abstract class Base_Category extends Base_Object {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @abstract
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_title();
|
||||
|
||||
/**
|
||||
* Get a unique category ID.
|
||||
*
|
||||
* TODO: Make abstract.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @deprecated 3.5.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
|
||||
get_class( $this ) . '::' . __FUNCTION__,
|
||||
'3.5.0',
|
||||
'This method will be replaced with an abstract method.'
|
||||
);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @abstract
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function get_category_items( array $options = [] );
|
||||
|
||||
/**
|
||||
* Is dynamic.
|
||||
*
|
||||
* Determine if the category is dynamic.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dynamic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
$settings = [
|
||||
'title' => $this->get_title(),
|
||||
'dynamic' => $this->is_dynamic(),
|
||||
];
|
||||
|
||||
if ( ! $settings['dynamic'] ) {
|
||||
$settings['items'] = $this->get_category_items();
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Categories_Manager {
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*
|
||||
* @var Base_Category[]
|
||||
*/
|
||||
private $categories;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $categories_list = [
|
||||
'edit',
|
||||
'general',
|
||||
'create',
|
||||
'site',
|
||||
'settings',
|
||||
'tools',
|
||||
];
|
||||
|
||||
/**
|
||||
* Add category.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @deprecated 3.5.0 Use `register()` method instead.
|
||||
* @access public
|
||||
*
|
||||
* @param string $category_name
|
||||
* @param Base_Category $category
|
||||
*
|
||||
* @deprecated 3.5.0 Use `register()` method instead.
|
||||
*/
|
||||
public function add_category( $category_name, Base_Category $category ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
|
||||
__METHOD__,
|
||||
'3.5.0',
|
||||
'register()'
|
||||
);
|
||||
|
||||
$this->register( $category, $category_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register finder category.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @access public
|
||||
*
|
||||
* @param Base_Category $finder_category_instance An Instance of a category.
|
||||
* @param string $finder_category_name A Category name. Deprecated parameter.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register( Base_Category $finder_category_instance, $finder_category_name = null ) {
|
||||
// TODO: For BC. Remove in the future.
|
||||
if ( $finder_category_name ) {
|
||||
Plugin::instance()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_argument(
|
||||
'$finder_category_name', '3.5.0'
|
||||
);
|
||||
} else {
|
||||
$finder_category_name = $finder_category_instance->get_id();
|
||||
}
|
||||
|
||||
$this->categories[ $finder_category_name ] = $finder_category_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a finder category.
|
||||
*
|
||||
* @param string $finder_category_name - Category to unregister.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.6.0
|
||||
* @access public
|
||||
*/
|
||||
public function unregister( $finder_category_name ) {
|
||||
unset( $this->categories[ $finder_category_name ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get categories.
|
||||
*
|
||||
* Retrieve the registered categories, or a specific category if the category name
|
||||
* is provided as a parameter.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $category Category name.
|
||||
*
|
||||
* @return Base_Category|Base_Category[]|null
|
||||
*/
|
||||
public function get_categories( $category = '' ) {
|
||||
if ( ! $this->categories ) {
|
||||
$this->init_categories();
|
||||
}
|
||||
|
||||
if ( $category ) {
|
||||
if ( isset( $this->categories[ $category ] ) ) {
|
||||
return $this->categories[ $category ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init categories.
|
||||
*
|
||||
* Used to initialize the native finder categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*/
|
||||
private function init_categories() {
|
||||
foreach ( $this->categories_list as $category_name ) {
|
||||
$class_name = __NAMESPACE__ . '\Categories\\' . $category_name;
|
||||
|
||||
$this->register( new $class_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor Finder categories init.
|
||||
*
|
||||
* Fires after Elementor Finder initialize it's native categories.
|
||||
*
|
||||
* This hook should be used to add your own Finder categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @deprecated 3.5.0 Use `elementor/finder/register` hook instead.
|
||||
*
|
||||
* @param Categories_Manager $this.
|
||||
*/
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action(
|
||||
'elementor/finder/categories/init',
|
||||
[ $this ],
|
||||
'3.5.0',
|
||||
'elementor/finder/register'
|
||||
);
|
||||
|
||||
/**
|
||||
* Elementor Finder categories registration.
|
||||
*
|
||||
* Fires after Elementor Finder initialize it's native categories.
|
||||
*
|
||||
* This hook should be used to register your own Finder categories.
|
||||
*
|
||||
* @since 3.5.0
|
||||
*
|
||||
* @param Categories_Manager $this Finder Categories manager.
|
||||
*/
|
||||
do_action( 'elementor/finder/register', $this );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Category
|
||||
*
|
||||
* Provides items related to creation of new posts/pages/templates etc.
|
||||
*/
|
||||
class Create extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Create', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'create';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
$result = [];
|
||||
|
||||
$registered_document_types = Plugin::$instance->documents->get_document_types();
|
||||
|
||||
// TODO: Remove - Support 'post' backwards compatibility - See `Documents_Manager::register_default_types()`.
|
||||
unset( $registered_document_types['post'] );
|
||||
|
||||
$elementor_supported_post_types = array_flip( get_post_types_by_support( 'elementor' ) );
|
||||
|
||||
foreach ( $registered_document_types as $document_name => $document_class ) {
|
||||
$document_properties = $document_class::get_properties();
|
||||
|
||||
if ( empty( $document_properties['show_in_finder'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $document_properties['cpt'] ) ) {
|
||||
foreach ( $document_properties['cpt'] as $cpt ) {
|
||||
unset( $elementor_supported_post_types[ $cpt ] );
|
||||
}
|
||||
}
|
||||
|
||||
$result[ $document_name ] = $this->create_item_url_by_document_class( $document_class );
|
||||
}
|
||||
|
||||
foreach ( $elementor_supported_post_types as $post_type => $val ) {
|
||||
$result[ $post_type ] = $this->create_item_url_by_post_type( $post_type );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function create_item_url_by_post_type( $post_type ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
// If there is an old post type from inactive plugins.
|
||||
if ( ! $post_type_object ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->get_create_new_template(
|
||||
sprintf( __( 'Add New %s', 'elementor' ), $post_type_object->labels->singular_name ),
|
||||
Plugin::$instance->documents->get_create_new_post_url( $post_type )
|
||||
);
|
||||
}
|
||||
|
||||
private function create_item_url_by_document_class( $document_class ) {
|
||||
$result = $this->get_create_new_template(
|
||||
$document_class::get_add_new_title(),
|
||||
$document_class::get_create_url()
|
||||
);
|
||||
|
||||
$lock_behavior = $document_class::get_lock_behavior_v2();
|
||||
$is_locked = ! empty( $lock_behavior ) && $lock_behavior->is_locked();
|
||||
|
||||
if ( $is_locked ) {
|
||||
$result['lock'] = $lock_behavior->get_config();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function get_create_new_template( $add_new_title, $url ) {
|
||||
return [
|
||||
'title' => $add_new_title,
|
||||
'icon' => 'plus-circle-o',
|
||||
'url' => $url,
|
||||
'keywords' => [ $add_new_title, 'post', 'page', 'template', 'new', 'create' ],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Base\Document;
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Category
|
||||
*
|
||||
* Provides items related to editing of posts/pages/templates etc.
|
||||
*/
|
||||
class Edit extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Edit', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'edit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is dynamic.
|
||||
*
|
||||
* Determine if the category is dynamic.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dynamic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
$post_types = get_post_types( [
|
||||
'exclude_from_search' => false,
|
||||
] );
|
||||
|
||||
$post_types[] = Source_Local::CPT;
|
||||
|
||||
$document_types = Plugin::$instance->documents->get_document_types( [
|
||||
'is_editable' => true,
|
||||
'show_in_finder' => true,
|
||||
] );
|
||||
|
||||
$recently_edited_query_args = [
|
||||
'no_found_rows' => true,
|
||||
'post_type' => $post_types,
|
||||
'post_status' => [ 'publish', 'draft', 'private', 'pending', 'future' ],
|
||||
'posts_per_page' => '10',
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => '_elementor_edit_mode',
|
||||
'value' => 'builder',
|
||||
],
|
||||
[
|
||||
'relation' => 'or',
|
||||
[
|
||||
'key' => Document::TYPE_META_KEY,
|
||||
'compare' => 'NOT EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => Document::TYPE_META_KEY,
|
||||
'value' => array_keys( $document_types ),
|
||||
],
|
||||
],
|
||||
],
|
||||
'orderby' => 'modified',
|
||||
's' => $options['filter'],
|
||||
];
|
||||
|
||||
$recently_edited_query = new \WP_Query( $recently_edited_query_args );
|
||||
|
||||
$items = [];
|
||||
|
||||
/** @var \WP_Post $post */
|
||||
foreach ( $recently_edited_query->posts as $post ) {
|
||||
$document = Plugin::$instance->documents->get( $post->ID );
|
||||
|
||||
if ( ! $document ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$is_template = Source_Local::CPT === $post->post_type;
|
||||
|
||||
$description = $document->get_title();
|
||||
|
||||
$icon = 'document-file';
|
||||
|
||||
if ( $is_template ) {
|
||||
$description = esc_html__( 'Template', 'elementor' ) . ' / ' . $description;
|
||||
|
||||
$icon = 'post-title';
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'icon' => $icon,
|
||||
'title' => esc_html( $post->post_title ),
|
||||
'description' => $description,
|
||||
'url' => $document->get_edit_url(),
|
||||
'actions' => [
|
||||
[
|
||||
'name' => 'view',
|
||||
'url' => $document->get_permalink(),
|
||||
'icon' => 'preview-medium',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Core\RoleManager\Role_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* General Category
|
||||
*
|
||||
* Provides general items related to Elementor Admin.
|
||||
*/
|
||||
class General extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'General', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'general';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
return [
|
||||
'saved-templates' => [
|
||||
'title' => esc_html__( 'Saved Templates', 'elementor' ),
|
||||
'icon' => 'library-save',
|
||||
'url' => Source_Local::get_admin_url(),
|
||||
'keywords' => [ 'template', 'section', 'page', 'library' ],
|
||||
],
|
||||
'system-info' => [
|
||||
'title' => esc_html__( 'System Info', 'elementor' ),
|
||||
'icon' => 'info-circle-o',
|
||||
'url' => admin_url( 'admin.php?page=elementor-system-info' ),
|
||||
'keywords' => [ 'system', 'info', 'environment', 'elementor' ],
|
||||
],
|
||||
'role-manager' => [
|
||||
'title' => esc_html__( 'Role Manager', 'elementor' ),
|
||||
'icon' => 'person',
|
||||
'url' => Role_Manager::get_url(),
|
||||
'keywords' => [ 'role', 'manager', 'user', 'elementor' ],
|
||||
],
|
||||
'knowledge-base' => [
|
||||
'title' => esc_html__( 'Knowledge Base', 'elementor' ),
|
||||
'url' => admin_url( 'admin.php?page=go_knowledge_base_site' ),
|
||||
'keywords' => [ 'help', 'knowledge', 'docs', 'elementor' ],
|
||||
],
|
||||
'theme-builder' => [
|
||||
'title' => esc_html__( 'Theme Builder', 'elementor' ),
|
||||
'icon' => 'library-save',
|
||||
'url' => Plugin::$instance->app->get_settings( 'menu_url' ),
|
||||
'keywords' => [ 'template', 'header', 'footer', 'single', 'archive', 'search', '404', 'library' ],
|
||||
],
|
||||
'kit-library' => [
|
||||
'title' => esc_html__( 'Kit Library', 'elementor' ),
|
||||
'icon' => 'kit-parts',
|
||||
'url' => Plugin::$instance->app->get_base_url() . '#/kit-library',
|
||||
'keywords' => [ 'kit library', 'kit', 'library', 'site parts', 'parts', 'assets', 'templates' ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Modules\ElementManager\Module as ElementManagerModule;
|
||||
use Elementor\Settings as ElementorSettings;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings Category
|
||||
*
|
||||
* Provides items related to Elementor's settings.
|
||||
*/
|
||||
class Settings extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Settings', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
return [
|
||||
'general-settings' => [
|
||||
'title' => esc_html__( 'General Settings', 'elementor' ),
|
||||
'url' => ElementorSettings::get_settings_tab_url( 'general' ),
|
||||
'keywords' => [ 'general', 'settings', 'elementor' ],
|
||||
],
|
||||
'advanced' => [
|
||||
'title' => esc_html__( 'Advanced', 'elementor' ),
|
||||
'url' => ElementorSettings::get_settings_tab_url( 'advanced' ),
|
||||
'keywords' => [ 'advanced', 'settings', 'elementor' ],
|
||||
],
|
||||
'experiments' => [
|
||||
'title' => esc_html__( 'Experiments', 'elementor' ),
|
||||
'url' => ElementorSettings::get_settings_tab_url( 'experiments' ),
|
||||
'keywords' => [ 'settings', 'elementor', 'experiments' ],
|
||||
],
|
||||
'features' => [
|
||||
'title' => esc_html__( 'Features', 'elementor' ),
|
||||
'url' => ElementorSettings::get_settings_tab_url( 'experiments' ),
|
||||
'keywords' => [ 'settings', 'elementor', 'features' ],
|
||||
],
|
||||
'element-manager' => [
|
||||
'title' => esc_html__( 'Element Manager', 'elementor' ),
|
||||
'url' => admin_url( 'admin.php?page=' . ElementManagerModule::PAGE_ID ),
|
||||
'keywords' => [ 'settings', 'elements', 'widgets', 'manager' ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Site Category
|
||||
*
|
||||
* Provides general site items.
|
||||
*/
|
||||
class Site extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Site', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'site';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
return [
|
||||
'homepage' => [
|
||||
'title' => esc_html__( 'Homepage', 'elementor' ),
|
||||
'url' => home_url(),
|
||||
'icon' => 'home-heart',
|
||||
'keywords' => [ 'home', 'page' ],
|
||||
],
|
||||
'wordpress-dashboard' => [
|
||||
'title' => esc_html__( 'Dashboard', 'elementor' ),
|
||||
'icon' => 'dashboard',
|
||||
'url' => admin_url(),
|
||||
'keywords' => [ 'dashboard', 'wordpress' ],
|
||||
],
|
||||
'wordpress-menus' => [
|
||||
'title' => esc_html__( 'Menus', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'nav-menus.php' ),
|
||||
'keywords' => [ 'menu', 'wordpress' ],
|
||||
],
|
||||
'wordpress-themes' => [
|
||||
'title' => esc_html__( 'Themes', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'themes.php' ),
|
||||
'keywords' => [ 'themes', 'wordpress' ],
|
||||
],
|
||||
'wordpress-customizer' => [
|
||||
'title' => esc_html__( 'Customizer', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'customize.php' ),
|
||||
'keywords' => [ 'customizer', 'wordpress' ],
|
||||
],
|
||||
'wordpress-plugins' => [
|
||||
'title' => esc_html__( 'Plugins', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'plugins.php' ),
|
||||
'keywords' => [ 'plugins', 'wordpress' ],
|
||||
],
|
||||
'wordpress-users' => [
|
||||
'title' => esc_html__( 'Users', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'users.php' ),
|
||||
'keywords' => [ 'users', 'profile', 'wordpress' ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Tools as ElementorTools;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools Category
|
||||
*
|
||||
* Provides items related to Elementor's tools.
|
||||
*/
|
||||
class Tools extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Tools', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'tools';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
$tools_url = ElementorTools::get_url();
|
||||
|
||||
$items = [
|
||||
'tools' => [
|
||||
'title' => esc_html__( 'Tools', 'elementor' ),
|
||||
'icon' => 'tools',
|
||||
'url' => $tools_url,
|
||||
'keywords' => [ 'tools', 'regenerate css', 'safe mode', 'debug bar', 'sync library', 'elementor' ],
|
||||
],
|
||||
'replace-url' => [
|
||||
'title' => esc_html__( 'Replace URL', 'elementor' ),
|
||||
'icon' => 'tools',
|
||||
'url' => $tools_url . '#tab-replace_url',
|
||||
'keywords' => [ 'tools', 'replace url', 'domain', 'elementor' ],
|
||||
],
|
||||
'maintenance-mode' => [
|
||||
'title' => esc_html__( 'Maintenance Mode', 'elementor' ),
|
||||
'icon' => 'tools',
|
||||
'url' => $tools_url . '#tab-maintenance_mode',
|
||||
'keywords' => [ 'tools', 'maintenance', 'coming soon', 'elementor' ],
|
||||
],
|
||||
'import-export' => [
|
||||
'title' => esc_html__( 'Import Export', 'elementor' ),
|
||||
'icon' => 'import-export',
|
||||
'url' => $tools_url . '#tab-import-export-kit',
|
||||
'keywords' => [ 'tools', 'import export', 'import', 'export', 'kit' ],
|
||||
],
|
||||
];
|
||||
|
||||
if ( ElementorTools::can_user_rollback_versions() ) {
|
||||
$items['version-control'] = [
|
||||
'title' => esc_html__( 'Version Control', 'elementor' ),
|
||||
'icon' => 'time-line',
|
||||
'url' => $tools_url . '#tab-versions',
|
||||
'keywords' => [ 'tools', 'version', 'control', 'rollback', 'beta', 'elementor' ],
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Finder Module
|
||||
*
|
||||
* Responsible for initializing Elementor Finder functionality
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
/**
|
||||
* Categories manager.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @var Categories_Manager
|
||||
*/
|
||||
private $categories_manager;
|
||||
|
||||
/**
|
||||
* Module constructor.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->categories_manager = new Categories_Manager();
|
||||
|
||||
$this->add_template();
|
||||
|
||||
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'finder';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add template.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function add_template() {
|
||||
Plugin::$instance->common->add_template( __DIR__ . '/template.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ajax actions.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param Ajax $ajax
|
||||
*/
|
||||
public function register_ajax_actions( Ajax $ajax ) {
|
||||
$ajax->register_ajax_action( 'finder_get_category_items', [ $this, 'ajax_get_category_items' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function ajax_get_category_items( array $data ) {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
throw new \Exception( 'Access denied.' );
|
||||
}
|
||||
|
||||
$category = $this->categories_manager->get_categories( $data['category'] );
|
||||
|
||||
return $category->get_category_items( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
$categories = $this->categories_manager->get_categories();
|
||||
|
||||
$categories_data = [];
|
||||
|
||||
foreach ( $categories as $category_name => $category ) {
|
||||
$categories_data[ $category_name ] = array_merge( $category->get_settings(), [ 'name' => $category_name ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Finder categories.
|
||||
*
|
||||
* Filters the list of finder categories. This hook is used to manage Finder
|
||||
* categories - to add new categories, remove and edit existing categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @param array $categories_data A list of finder categories.
|
||||
*/
|
||||
$categories_data = apply_filters( 'elementor/finder/categories', $categories_data );
|
||||
|
||||
return [
|
||||
'data' => $categories_data,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Modules\Finder;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
?>
|
||||
<script type="text/template" id="tmpl-elementor-finder">
|
||||
<div id="elementor-finder__search">
|
||||
<i class="eicon-search" aria-hidden="true"></i>
|
||||
<input id="elementor-finder__search__input" placeholder="<?php echo esc_attr__( 'Type to find anything in Elementor', 'elementor' ); ?>" autocomplete="off">
|
||||
</div>
|
||||
<div id="elementor-finder__content"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="tmpl-elementor-finder-results-container">
|
||||
<div id="elementor-finder__no-results"><?php echo esc_html__( 'No Results Found', 'elementor' ); ?></div>
|
||||
<div id="elementor-finder__results"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="tmpl-elementor-finder__results__category">
|
||||
<div class="elementor-finder__results__category__title">{{{ title }}}</div>
|
||||
<div class="elementor-finder__results__category__items"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="tmpl-elementor-finder__results__item">
|
||||
<a href="{{ url }}" class="elementor-finder__results__item__link">
|
||||
<div class="elementor-finder__results__item__icon">
|
||||
<i class="eicon-{{{ icon }}}" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="elementor-finder__results__item__title">{{{ title }}}</div>
|
||||
<# if ( description ) { #>
|
||||
<div class="elementor-finder__results__item__description">- {{{ description }}}</div>
|
||||
<# } #>
|
||||
|
||||
<# if ( lock ) { #>
|
||||
<div class="elementor-finder__results__item__badge"><i class="{{{ lock.badge.icon }}}"></i>{{ lock.badge.text }}</div>
|
||||
<# } #>
|
||||
</a>
|
||||
<# if ( actions.length ) { #>
|
||||
<div class="elementor-finder__results__item__actions">
|
||||
<# jQuery.each( actions, function() { #>
|
||||
<a class="elementor-finder__results__item__action elementor-finder__results__item__action--{{ this.name }}" href="{{ this.url }}" target="_blank">
|
||||
<i class="eicon-{{{ this.icon }}}"></i>
|
||||
</a>
|
||||
<# } ); #>
|
||||
</div>
|
||||
<# } #>
|
||||
</script>
|
||||
48
wp-content/plugins/elementor/core/debug/classes/htaccess.php
Normal file
48
wp-content/plugins/elementor/core/debug/classes/htaccess.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Debug\Classes;
|
||||
|
||||
use Elementor\Modules\SafeMode\Module as Safe_Mode;
|
||||
use Elementor\Utils;
|
||||
|
||||
class Htaccess extends Inspection_Base {
|
||||
|
||||
private $message = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->message = esc_html__( 'Your site\'s .htaccess file appears to be missing.', 'elementor' );
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$safe_mode_enabled = get_option( Safe_Mode::OPTION_ENABLED, '' );
|
||||
if ( empty( $safe_mode_enabled ) || is_multisite() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$permalink_structure = get_option( 'permalink_structure' );
|
||||
if ( empty( $permalink_structure ) || empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$server = strtoupper( Utils::get_super_global_value( $_SERVER, 'SERVER_SOFTWARE' ) );
|
||||
|
||||
if ( strstr( $server, 'APACHE' ) ) {
|
||||
$htaccess_file = get_home_path() . '.htaccess';
|
||||
/* translators: %s: Path to .htaccess file. */
|
||||
$this->message .= ' ' . sprintf( esc_html__( 'File Path: %s', 'elementor' ), $htaccess_file ) . ' ';
|
||||
return file_exists( $htaccess_file );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return 'apache-htaccess';
|
||||
}
|
||||
|
||||
public function get_message() {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function get_help_doc_url() {
|
||||
return 'https://go.elementor.com/preview-not-loaded/#htaccess';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Debug\Classes;
|
||||
|
||||
abstract class Inspection_Base {
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function run();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_message();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_header_message() {
|
||||
return esc_html__( 'The preview could not be loaded', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_help_doc_url();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Debug\Classes;
|
||||
|
||||
use Elementor\Modules\SafeMode\Module as Safe_Mode;
|
||||
|
||||
class Theme_Missing extends Inspection_Base {
|
||||
|
||||
public function run() {
|
||||
$safe_mode_enabled = get_option( Safe_Mode::OPTION_ENABLED, '' );
|
||||
if ( ! empty( $safe_mode_enabled ) ) {
|
||||
return true;
|
||||
}
|
||||
$theme = wp_get_theme();
|
||||
return $theme->exists();
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return 'theme-missing';
|
||||
}
|
||||
|
||||
public function get_message() {
|
||||
return esc_html__( 'Some of your theme files are missing.', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_help_doc_url() {
|
||||
return 'https://go.elementor.com/preview-not-loaded/#theme-files';
|
||||
}
|
||||
}
|
||||
144
wp-content/plugins/elementor/core/debug/inspector.php
Normal file
144
wp-content/plugins/elementor/core/debug/inspector.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Debug;
|
||||
|
||||
use Elementor\Settings;
|
||||
use Elementor\Tools;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Inspector {
|
||||
|
||||
protected $is_enabled = false;
|
||||
|
||||
protected $log = [];
|
||||
|
||||
/**
|
||||
* @since 2.1.2
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$is_debug = ( defined( 'WP_DEBUG' ) && WP_DEBUG );
|
||||
$option = get_option( 'elementor_enable_inspector', null );
|
||||
|
||||
$this->is_enabled = is_null( $option ) ? $is_debug : 'enable' === $option;
|
||||
|
||||
if ( $this->is_enabled ) {
|
||||
add_action( 'admin_bar_menu', [ $this, 'add_menu_in_admin_bar' ], 201 );
|
||||
}
|
||||
|
||||
add_action( 'elementor/admin/after_create_settings/' . Tools::PAGE_ID, [ $this, 'register_admin_tools_fields' ], 50 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.1.3
|
||||
* @access public
|
||||
*/
|
||||
public function is_enabled() {
|
||||
return $this->is_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.1.3
|
||||
* @access public
|
||||
*/
|
||||
public function register_admin_tools_fields( Tools $tools ) {
|
||||
$tools->add_fields( Settings::TAB_GENERAL, 'tools', [
|
||||
'enable_inspector' => [
|
||||
'label' => esc_html__( 'Debug Bar', 'elementor' ),
|
||||
'field_args' => [
|
||||
'type' => 'select',
|
||||
'std' => $this->is_enabled ? 'enable' : '',
|
||||
'options' => [
|
||||
'' => esc_html__( 'Disable', 'elementor' ),
|
||||
'enable' => esc_html__( 'Enable', 'elementor' ),
|
||||
],
|
||||
'desc' => esc_html__( 'Debug Bar adds an admin bar menu that lists all the templates that are used on a page that is being displayed.', 'elementor' ),
|
||||
],
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.1.2
|
||||
* @access public
|
||||
*/
|
||||
public function parse_template_path( $template ) {
|
||||
// `untrailingslashit` for windows path style.
|
||||
if ( 0 === strpos( $template, untrailingslashit( ELEMENTOR_PATH ) ) ) {
|
||||
return 'Elementor - ' . basename( $template );
|
||||
}
|
||||
|
||||
if ( 0 === strpos( $template, get_stylesheet_directory() ) ) {
|
||||
return wp_get_theme()->get( 'Name' ) . ' - ' . basename( $template );
|
||||
}
|
||||
|
||||
$plugins_dir = dirname( ELEMENTOR_PATH );
|
||||
if ( 0 === strpos( $template, $plugins_dir ) ) {
|
||||
return ltrim( str_replace( $plugins_dir, '', $template ), '/\\' );
|
||||
}
|
||||
|
||||
return str_replace( WP_CONTENT_DIR, '', $template );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.1.2
|
||||
* @access public
|
||||
*/
|
||||
public function add_log( $module, $title, $url = '' ) {
|
||||
if ( ! $this->is_enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $this->log[ $module ] ) ) {
|
||||
$this->log[ $module ] = [];
|
||||
}
|
||||
|
||||
$this->log[ $module ][] = [
|
||||
'title' => $title,
|
||||
'url' => $url,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.1.2
|
||||
* @access public
|
||||
*/
|
||||
public function add_menu_in_admin_bar( \WP_Admin_Bar $wp_admin_bar ) {
|
||||
if ( empty( $this->log ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wp_admin_bar->add_node( [
|
||||
'id' => 'elementor_inspector',
|
||||
'title' => esc_html__( 'Elementor Debugger', 'elementor' ),
|
||||
] );
|
||||
|
||||
foreach ( $this->log as $module => $log ) {
|
||||
$module_id = sanitize_key( $module );
|
||||
|
||||
$wp_admin_bar->add_menu( [
|
||||
'id' => 'elementor_inspector_' . $module_id,
|
||||
'parent' => 'elementor_inspector',
|
||||
'title' => $module,
|
||||
] );
|
||||
|
||||
foreach ( $log as $index => $row ) {
|
||||
$url = $row['url'];
|
||||
|
||||
unset( $row['url'] );
|
||||
|
||||
$wp_admin_bar->add_menu( [
|
||||
'id' => 'elementor_inspector_log_' . $module_id . '_' . $index,
|
||||
'parent' => 'elementor_inspector_' . $module_id,
|
||||
'href' => $url,
|
||||
'title' => implode( ' > ', $row ),
|
||||
'meta' => [
|
||||
'target' => '_blank',
|
||||
],
|
||||
] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Debug;
|
||||
|
||||
use Elementor\Core\Debug\Classes\Inspection_Base;
|
||||
use Elementor\Core\Debug\Classes\Theme_Missing;
|
||||
use Elementor\Core\Debug\Classes\Htaccess;
|
||||
|
||||
class Loading_Inspection_Manager {
|
||||
|
||||
public static $_instance = null;
|
||||
|
||||
public static function instance() {
|
||||
if ( null === self::$_instance ) {
|
||||
self::$_instance = new Loading_Inspection_Manager();
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/** @var Inspection_Base[] */
|
||||
private $inspections = [];
|
||||
|
||||
public function register_inspections() {
|
||||
$this->inspections['theme-missing'] = new Theme_Missing();
|
||||
$this->inspections['htaccess'] = new Htaccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Inspection_Base $inspection
|
||||
*/
|
||||
public function register_inspection( $inspection ) {
|
||||
$this->inspections[ $inspection->get_name() ] = $inspection;
|
||||
}
|
||||
|
||||
public function run_inspections() {
|
||||
$debug_data = [
|
||||
'message' => esc_html__( "We’re sorry, but something went wrong. Click on 'Learn more' and follow each of the steps to quickly solve it.", 'elementor' ),
|
||||
'header' => esc_html__( 'The preview could not be loaded', 'elementor' ),
|
||||
'doc_url' => 'https://go.elementor.com/preview-not-loaded/',
|
||||
];
|
||||
foreach ( $this->inspections as $inspection ) {
|
||||
if ( ! $inspection->run() ) {
|
||||
$debug_data = [
|
||||
'message' => $inspection->get_message(),
|
||||
'header' => $inspection->get_header_message(),
|
||||
'doc_url' => $inspection->get_help_doc_url(),
|
||||
'error' => true,
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $debug_data;
|
||||
}
|
||||
}
|
||||
300
wp-content/plugins/elementor/core/document-types/page-base.php
Normal file
300
wp-content/plugins/elementor/core/document-types/page-base.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DocumentTypes;
|
||||
|
||||
use Elementor\Controls_Manager;
|
||||
use Elementor\Core\Base\Document;
|
||||
use Elementor\Group_Control_Background;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class PageBase extends Document {
|
||||
|
||||
/**
|
||||
* Get Properties
|
||||
*
|
||||
* Return the document configuration properties.
|
||||
*
|
||||
* @since 2.0.8
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_properties() {
|
||||
$properties = parent::get_properties();
|
||||
|
||||
$properties['admin_tab_group'] = '';
|
||||
$properties['support_wp_page_templates'] = true;
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.1.2
|
||||
* @access protected
|
||||
* @static
|
||||
*/
|
||||
protected static function get_editor_panel_categories() {
|
||||
return Utils::array_inject(
|
||||
parent::get_editor_panel_categories(),
|
||||
'theme-elements',
|
||||
[
|
||||
'theme-elements-single' => [
|
||||
'title' => esc_html__( 'Single', 'elementor' ),
|
||||
'active' => false,
|
||||
'promotion' => [
|
||||
'url' => esc_url( 'https://go.elementor.com/go-pro-section-single-widget-panel/' ),
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_css_wrapper_selector() {
|
||||
return 'body.elementor-page-' . $this->get_main_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function register_controls() {
|
||||
parent::register_controls();
|
||||
|
||||
static::register_hide_title_control( $this );
|
||||
|
||||
static::register_post_fields_control( $this );
|
||||
|
||||
static::register_style_controls( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @static
|
||||
* @param Document $document
|
||||
*/
|
||||
public static function register_hide_title_control( $document ) {
|
||||
$document->start_injection( [
|
||||
'of' => 'post_status',
|
||||
'fallback' => [
|
||||
'of' => 'post_title',
|
||||
],
|
||||
] );
|
||||
|
||||
$document->add_control(
|
||||
'hide_title',
|
||||
[
|
||||
'label' => esc_html__( 'Hide Title', 'elementor' ),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'description' => sprintf(
|
||||
/* translators: 1: Link open tag, 2: Link close tag. */
|
||||
esc_html__( 'Set a different selector for the title in the %1$sLayout panel%2$s.', 'elementor' ),
|
||||
'<a href="javascript: $e.run( \'panel/global/open\' ).then( () => $e.route( \'panel/global/settings-layout\' ) )">',
|
||||
'</a>'
|
||||
),
|
||||
'separator' => 'before',
|
||||
'selectors' => [
|
||||
':root' => '--page-title-display: none',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$document->end_injection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @static
|
||||
* @param Document $document
|
||||
*/
|
||||
public static function register_style_controls( $document ) {
|
||||
$document->start_controls_section(
|
||||
'section_page_style',
|
||||
[
|
||||
'label' => esc_html__( 'Body Style', 'elementor' ),
|
||||
'tab' => Controls_Manager::TAB_STYLE,
|
||||
]
|
||||
);
|
||||
|
||||
$document->add_responsive_control(
|
||||
'margin',
|
||||
[
|
||||
'label' => esc_html__( 'Margin', 'elementor' ),
|
||||
'type' => Controls_Manager::DIMENSIONS,
|
||||
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
|
||||
'selectors' => [
|
||||
'{{WRAPPER}}' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$document->add_responsive_control(
|
||||
'padding',
|
||||
[
|
||||
'label' => esc_html__( 'Padding', 'elementor' ),
|
||||
'type' => Controls_Manager::DIMENSIONS,
|
||||
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
|
||||
'selectors' => [
|
||||
'{{WRAPPER}}' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$document->add_group_control(
|
||||
Group_Control_Background::get_type(),
|
||||
[
|
||||
'name' => 'background',
|
||||
'separator' => 'before',
|
||||
'fields_options' => [
|
||||
'image' => [
|
||||
// Currently isn't supported.
|
||||
'dynamic' => [
|
||||
'active' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$document->end_controls_section();
|
||||
|
||||
Plugin::$instance->controls_manager->add_custom_css_controls( $document );
|
||||
}
|
||||
|
||||
public static function get_labels() : array {
|
||||
$plural_label = static::get_plural_title();
|
||||
$singular_label = static::get_title();
|
||||
|
||||
$labels = [
|
||||
'name' => $plural_label, // Already translated.
|
||||
'singular_name' => $singular_label, // Already translated.
|
||||
'all_items' => sprintf( __( 'All %s', 'elementor' ), $plural_label ),
|
||||
'add_new' => esc_html__( 'Add New', 'elementor' ),
|
||||
'add_new_item' => sprintf( __( 'Add New %s', 'elementor' ), $singular_label ),
|
||||
'edit_item' => sprintf( __( 'Edit %s', 'elementor' ), $singular_label ),
|
||||
'new_item' => sprintf( __( 'New %s', 'elementor' ), $singular_label ),
|
||||
'view_item' => sprintf( __( 'View %s', 'elementor' ), $singular_label ),
|
||||
'search_items' => sprintf( __( 'Search %s', 'elementor' ), $plural_label ),
|
||||
'not_found' => sprintf( __( 'No %s found.', 'elementor' ), strtolower( $plural_label ) ),
|
||||
'not_found_in_trash' => sprintf( __( 'No %s found in Trash.', 'elementor' ), strtolower( $plural_label ) ),
|
||||
'parent_item_colon' => '',
|
||||
'menu_name' => $plural_label,
|
||||
];
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @static
|
||||
* @param Document $document
|
||||
*/
|
||||
public static function register_post_fields_control( $document ) {
|
||||
$document->start_injection( [
|
||||
'of' => 'post_status',
|
||||
'fallback' => [
|
||||
'of' => 'post_title',
|
||||
],
|
||||
] );
|
||||
|
||||
if ( post_type_supports( $document->post->post_type, 'excerpt' ) ) {
|
||||
$document->add_control(
|
||||
'post_excerpt',
|
||||
[
|
||||
'label' => esc_html__( 'Excerpt', 'elementor' ),
|
||||
'type' => Controls_Manager::TEXTAREA,
|
||||
'default' => $document->post->post_excerpt,
|
||||
'separator' => 'before',
|
||||
'ai' => [
|
||||
'type' => 'excerpt',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ( current_theme_supports( 'post-thumbnails' ) && post_type_supports( $document->post->post_type, 'thumbnail' ) ) {
|
||||
$document->add_control(
|
||||
'post_featured_image',
|
||||
[
|
||||
'label' => esc_html__( 'Featured Image', 'elementor' ),
|
||||
'type' => Controls_Manager::MEDIA,
|
||||
'default' => [
|
||||
'id' => get_post_thumbnail_id(),
|
||||
'url' => (string) get_the_post_thumbnail_url( $document->post->ID ),
|
||||
],
|
||||
'separator' => 'before',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_post_type_hierarchical( $document->post->post_type ) ) {
|
||||
$document->add_control(
|
||||
'menu_order',
|
||||
[
|
||||
'label' => esc_html__( 'Order', 'elementor' ),
|
||||
'type' => Controls_Manager::NUMBER,
|
||||
'default' => $document->post->menu_order,
|
||||
'separator' => 'before',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ( post_type_supports( $document->post->post_type, 'comments' ) ) {
|
||||
$document->add_control(
|
||||
'comment_status',
|
||||
[
|
||||
'label' => esc_html__( 'Allow Comments', 'elementor' ),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'return_value' => 'open',
|
||||
'default' => $document->post->comment_status,
|
||||
'separator' => 'before',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$document->end_injection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct( array $data = [] ) {
|
||||
if ( $data ) {
|
||||
$template = get_post_meta( $data['post_id'], '_wp_page_template', true );
|
||||
|
||||
if ( empty( $template ) ) {
|
||||
$template = 'default';
|
||||
}
|
||||
|
||||
$data['settings']['template'] = $template;
|
||||
}
|
||||
|
||||
parent::__construct( $data );
|
||||
}
|
||||
|
||||
protected function get_remote_library_config() {
|
||||
$config = parent::get_remote_library_config();
|
||||
|
||||
$config['category'] = '';
|
||||
$config['type'] = 'block';
|
||||
$config['default_route'] = 'templates/blocks';
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
67
wp-content/plugins/elementor/core/document-types/page.php
Normal file
67
wp-content/plugins/elementor/core/document-types/page.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DocumentTypes;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Page extends PageBase {
|
||||
|
||||
/**
|
||||
* Get Properties
|
||||
*
|
||||
* Return the page document configuration properties.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_properties() {
|
||||
$properties = parent::get_properties();
|
||||
|
||||
$properties['cpt'] = [ 'page' ];
|
||||
$properties['support_kit'] = true;
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Return the page document type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_type() {
|
||||
return 'wp-page';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Title
|
||||
*
|
||||
* Return the page document title.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return esc_html__( 'Page', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Plural Title
|
||||
*
|
||||
* Return the page document plural title.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plural_title() {
|
||||
return esc_html__( 'Pages', 'elementor' );
|
||||
}
|
||||
}
|
||||
67
wp-content/plugins/elementor/core/document-types/post.php
Normal file
67
wp-content/plugins/elementor/core/document-types/post.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DocumentTypes;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Post extends PageBase {
|
||||
|
||||
/**
|
||||
* Get Properties
|
||||
*
|
||||
* Return the post document configuration properties.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_properties() {
|
||||
$properties = parent::get_properties();
|
||||
|
||||
$properties['support_kit'] = true;
|
||||
$properties['cpt'] = [ 'post' ];
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Return the post document type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_type() {
|
||||
return 'wp-post';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Title
|
||||
*
|
||||
* Return the post document title.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_title() {
|
||||
return esc_html__( 'Post', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Plural Title
|
||||
*
|
||||
* Return the post document plural title.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plural_title() {
|
||||
return esc_html__( 'Posts', 'elementor' );
|
||||
}
|
||||
}
|
||||
777
wp-content/plugins/elementor/core/documents-manager.php
Normal file
777
wp-content/plugins/elementor/core/documents-manager.php
Normal file
@@ -0,0 +1,777 @@
|
||||
<?php
|
||||
namespace Elementor\Core;
|
||||
|
||||
use Elementor\Core\Base\Document;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Core\DocumentTypes\Page;
|
||||
use Elementor\Core\DocumentTypes\Post;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor documents manager.
|
||||
*
|
||||
* Elementor documents manager handler class is responsible for registering and
|
||||
* managing Elementor documents.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class Documents_Manager {
|
||||
|
||||
/**
|
||||
* Registered types.
|
||||
*
|
||||
* Holds the list of all the registered types.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @var Document[]
|
||||
*/
|
||||
protected $types = [];
|
||||
|
||||
/**
|
||||
* Registered documents.
|
||||
*
|
||||
* Holds the list of all the registered documents.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @var Document[]
|
||||
*/
|
||||
protected $documents = [];
|
||||
|
||||
/**
|
||||
* Current document.
|
||||
*
|
||||
* Holds the current document.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @var Document
|
||||
*/
|
||||
protected $current_doc;
|
||||
|
||||
/**
|
||||
* Switched data.
|
||||
*
|
||||
* Holds the current document when changing to the requested post.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $switched_data = [];
|
||||
|
||||
protected $cpt = [];
|
||||
|
||||
/**
|
||||
* Documents manager constructor.
|
||||
*
|
||||
* Initializing the Elementor documents manager.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'elementor/documents/register', [ $this, 'register_default_types' ], 0 );
|
||||
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
|
||||
add_filter( 'post_row_actions', [ $this, 'filter_post_row_actions' ], 11, 2 );
|
||||
add_filter( 'page_row_actions', [ $this, 'filter_post_row_actions' ], 11, 2 );
|
||||
add_filter( 'user_has_cap', [ $this, 'remove_user_edit_cap' ], 10, 3 );
|
||||
add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ajax actions.
|
||||
*
|
||||
* Process ajax action handles when saving data and discarding changes.
|
||||
*
|
||||
* Fired by `elementor/ajax/register_actions` action.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param Ajax $ajax_manager An instance of the ajax manager.
|
||||
*/
|
||||
public function register_ajax_actions( $ajax_manager ) {
|
||||
$ajax_manager->register_ajax_action( 'save_builder', [ $this, 'ajax_save' ] );
|
||||
$ajax_manager->register_ajax_action( 'discard_changes', [ $this, 'ajax_discard_changes' ] );
|
||||
$ajax_manager->register_ajax_action( 'get_document_config', [ $this, 'ajax_get_document_config' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register default types.
|
||||
*
|
||||
* Registers the default document types.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_default_types() {
|
||||
$default_types = [
|
||||
'post' => Post::get_class_full_name(), // BC.
|
||||
'wp-post' => Post::get_class_full_name(),
|
||||
'wp-page' => Page::get_class_full_name(),
|
||||
];
|
||||
|
||||
foreach ( $default_types as $type => $class ) {
|
||||
$this->register_document_type( $type, $class );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register document type.
|
||||
*
|
||||
* Registers a single document.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $type Document type name.
|
||||
* @param string $class The name of the class that registers the document type.
|
||||
* Full name with the namespace.
|
||||
*
|
||||
* @return Documents_Manager The updated document manager instance.
|
||||
*/
|
||||
public function register_document_type( $type, $class ) {
|
||||
$this->types[ $type ] = $class;
|
||||
|
||||
$cpt = $class::get_property( 'cpt' );
|
||||
|
||||
if ( $cpt ) {
|
||||
foreach ( $cpt as $post_type ) {
|
||||
$this->cpt[ $post_type ] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $class::get_property( 'register_type' ) ) {
|
||||
Source_Local::add_template_type( $type );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document.
|
||||
*
|
||||
* Retrieve the document data based on a post ID.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param bool $from_cache Optional. Whether to retrieve cached data. Default is true.
|
||||
*
|
||||
* @return false|Document Document data or false if post ID was not entered.
|
||||
*/
|
||||
public function get( $post_id, $from_cache = true ) {
|
||||
$this->register_types();
|
||||
|
||||
$post_id = absint( $post_id );
|
||||
|
||||
if ( ! $post_id || ! get_post( $post_id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve document post ID.
|
||||
*
|
||||
* Filters the document post ID.
|
||||
*
|
||||
* @since 2.0.7
|
||||
*
|
||||
* @param int $post_id The post ID of the document.
|
||||
*/
|
||||
$post_id = apply_filters( 'elementor/documents/get/post_id', $post_id );
|
||||
|
||||
if ( ! $from_cache || ! isset( $this->documents[ $post_id ] ) ) {
|
||||
$doc_type = $this->get_doc_type_by_id( $post_id );
|
||||
$doc_type_class = $this->get_document_type( $doc_type );
|
||||
|
||||
$this->documents[ $post_id ] = new $doc_type_class( [
|
||||
'post_id' => $post_id,
|
||||
] );
|
||||
}
|
||||
|
||||
return $this->documents[ $post_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a document after checking it exist and allowed to edit.
|
||||
*
|
||||
* @since 3.13.0
|
||||
*
|
||||
* @param int $post_id The post ID of the document.
|
||||
*
|
||||
* @return Document
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get_with_permissions( $id ): Document {
|
||||
$document = $this->get( $id );
|
||||
|
||||
if ( ! $document ) {
|
||||
throw new \Exception( 'Not found.' );
|
||||
}
|
||||
|
||||
if ( ! $document->is_editable_by_current_user() ) {
|
||||
throw new \Exception( 'Access denied.' );
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `void` version for `get_with_permissions`.
|
||||
*
|
||||
* @param $id
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function check_permissions( $id ) {
|
||||
$this->get_with_permissions( $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document or autosave.
|
||||
*
|
||||
* Retrieve either the document or the autosave.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param int $id Optional. Post ID. Default is `0`.
|
||||
* @param int $user_id Optional. User ID. Default is `0`.
|
||||
*
|
||||
* @return false|Document The document if it exist, False otherwise.
|
||||
*/
|
||||
public function get_doc_or_auto_save( $id, $user_id = 0 ) {
|
||||
$document = $this->get( $id );
|
||||
if ( $document && $document->get_autosave_id( $user_id ) ) {
|
||||
$document = $document->get_autosave( $user_id );
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document for frontend.
|
||||
*
|
||||
* Retrieve the document for frontend use.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param int $post_id Optional. Post ID. Default is `0`.
|
||||
*
|
||||
* @return false|Document The document if it exist, False otherwise.
|
||||
*/
|
||||
public function get_doc_for_frontend( $post_id ) {
|
||||
$preview_id = (int) Utils::get_super_global_value( $_GET, 'preview_id' );
|
||||
$is_preview = is_preview() && $post_id === $preview_id;
|
||||
$is_nonce_verify = wp_verify_nonce( Utils::get_super_global_value( $_GET, 'preview_nonce' ), 'post_preview_' . $preview_id );
|
||||
|
||||
if ( ( $is_preview && $is_nonce_verify ) || Plugin::$instance->preview->is_preview_mode() ) {
|
||||
$document = $this->get_doc_or_auto_save( $post_id, get_current_user_id() );
|
||||
} else {
|
||||
$document = $this->get( $post_id );
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document type.
|
||||
*
|
||||
* Retrieve the type of any given document.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @param string $fallback
|
||||
*
|
||||
* @return Document|bool The type of the document.
|
||||
*/
|
||||
public function get_document_type( $type, $fallback = 'post' ) {
|
||||
$types = $this->get_document_types();
|
||||
|
||||
if ( isset( $types[ $type ] ) ) {
|
||||
return $types[ $type ];
|
||||
}
|
||||
|
||||
if ( isset( $types[ $fallback ] ) ) {
|
||||
return $types[ $fallback ];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document types.
|
||||
*
|
||||
* Retrieve the all the registered document types.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $args Optional. An array of key => value arguments to match against
|
||||
* the properties. Default is empty array.
|
||||
* @param string $operator Optional. The logical operation to perform. 'or' means only one
|
||||
* element from the array needs to match; 'and' means all elements
|
||||
* must match; 'not' means no elements may match. Default 'and'.
|
||||
*
|
||||
* @return Document[] All the registered document types.
|
||||
*/
|
||||
public function get_document_types( $args = [], $operator = 'and' ) {
|
||||
$this->register_types();
|
||||
|
||||
if ( ! empty( $args ) ) {
|
||||
$types_properties = $this->get_types_properties();
|
||||
|
||||
$filtered = wp_filter_object_list( $types_properties, $args, $operator );
|
||||
|
||||
return array_intersect_key( $this->types, $filtered );
|
||||
}
|
||||
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document types with their properties.
|
||||
*
|
||||
* @return array A list of properties arrays indexed by the type.
|
||||
*/
|
||||
public function get_types_properties() {
|
||||
$types_properties = [];
|
||||
|
||||
foreach ( $this->get_document_types() as $type => $class ) {
|
||||
$types_properties[ $type ] = $class::get_properties();
|
||||
}
|
||||
return $types_properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a document.
|
||||
*
|
||||
* Create a new document using any given parameters.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $type Document type.
|
||||
* @param array $post_data An array containing the post data.
|
||||
* @param array $meta_data An array containing the post meta data.
|
||||
*
|
||||
* @return Document The type of the document.
|
||||
*/
|
||||
public function create( $type, $post_data = [], $meta_data = [] ) {
|
||||
$class = $this->get_document_type( $type, false );
|
||||
|
||||
if ( ! $class ) {
|
||||
return new \WP_Error( 500, sprintf( 'Type %s does not exist.', $type ) );
|
||||
}
|
||||
|
||||
if ( empty( $post_data['post_title'] ) ) {
|
||||
$post_data['post_title'] = esc_html__( 'Elementor', 'elementor' );
|
||||
if ( 'post' !== $type ) {
|
||||
$post_data['post_title'] = sprintf(
|
||||
/* translators: %s: Document title. */
|
||||
__( 'Elementor %s', 'elementor' ),
|
||||
call_user_func( [ $class, 'get_title' ] )
|
||||
);
|
||||
}
|
||||
$update_title = true;
|
||||
}
|
||||
|
||||
$meta_data['_elementor_edit_mode'] = 'builder';
|
||||
|
||||
// Save the type as-is for plugins that hooked at `wp_insert_post`.
|
||||
$meta_data[ Document::TYPE_META_KEY ] = $type;
|
||||
|
||||
$post_data['meta_input'] = $meta_data;
|
||||
|
||||
$post_types = $class::get_property( 'cpt' );
|
||||
|
||||
if ( ! empty( $post_types[0] ) && empty( $post_data['post_type'] ) ) {
|
||||
$post_data['post_type'] = $post_types[0];
|
||||
}
|
||||
|
||||
$post_id = wp_insert_post( $post_data );
|
||||
|
||||
if ( ! empty( $update_title ) ) {
|
||||
$post_data['ID'] = $post_id;
|
||||
$post_data['post_title'] .= ' #' . $post_id;
|
||||
|
||||
// The meta doesn't need update.
|
||||
unset( $post_data['meta_input'] );
|
||||
|
||||
wp_update_post( $post_data );
|
||||
}
|
||||
|
||||
/** @var Document $document */
|
||||
$document = new $class( [
|
||||
'post_id' => $post_id,
|
||||
] );
|
||||
|
||||
// Let the $document to re-save the template type by his way + version.
|
||||
$document->save( [] );
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user edit capabilities if document is not editable.
|
||||
*
|
||||
* Filters the user capabilities to disable editing in admin.
|
||||
*
|
||||
* @param array $allcaps An array of all the user's capabilities.
|
||||
* @param array $caps Actual capabilities for meta capability.
|
||||
* @param array $args Optional parameters passed to has_cap(), typically object ID.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function remove_user_edit_cap( $allcaps, $caps, $args ) {
|
||||
global $pagenow;
|
||||
|
||||
if ( ! in_array( $pagenow, [ 'post.php', 'edit.php' ], true ) ) {
|
||||
return $allcaps;
|
||||
}
|
||||
|
||||
// Don't touch not existing or not allowed caps.
|
||||
if ( empty( $caps[0] ) || empty( $allcaps[ $caps[0] ] ) ) {
|
||||
return $allcaps;
|
||||
}
|
||||
|
||||
$capability = $args[0];
|
||||
|
||||
if ( 'edit_post' !== $capability ) {
|
||||
return $allcaps;
|
||||
}
|
||||
|
||||
if ( empty( $args[2] ) ) {
|
||||
return $allcaps;
|
||||
}
|
||||
|
||||
$post_id = $args[2];
|
||||
|
||||
$document = Plugin::$instance->documents->get( $post_id );
|
||||
|
||||
if ( ! $document ) {
|
||||
return $allcaps;
|
||||
}
|
||||
|
||||
$allcaps[ $caps[0] ] = $document::get_property( 'is_editable' );
|
||||
|
||||
return $allcaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter Post Row Actions.
|
||||
*
|
||||
* Let the Document to filter the array of row action links on the Posts list table.
|
||||
*
|
||||
* @param array $actions
|
||||
* @param \WP_Post $post
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_post_row_actions( $actions, $post ) {
|
||||
$document = $this->get( $post->ID );
|
||||
|
||||
if ( $document ) {
|
||||
$actions = $document->filter_admin_row_actions( $actions );
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save document data using ajax.
|
||||
*
|
||||
* Save the document on the builder using ajax, when saving the changes, and refresh the editor.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param $request Post ID.
|
||||
*
|
||||
* @throws \Exception If current user don't have permissions to edit the post or the post is not using Elementor.
|
||||
*
|
||||
* @return array The document data after saving.
|
||||
*/
|
||||
public function ajax_save( $request ) {
|
||||
$document = $this->get( $request['editor_post_id'] );
|
||||
|
||||
if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
|
||||
throw new \Exception( 'Access denied.' );
|
||||
}
|
||||
|
||||
$this->switch_to_document( $document );
|
||||
|
||||
// Set the post as global post.
|
||||
Plugin::$instance->db->switch_to_post( $document->get_post()->ID );
|
||||
|
||||
$status = Document::STATUS_DRAFT;
|
||||
|
||||
if ( isset( $request['status'] ) && in_array( $request['status'], [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE, Document::STATUS_PENDING, Document::STATUS_AUTOSAVE ], true ) ) {
|
||||
$status = $request['status'];
|
||||
}
|
||||
|
||||
if ( Document::STATUS_AUTOSAVE === $status ) {
|
||||
// If the post is a draft - save the `autosave` to the original draft.
|
||||
// Allow a revision only if the original post is already published.
|
||||
if ( in_array( $document->get_post()->post_status, [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE ], true ) ) {
|
||||
$document = $document->get_autosave( 0, true );
|
||||
}
|
||||
}
|
||||
|
||||
// Set default page template because the footer-saver doesn't send default values,
|
||||
// But if the template was changed from canvas to default - it needed to save.
|
||||
if ( Utils::is_cpt_custom_templates_supported() && ! isset( $request['settings']['template'] ) ) {
|
||||
$request['settings']['template'] = 'default';
|
||||
}
|
||||
|
||||
$data = [
|
||||
'elements' => $request['elements'],
|
||||
'settings' => $request['settings'],
|
||||
];
|
||||
|
||||
$document->save( $data );
|
||||
|
||||
$post = $document->get_post();
|
||||
$main_post = $document->get_main_post();
|
||||
|
||||
// Refresh after save.
|
||||
$document = $this->get( $post->ID, false );
|
||||
|
||||
$return_data = [
|
||||
'status' => $post->post_status,
|
||||
'config' => [
|
||||
'document' => [
|
||||
'last_edited' => $document->get_last_edited(),
|
||||
'urls' => [
|
||||
'wp_preview' => $document->get_wp_preview_url(),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$post_status_object = get_post_status_object( $main_post->post_status );
|
||||
|
||||
if ( $post_status_object ) {
|
||||
$return_data['config']['document']['status'] = [
|
||||
'value' => $post_status_object->name,
|
||||
'label' => $post_status_object->label,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned documents ajax saved data.
|
||||
*
|
||||
* Filters the ajax data returned when saving the post on the builder.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param array $return_data The returned data.
|
||||
* @param Document $document The document instance.
|
||||
*/
|
||||
$return_data = apply_filters( 'elementor/documents/ajax_save/return_data', $return_data, $document );
|
||||
|
||||
return $return_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax discard changes.
|
||||
*
|
||||
* Load the document data from an autosave, deleting unsaved changes.
|
||||
*
|
||||
* @param $request
|
||||
*
|
||||
* @return bool True if changes discarded, False otherwise.
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
*/
|
||||
public function ajax_discard_changes( $request ) {
|
||||
$document = $this->get_with_permissions( $request['editor_post_id'] );
|
||||
|
||||
$autosave = $document->get_autosave();
|
||||
|
||||
if ( $autosave ) {
|
||||
$success = $autosave->delete();
|
||||
} else {
|
||||
$success = true;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function ajax_get_document_config( $request ) {
|
||||
$post_id = absint( $request['id'] );
|
||||
|
||||
Plugin::$instance->editor->set_post_id( $post_id );
|
||||
|
||||
$document = $this->get_doc_or_auto_save( $post_id );
|
||||
|
||||
if ( ! $document ) {
|
||||
throw new \Exception( 'Not found.' );
|
||||
}
|
||||
|
||||
if ( ! $document->is_editable_by_current_user() ) {
|
||||
throw new \Exception( 'Access denied.' );
|
||||
}
|
||||
|
||||
// Set the global data like $post, $authordata and etc
|
||||
Plugin::$instance->db->switch_to_post( $post_id );
|
||||
|
||||
$this->switch_to_document( $document );
|
||||
|
||||
// Change mode to Builder
|
||||
$document->set_is_built_with_elementor( true );
|
||||
|
||||
$doc_config = $document->get_config();
|
||||
|
||||
return $doc_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to document.
|
||||
*
|
||||
* Change the document to any new given document type.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param Document $document The document to switch to.
|
||||
*/
|
||||
public function switch_to_document( $document ) {
|
||||
// If is already switched, or is the same post, return.
|
||||
if ( $this->current_doc === $document ) {
|
||||
$this->switched_data[] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->switched_data[] = [
|
||||
'switched_doc' => $document,
|
||||
'original_doc' => $this->current_doc, // Note, it can be null if the global isn't set
|
||||
];
|
||||
|
||||
$this->current_doc = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore document.
|
||||
*
|
||||
* Rollback to the original document.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function restore_document() {
|
||||
$data = array_pop( $this->switched_data );
|
||||
|
||||
// If not switched, return.
|
||||
if ( ! $data ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->current_doc = $data['original_doc'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current document.
|
||||
*
|
||||
* Retrieve the current document.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @return Document The current document.
|
||||
*/
|
||||
public function get_current() {
|
||||
return $this->current_doc;
|
||||
}
|
||||
|
||||
public function localize_settings( $settings ) {
|
||||
$translations = [];
|
||||
|
||||
foreach ( $this->get_document_types() as $type => $class ) {
|
||||
$translations[ $type ] = $class::get_title();
|
||||
}
|
||||
|
||||
return array_replace_recursive( $settings, [
|
||||
'i18n' => $translations,
|
||||
] );
|
||||
}
|
||||
|
||||
private function register_types() {
|
||||
if ( ! did_action( 'elementor/documents/register' ) ) {
|
||||
/**
|
||||
* Register Elementor documents.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param Documents_Manager $this The document manager instance.
|
||||
*/
|
||||
do_action( 'elementor/documents/register', $this );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get create new post URL.
|
||||
*
|
||||
* Retrieve a custom URL for creating a new post/page using Elementor.
|
||||
*
|
||||
* @param string $post_type Optional. Post type slug. Default is 'page'.
|
||||
* @param string|null $template_type Optional. Query arg 'template_type'. Default is null.
|
||||
*
|
||||
* @return string A URL for creating new post using Elementor.
|
||||
*/
|
||||
public static function get_create_new_post_url( $post_type = 'page', $template_type = null ) {
|
||||
$query_args = [
|
||||
'action' => 'elementor_new_post',
|
||||
'post_type' => $post_type,
|
||||
];
|
||||
|
||||
if ( $template_type ) {
|
||||
$query_args['template_type'] = $template_type;
|
||||
}
|
||||
|
||||
$new_post_url = add_query_arg( $query_args, admin_url( 'edit.php' ) );
|
||||
|
||||
$new_post_url = add_query_arg( '_wpnonce', wp_create_nonce( 'elementor_action_new_post' ), $new_post_url );
|
||||
|
||||
return $new_post_url;
|
||||
}
|
||||
|
||||
private function get_doc_type_by_id( $post_id ) {
|
||||
// Auto-save inherits from the original post.
|
||||
if ( wp_is_post_autosave( $post_id ) ) {
|
||||
$post_id = wp_get_post_parent_id( $post_id );
|
||||
}
|
||||
|
||||
// Content built with Elementor.
|
||||
$template_type = get_post_meta( $post_id, Document::TYPE_META_KEY, true );
|
||||
|
||||
if ( $template_type && isset( $this->types[ $template_type ] ) ) {
|
||||
return $template_type;
|
||||
}
|
||||
|
||||
// Elementor installation on a site with existing content (which doesn't contain Elementor's meta).
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
return $this->cpt[ $post_type ] ?? 'post';
|
||||
}
|
||||
}
|
||||
191
wp-content/plugins/elementor/core/dynamic-tags/base-tag.php
Normal file
191
wp-content/plugins/elementor/core/dynamic-tags/base-tag.php
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DynamicTags;
|
||||
|
||||
use Elementor\Controls_Stack;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor base tag.
|
||||
*
|
||||
* An abstract class to register new Elementor tags.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Base_Tag extends Controls_Stack {
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
final public static function get_type() {
|
||||
return 'tag';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @abstract
|
||||
*/
|
||||
abstract public function get_categories();
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @abstract
|
||||
*/
|
||||
abstract public function get_group();
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @abstract
|
||||
*/
|
||||
abstract public function get_title();
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @abstract
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
abstract public function get_content( array $options = [] );
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @abstract
|
||||
*/
|
||||
abstract public function get_content_type();
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_panel_template_setting_key() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function is_settings_required() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.9
|
||||
* @access public
|
||||
*/
|
||||
public function get_editor_config() {
|
||||
ob_start();
|
||||
|
||||
$this->print_panel_template();
|
||||
|
||||
$panel_template = ob_get_clean();
|
||||
|
||||
return [
|
||||
'name' => $this->get_name(),
|
||||
'title' => $this->get_title(),
|
||||
'panel_template' => $panel_template,
|
||||
'categories' => $this->get_categories(),
|
||||
'group' => $this->get_group(),
|
||||
'controls' => $this->get_controls(),
|
||||
'content_type' => $this->get_content_type(),
|
||||
'settings_required' => $this->is_settings_required(),
|
||||
'editable' => $this->is_editable(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function print_panel_template() {
|
||||
$panel_template_setting_key = $this->get_panel_template_setting_key();
|
||||
|
||||
if ( ! $panel_template_setting_key ) {
|
||||
return;
|
||||
}
|
||||
?><#
|
||||
var key = <?php echo esc_html( $panel_template_setting_key ); ?>;
|
||||
|
||||
if ( key ) {
|
||||
var settingsKey = "<?php echo esc_html( $panel_template_setting_key ); ?>";
|
||||
|
||||
/*
|
||||
* If the tag has controls,
|
||||
* and key is an existing control (and not an old one),
|
||||
* and the control has options (select/select2),
|
||||
* and the key is an existing option (and not in a group or an old one).
|
||||
*/
|
||||
if ( controls && controls[settingsKey] ) {
|
||||
var controlSettings = controls[settingsKey];
|
||||
|
||||
if ( controlSettings.options && controlSettings.options[ key ] ) {
|
||||
key = controlSettings.options[ key ];
|
||||
} else if ( controlSettings.groups ) {
|
||||
var label = _.filter( _.pluck( _.pluck( controls.key.groups, 'options' ), key ) );
|
||||
|
||||
if ( label[0] ) {
|
||||
key = label[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print( '(' + _.escape( key ) + ')' );
|
||||
}
|
||||
#>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
final public function get_unique_name() {
|
||||
return 'tag-' . $this->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function register_advanced_section() {}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*/
|
||||
final protected function init_controls() {
|
||||
Plugin::$instance->controls_manager->open_stack( $this );
|
||||
|
||||
$this->start_controls_section( 'settings', [
|
||||
'label' => esc_html__( 'Settings', 'elementor' ),
|
||||
] );
|
||||
|
||||
if ( $this->has_own_method( '_register_controls' ) ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_register_controls', '3.1.0', __CLASS__ . '::register_controls()' );
|
||||
|
||||
$this->_register_controls();
|
||||
} else {
|
||||
$this->register_controls();
|
||||
}
|
||||
|
||||
$this->end_controls_section();
|
||||
|
||||
// If in fact no controls were registered, empty the stack
|
||||
if ( 1 === count( Plugin::$instance->controls_manager->get_stacks( $this->get_unique_name() )['controls'] ) ) {
|
||||
Plugin::$instance->controls_manager->open_stack( $this );
|
||||
}
|
||||
|
||||
$this->register_advanced_section();
|
||||
}
|
||||
}
|
||||
46
wp-content/plugins/elementor/core/dynamic-tags/data-tag.php
Normal file
46
wp-content/plugins/elementor/core/dynamic-tags/data-tag.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DynamicTags;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor base data tag.
|
||||
*
|
||||
* An abstract class to register new Elementor data tags.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Data_Tag extends Base_Tag {
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
* @abstract
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
abstract protected function get_value( array $options = [] );
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
final public function get_content_type() {
|
||||
return 'plain';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_content( array $options = [] ) {
|
||||
return $this->get_value( $options );
|
||||
}
|
||||
}
|
||||
127
wp-content/plugins/elementor/core/dynamic-tags/dynamic-css.php
Normal file
127
wp-content/plugins/elementor/core/dynamic-tags/dynamic-css.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DynamicTags;
|
||||
|
||||
use Elementor\Controls_Stack;
|
||||
use Elementor\Core\Files\CSS\Post as Post_CSS;
|
||||
use Elementor\Core\Files\CSS\Post_Local_Cache;
|
||||
use Elementor\Core\Files\CSS\Post_Preview;
|
||||
use Elementor\Element_Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Dynamic_CSS extends Post_Local_Cache {
|
||||
|
||||
private $post_dynamic_elements_ids;
|
||||
|
||||
private $post_id_for_data;
|
||||
|
||||
protected function get_post_id_for_data() {
|
||||
if ( empty( $this->post_dynamic_elements_ids ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->post_id_for_data;
|
||||
}
|
||||
|
||||
protected function is_global_parsing_supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function render_styles( Element_Base $element ) {
|
||||
$id = $element->get_id();
|
||||
|
||||
if ( in_array( $id, $this->post_dynamic_elements_ids ) ) {
|
||||
parent::render_styles( $element );
|
||||
}
|
||||
|
||||
foreach ( $element->get_children() as $child_element ) {
|
||||
$this->render_styles( $child_element );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic_CSS constructor.
|
||||
*
|
||||
* @since 2.0.13
|
||||
* @access public
|
||||
*
|
||||
* @param int $post_id Post ID
|
||||
* @param Post_CSS $post_css_file
|
||||
*/
|
||||
public function __construct( $post_id, Post_CSS $post_css_file ) {
|
||||
if ( $post_css_file instanceof Post_Preview ) {
|
||||
$this->post_id_for_data = $post_css_file->get_post_id_for_data();
|
||||
} else {
|
||||
$this->post_id_for_data = $post_id;
|
||||
}
|
||||
|
||||
$this->post_dynamic_elements_ids = $post_css_file->get_meta( 'dynamic_elements_ids' );
|
||||
|
||||
parent::__construct( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.13
|
||||
* @access public
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'dynamic';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Responsive Control Duplication Mode
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_responsive_control_duplication_mode() {
|
||||
return 'dynamic';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.13
|
||||
* @access protected
|
||||
*/
|
||||
protected function use_external_file() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.13
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_file_handle_id() {
|
||||
return 'elementor-post-dynamic-' . $this->get_post_id_for_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.13
|
||||
* @access public
|
||||
*/
|
||||
public function add_controls_stack_style_rules( Controls_Stack $controls_stack, array $controls, array $values, array $placeholders, array $replacements, array $all_controls = null ) {
|
||||
$dynamic_settings = $controls_stack->get_settings( '__dynamic__' );
|
||||
|
||||
if ( ! empty( $dynamic_settings ) ) {
|
||||
$controls = array_intersect_key( $controls, $dynamic_settings );
|
||||
|
||||
$all_controls = $controls_stack->get_controls();
|
||||
|
||||
$parsed_dynamic_settings = $controls_stack->parse_dynamic_settings( $values, $controls );
|
||||
|
||||
foreach ( $controls as $control ) {
|
||||
if ( ! empty( $control['style_fields'] ) ) {
|
||||
$this->add_repeater_control_style_rules( $controls_stack, $control, $values[ $control['name'] ], $placeholders, $replacements );
|
||||
}
|
||||
|
||||
if ( empty( $control['selectors'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->add_control_style_rules( $control, $parsed_dynamic_settings, $all_controls, $placeholders, $replacements );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
508
wp-content/plugins/elementor/core/dynamic-tags/manager.php
Normal file
508
wp-content/plugins/elementor/core/dynamic-tags/manager.php
Normal file
@@ -0,0 +1,508 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DynamicTags;
|
||||
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Core\Files\CSS\Post;
|
||||
use Elementor\Core\Files\CSS\Post_Preview;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\User;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Manager {
|
||||
|
||||
const TAG_LABEL = 'elementor-tag';
|
||||
|
||||
const MODE_RENDER = 'render';
|
||||
|
||||
const MODE_REMOVE = 'remove';
|
||||
|
||||
const DYNAMIC_SETTING_KEY = '__dynamic__';
|
||||
|
||||
private $tags_groups = [];
|
||||
|
||||
private $tags_info = [];
|
||||
|
||||
private $parsing_mode = self::MODE_RENDER;
|
||||
|
||||
/**
|
||||
* Dynamic tags manager constructor.
|
||||
*
|
||||
* Initializing Elementor dynamic tags manager.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->add_actions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.0
|
||||
*/
|
||||
public function localize_settings() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' );
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse dynamic tags text.
|
||||
*
|
||||
* Receives the dynamic tag text, and returns a single value or multiple values
|
||||
* from the tag callback function.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $text Dynamic tag text.
|
||||
* @param array $settings The dynamic tag settings.
|
||||
* @param callable $parse_callback The functions that renders the dynamic tag.
|
||||
*
|
||||
* @return string|string[]|mixed A single string or an array of strings with
|
||||
* the return values from each tag callback
|
||||
* function.
|
||||
*/
|
||||
public function parse_tags_text( $text, array $settings, callable $parse_callback ) {
|
||||
if ( ! empty( $settings['returnType'] ) && 'object' === $settings['returnType'] ) {
|
||||
$value = $this->parse_tag_text( $text, $settings, $parse_callback );
|
||||
} else {
|
||||
|
||||
$value = preg_replace_callback( '/\[' . self::TAG_LABEL . '.+?(?=\])\]/', function( $tag_text_match ) use ( $settings, $parse_callback ) {
|
||||
return $this->parse_tag_text( $tag_text_match[0], $settings, $parse_callback );
|
||||
}, $text );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse dynamic tag text.
|
||||
*
|
||||
* Receives the dynamic tag text, and returns the value from the callback
|
||||
* function.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $tag_text Dynamic tag text.
|
||||
* @param array $settings The dynamic tag settings.
|
||||
* @param callable $parse_callback The functions that renders the dynamic tag.
|
||||
*
|
||||
* @return string|array|mixed If the tag was not found an empty string or an
|
||||
* empty array will be returned, otherwise the
|
||||
* return value from the tag callback function.
|
||||
*/
|
||||
public function parse_tag_text( $tag_text, array $settings, callable $parse_callback ) {
|
||||
$tag_data = $this->tag_text_to_tag_data( $tag_text );
|
||||
|
||||
if ( ! $tag_data ) {
|
||||
if ( ! empty( $settings['returnType'] ) && 'object' === $settings['returnType'] ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
return call_user_func_array( $parse_callback, array_values( $tag_data ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $tag_text
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function tag_text_to_tag_data( $tag_text ) {
|
||||
preg_match( '/id="(.*?(?="))"/', $tag_text, $tag_id_match );
|
||||
preg_match( '/name="(.*?(?="))"/', $tag_text, $tag_name_match );
|
||||
preg_match( '/settings="(.*?(?="]))/', $tag_text, $tag_settings_match );
|
||||
|
||||
if ( ! $tag_id_match || ! $tag_name_match || ! $tag_settings_match ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $tag_id_match[1],
|
||||
'name' => $tag_name_match[1],
|
||||
'settings' => json_decode( urldecode( $tag_settings_match[1] ), true ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic tag to text.
|
||||
*
|
||||
* Retrieve the shortcode that represents the dynamic tag.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param Base_Tag $tag An instance of the dynamic tag.
|
||||
*
|
||||
* @return string The shortcode that represents the dynamic tag.
|
||||
*/
|
||||
public function tag_to_text( Base_Tag $tag ) {
|
||||
return sprintf( '[%1$s id="%2$s" name="%3$s" settings="%4$s"]', self::TAG_LABEL, $tag->get_id(), $tag->get_name(), urlencode( wp_json_encode( $tag->get_settings(), JSON_FORCE_OBJECT ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @param string $tag_id
|
||||
* @param string $tag_name
|
||||
* @param array $settings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function tag_data_to_tag_text( $tag_id, $tag_name, array $settings = [] ) {
|
||||
$tag = $this->create_tag( $tag_id, $tag_name, $settings );
|
||||
|
||||
if ( ! $tag ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->tag_to_text( $tag );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @param string $tag_id
|
||||
* @param string $tag_name
|
||||
* @param array $settings
|
||||
*
|
||||
* @return Tag|null
|
||||
*/
|
||||
public function create_tag( $tag_id, $tag_name, array $settings = [] ) {
|
||||
$tag_info = $this->get_tag_info( $tag_name );
|
||||
|
||||
if ( ! $tag_info ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag_class = $tag_info['class'];
|
||||
|
||||
return new $tag_class( [
|
||||
'settings' => $settings,
|
||||
'id' => $tag_id,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param $tag_id
|
||||
* @param $tag_name
|
||||
* @param array $settings
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_tag_data_content( $tag_id, $tag_name, array $settings = [] ) {
|
||||
if ( self::MODE_REMOVE === $this->parsing_mode ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag = $this->create_tag( $tag_id, $tag_name, $settings );
|
||||
|
||||
if ( ! $tag ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tag->get_content();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param $tag_name
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get_tag_info( $tag_name ) {
|
||||
$tags = $this->get_tags();
|
||||
|
||||
if ( empty( $tags[ $tag_name ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tags[ $tag_name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.9
|
||||
* @access public
|
||||
*/
|
||||
public function get_tags() {
|
||||
if ( ! did_action( 'elementor/dynamic_tags/register_tags' ) ) {
|
||||
/**
|
||||
* Register dynamic tags.
|
||||
*
|
||||
* Fires when Elementor registers dynamic tags.
|
||||
*
|
||||
* @since 2.0.9
|
||||
* @deprecated 3.5.0 Use `elementor/dynamic_tags/register` hook instead.
|
||||
*
|
||||
* @param Manager $this Dynamic tags manager.
|
||||
*/
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action(
|
||||
'elementor/dynamic_tags/register_tags',
|
||||
[ $this ],
|
||||
'3.5.0',
|
||||
'elementor/dynamic_tags/register'
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! did_action( 'elementor/dynamic_tags/register' ) ) {
|
||||
/**
|
||||
* Register dynamic tags.
|
||||
*
|
||||
* Fires when Elementor registers dynamic tags.
|
||||
*
|
||||
* @since 3.5.0
|
||||
*
|
||||
* @param Manager $this Dynamic tags manager.
|
||||
*/
|
||||
do_action( 'elementor/dynamic_tags/register', $this );
|
||||
}
|
||||
|
||||
return $this->tags_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
* @deprecated 3.5.0 Use `register()` method instead.
|
||||
*
|
||||
* @param string $class
|
||||
*/
|
||||
public function register_tag( $class ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
|
||||
__METHOD__,
|
||||
'3.5.0',
|
||||
'register()'
|
||||
);
|
||||
|
||||
/** @var Base_Tag $tag */
|
||||
$instance = new $class();
|
||||
|
||||
$this->register( $instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new Dynamic Tag.
|
||||
*
|
||||
* @param Base_Tag $dynamic_tag_instance
|
||||
*
|
||||
* @return void
|
||||
* @since 3.5.0
|
||||
* @access public
|
||||
*
|
||||
*/
|
||||
public function register( Base_Tag $dynamic_tag_instance ) {
|
||||
$this->tags_info[ $dynamic_tag_instance->get_name() ] = [
|
||||
'class' => get_class( $dynamic_tag_instance ),
|
||||
'instance' => $dynamic_tag_instance,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.9
|
||||
* @access public
|
||||
* @deprecated 3.5.0 Use `unregister()` method instead.
|
||||
*
|
||||
* @param string $tag_name
|
||||
*/
|
||||
public function unregister_tag( $tag_name ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
|
||||
__METHOD__,
|
||||
'3.5.0',
|
||||
'unregister()'
|
||||
);
|
||||
|
||||
$this->unregister( $tag_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a dynamic tag.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $tag_name Dynamic Tag name to unregister.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister( $tag_name ) {
|
||||
unset( $this->tags_info[ $tag_name ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param $group_name
|
||||
* @param array $group_settings
|
||||
*/
|
||||
public function register_group( $group_name, array $group_settings ) {
|
||||
$default_group_settings = [
|
||||
'title' => '',
|
||||
];
|
||||
|
||||
$group_settings = array_merge( $default_group_settings, $group_settings );
|
||||
|
||||
$this->tags_groups[ $group_name ] = $group_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function print_templates() {
|
||||
foreach ( $this->get_tags() as $tag_name => $tag_info ) {
|
||||
$tag = $tag_info['instance'];
|
||||
|
||||
if ( ! $tag instanceof Tag ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tag->print_template();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_tags_config() {
|
||||
$config = [];
|
||||
|
||||
foreach ( $this->get_tags() as $tag_name => $tag_info ) {
|
||||
/** @var Tag $tag */
|
||||
$tag = $tag_info['instance'];
|
||||
|
||||
$config[ $tag_name ] = $tag->get_editor_config();
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_config() {
|
||||
return [
|
||||
'tags' => $this->get_tags_config(),
|
||||
'groups' => $this->tags_groups,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @throws \Exception If post ID is missing.
|
||||
* @throws \Exception If current user don't have permissions to edit the post.
|
||||
*/
|
||||
public function ajax_render_tags( $data ) {
|
||||
if ( empty( $data['post_id'] ) ) {
|
||||
throw new \Exception( 'Missing post id.' );
|
||||
}
|
||||
|
||||
if ( ! User::is_current_user_can_edit( $data['post_id'] ) ) {
|
||||
throw new \Exception( 'Access denied.' );
|
||||
}
|
||||
|
||||
Plugin::$instance->db->switch_to_post( $data['post_id'] );
|
||||
|
||||
/**
|
||||
* Before dynamic tags rendered.
|
||||
*
|
||||
* Fires before Elementor renders the dynamic tags.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
do_action( 'elementor/dynamic_tags/before_render' );
|
||||
|
||||
$tags_data = [];
|
||||
|
||||
foreach ( $data['tags'] as $tag_key ) {
|
||||
$tag_key_parts = explode( '-', $tag_key );
|
||||
|
||||
$tag_name = base64_decode( $tag_key_parts[0] );
|
||||
|
||||
$tag_settings = json_decode( urldecode( base64_decode( $tag_key_parts[1] ) ), true );
|
||||
|
||||
$tag = $this->create_tag( null, $tag_name, $tag_settings );
|
||||
|
||||
$tags_data[ $tag_key ] = $tag->get_content();
|
||||
}
|
||||
|
||||
/**
|
||||
* After dynamic tags rendered.
|
||||
*
|
||||
* Fires after Elementor renders the dynamic tags.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
do_action( 'elementor/dynamic_tags/after_render' );
|
||||
|
||||
return $tags_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param $mode
|
||||
*/
|
||||
public function set_parsing_mode( $mode ) {
|
||||
$this->parsing_mode = $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_parsing_mode() {
|
||||
return $this->parsing_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.1.0
|
||||
* @access public
|
||||
* @param Post $css_file
|
||||
*/
|
||||
public function after_enqueue_post_css( $css_file ) {
|
||||
$post_id = $css_file->get_post_id();
|
||||
$should_enqueue = apply_filters( 'elementor/css-file/dynamic/should_enqueue', true, $post_id );
|
||||
|
||||
if ( $should_enqueue ) {
|
||||
$css_file = Dynamic_CSS::create( $post_id, $css_file );
|
||||
$css_file->enqueue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_ajax_actions( Ajax $ajax ) {
|
||||
$ajax->register_ajax_action( 'render_tags', [ $this, 'ajax_render_tags' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*/
|
||||
private function add_actions() {
|
||||
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
|
||||
add_action( 'elementor/css-file/post/enqueue', [ $this, 'after_enqueue_post_css' ] );
|
||||
}
|
||||
}
|
||||
124
wp-content/plugins/elementor/core/dynamic-tags/tag.php
Normal file
124
wp-content/plugins/elementor/core/dynamic-tags/tag.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DynamicTags;
|
||||
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor tag.
|
||||
*
|
||||
* An abstract class to register new Elementor tag.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @abstract
|
||||
*/
|
||||
abstract class Tag extends Base_Tag {
|
||||
|
||||
const WRAPPED_TAG = false;
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_content( array $options = [] ) {
|
||||
$settings = $this->get_settings();
|
||||
|
||||
ob_start();
|
||||
|
||||
$this->render();
|
||||
|
||||
$value = ob_get_clean();
|
||||
|
||||
if ( ! Utils::is_empty( $value ) ) {
|
||||
// TODO: fix spaces in `before`/`after` if WRAPPED_TAG ( conflicted with .elementor-tag { display: inline-flex; } );
|
||||
if ( ! Utils::is_empty( $settings, 'before' ) ) {
|
||||
$value = wp_kses_post( $settings['before'] ) . $value;
|
||||
}
|
||||
|
||||
if ( ! Utils::is_empty( $settings, 'after' ) ) {
|
||||
$value .= wp_kses_post( $settings['after'] );
|
||||
}
|
||||
|
||||
if ( static::WRAPPED_TAG ) :
|
||||
$value = '<span id="elementor-tag-' . esc_attr( $this->get_id() ) . '" class="elementor-tag">' . $value . '</span>';
|
||||
endif;
|
||||
|
||||
} elseif ( ! Utils::is_empty( $settings, 'fallback' ) ) {
|
||||
$value = wp_kses_post_deep( $settings['fallback'] );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
final public function get_content_type() {
|
||||
return 'ui';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.9
|
||||
* @access public
|
||||
*/
|
||||
public function get_editor_config() {
|
||||
$config = parent::get_editor_config();
|
||||
|
||||
$config['wrapped_tag'] = $this::WRAPPED_TAG;
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function register_advanced_section() {
|
||||
$this->start_controls_section(
|
||||
'advanced',
|
||||
[
|
||||
'label' => esc_html__( 'Advanced', 'elementor' ),
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'before',
|
||||
[
|
||||
'label' => esc_html__( 'Before', 'elementor' ),
|
||||
'ai' => [
|
||||
'active' => false,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'after',
|
||||
[
|
||||
'label' => esc_html__( 'After', 'elementor' ),
|
||||
'ai' => [
|
||||
'active' => false,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'fallback',
|
||||
[
|
||||
'label' => esc_html__( 'Fallback', 'elementor' ),
|
||||
'ai' => [
|
||||
'active' => false,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Data\Globals;
|
||||
|
||||
use Elementor\Data\V2\Base\Controller as Controller_Base;
|
||||
use Elementor\Data\V2\Base\Endpoint;
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Controller extends Controller_Base {
|
||||
public function get_name() {
|
||||
return 'globals';
|
||||
}
|
||||
|
||||
public function register_endpoints() {
|
||||
$this->register_endpoint( new Endpoints\Colors( $this ) );
|
||||
$this->register_endpoint( new Endpoints\Typography( $this ) );
|
||||
}
|
||||
|
||||
public function get_collection_params() {
|
||||
// Does not have 'get_items' args (OPTIONS).
|
||||
// Maybe TODO: try `$this->get_index_endpoint()->get_collection_params()`.
|
||||
return [];
|
||||
}
|
||||
|
||||
public function get_permission_callback( $request ) {
|
||||
// Allow internal get global values. (e.g render global.css for a visitor)
|
||||
if ( 'GET' === $request->get_method() && Plugin::$instance->data_manager_v2->is_internal() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return current_user_can( 'edit_posts' );
|
||||
}
|
||||
|
||||
protected function register_index_endpoint() {
|
||||
$this->register_endpoint( new Endpoint\Index\AllChildren( $this ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Data\Globals\Endpoints;
|
||||
|
||||
use Elementor\Data\V2\Base\Endpoint;
|
||||
use Elementor\Data\V2\Base\Exceptions\Data_Exception;
|
||||
use Elementor\Data\V2\Base\Exceptions\Error_404;
|
||||
use Elementor\Plugin;
|
||||
|
||||
abstract class Base extends Endpoint {
|
||||
protected function register() {
|
||||
parent::register();
|
||||
|
||||
$args = [
|
||||
'id_arg_type_regex' => '[\w]+',
|
||||
];
|
||||
|
||||
$this->register_item_route( \WP_REST_Server::READABLE, $args );
|
||||
$this->register_item_route( \WP_REST_Server::CREATABLE, $args );
|
||||
$this->register_item_route( \WP_REST_Server::DELETABLE, $args );
|
||||
}
|
||||
|
||||
public function get_items( $request ) {
|
||||
return $this->get_kit_items();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \Elementor\Data\V2\Base\Exceptions\Error_404
|
||||
*/
|
||||
public function get_item( $id, $request ) {
|
||||
$items = $this->get_kit_items();
|
||||
|
||||
if ( ! isset( $items[ $id ] ) ) {
|
||||
throw new Error_404( esc_html__( 'The Global value you are trying to use is not available.', 'elementor' ),
|
||||
'global_not_found'
|
||||
);
|
||||
}
|
||||
|
||||
return $items[ $id ];
|
||||
}
|
||||
|
||||
public function create_item( $id, $request ) {
|
||||
$item = $request->get_body_params();
|
||||
|
||||
if ( ! isset( $item['title'] ) ) {
|
||||
return new Data_Exception( esc_html__( 'Invalid title', 'elementor' ), 'invalid_title' );
|
||||
}
|
||||
|
||||
$kit = Plugin::$instance->kits_manager->get_active_kit();
|
||||
|
||||
$item['id'] = $id;
|
||||
|
||||
$db_item = $this->convert_db_format( $item );
|
||||
|
||||
$kit->add_repeater_row( 'custom_' . $this->get_name(), $db_item );
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
abstract protected function get_kit_items();
|
||||
|
||||
/**
|
||||
* @param array $item frontend format.
|
||||
* @return array backend format.
|
||||
*/
|
||||
abstract protected function convert_db_format( $item );
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Data\Globals\Endpoints;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Colors extends Base {
|
||||
public function get_name() {
|
||||
return 'colors';
|
||||
}
|
||||
|
||||
public function get_format() {
|
||||
return 'globals/colors/{id}';
|
||||
}
|
||||
|
||||
protected function get_kit_items() {
|
||||
$result = [];
|
||||
$kit = Plugin::$instance->kits_manager->get_active_kit_for_frontend();
|
||||
|
||||
$system_items = $kit->get_settings_for_display( 'system_colors' );
|
||||
$custom_items = $kit->get_settings_for_display( 'custom_colors' );
|
||||
|
||||
if ( ! $system_items ) {
|
||||
$system_items = [];
|
||||
}
|
||||
|
||||
if ( ! $custom_items ) {
|
||||
$custom_items = [];
|
||||
}
|
||||
|
||||
$items = array_merge( $system_items, $custom_items );
|
||||
|
||||
foreach ( $items as $index => $item ) {
|
||||
$id = $item['_id'];
|
||||
$result[ $id ] = [
|
||||
'id' => $id,
|
||||
'title' => $item['title'],
|
||||
'value' => $item['color'],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function convert_db_format( $item ) {
|
||||
return [
|
||||
'_id' => $item['id'],
|
||||
'title' => $item['title'],
|
||||
'color' => $item['value'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Data\Globals\Endpoints;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
class Typography extends Base {
|
||||
public function get_name() {
|
||||
return 'typography';
|
||||
}
|
||||
|
||||
public function get_format() {
|
||||
return 'globals/typography/{id}';
|
||||
}
|
||||
|
||||
protected function get_kit_items() {
|
||||
$result = [];
|
||||
|
||||
$kit = Plugin::$instance->kits_manager->get_active_kit_for_frontend();
|
||||
|
||||
// Use raw settings that doesn't have default values.
|
||||
$kit_raw_settings = $kit->get_data( 'settings' );
|
||||
|
||||
if ( isset( $kit_raw_settings['system_typography'] ) ) {
|
||||
$system_items = $kit_raw_settings['system_typography'];
|
||||
} else {
|
||||
// Get default items, but without empty defaults.
|
||||
$control = $kit->get_controls( 'system_typography' );
|
||||
$system_items = $control['default'];
|
||||
}
|
||||
|
||||
$custom_items = $kit->get_settings( 'custom_typography' );
|
||||
|
||||
if ( ! $custom_items ) {
|
||||
$custom_items = [];
|
||||
}
|
||||
|
||||
$items = array_merge( $system_items, $custom_items );
|
||||
|
||||
foreach ( $items as $index => &$item ) {
|
||||
foreach ( $item as $setting => $value ) {
|
||||
$new_setting = str_replace( 'styles_', '', $setting, $count );
|
||||
if ( $count ) {
|
||||
$item[ $new_setting ] = $value;
|
||||
unset( $item[ $setting ] );
|
||||
}
|
||||
}
|
||||
|
||||
$id = $item['_id'];
|
||||
|
||||
$result[ $id ] = [
|
||||
'title' => $item['title'],
|
||||
'id' => $id,
|
||||
];
|
||||
|
||||
unset( $item['_id'], $item['title'] );
|
||||
|
||||
$result[ $id ]['value'] = $item;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function convert_db_format( $item ) {
|
||||
$db_format = [
|
||||
'_id' => $item['id'],
|
||||
'title' => $item['title'],
|
||||
];
|
||||
|
||||
$db_format = array_merge( $item['value'], $db_format );
|
||||
|
||||
return $db_format;
|
||||
}
|
||||
}
|
||||
672
wp-content/plugins/elementor/core/editor/editor.php
Normal file
672
wp-content/plugins/elementor/core/editor/editor.php
Normal file
@@ -0,0 +1,672 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor;
|
||||
|
||||
use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module;
|
||||
use Elementor\Core\Debug\Loading_Inspection_Manager;
|
||||
use Elementor\Core\Editor\Loader\Editor_Loader_Factory;
|
||||
use Elementor\Core\Editor\Loader\Editor_Loader_Interface;
|
||||
use Elementor\Core\Experiments\Manager as Experiments_Manager;
|
||||
use Elementor\Core\Settings\Manager as SettingsManager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
use Elementor\Utils;
|
||||
use Elementor\Core\Editor\Data;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor editor.
|
||||
*
|
||||
* Elementor editor handler class is responsible for initializing Elementor
|
||||
* editor and register all the actions needed to display the editor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Editor {
|
||||
|
||||
/**
|
||||
* User capability required to access Elementor editor.
|
||||
*/
|
||||
const EDITING_CAPABILITY = 'edit_posts';
|
||||
|
||||
const EDITOR_V2_EXPERIMENT_NAME = 'editor_v2';
|
||||
|
||||
/**
|
||||
* Post ID.
|
||||
*
|
||||
* Holds the ID of the current post being edited.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var int Post ID.
|
||||
*/
|
||||
private $post_id;
|
||||
|
||||
/**
|
||||
* Whether the edit mode is active.
|
||||
*
|
||||
* Used to determine whether we are in edit mode.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var bool Whether the edit mode is active.
|
||||
*/
|
||||
private $is_edit_mode;
|
||||
|
||||
/**
|
||||
* @var Notice_Bar
|
||||
*/
|
||||
public $notice_bar;
|
||||
|
||||
/**
|
||||
* @var Promotion
|
||||
*/
|
||||
public $promotion;
|
||||
|
||||
/**
|
||||
* @var Editor_Loader_Interface
|
||||
*/
|
||||
private $loader;
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* Initialize Elementor editor. Registers all needed actions to run Elementor,
|
||||
* removes conflicting actions etc.
|
||||
*
|
||||
* Fired by `admin_action_elementor` action.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param bool $die Optional. Whether to die at the end. Default is `true`.
|
||||
*/
|
||||
public function init( $die = true ) {
|
||||
if ( empty( $_REQUEST['post'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_post_id( absint( $_REQUEST['post'] ) );
|
||||
|
||||
if ( ! $this->is_edit_mode( $this->post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// BC: From 2.9.0, the editor shouldn't handle the global post / current document.
|
||||
// Use requested id and not the global in order to avoid conflicts with plugins that changes the global post.
|
||||
query_posts( [
|
||||
'p' => $this->post_id,
|
||||
'post_type' => get_post_type( $this->post_id ),
|
||||
] );
|
||||
|
||||
Plugin::$instance->db->switch_to_post( $this->post_id );
|
||||
|
||||
$document = Plugin::$instance->documents->get( $this->post_id );
|
||||
|
||||
Plugin::$instance->documents->switch_to_document( $document );
|
||||
|
||||
// Change mode to Builder
|
||||
$document->set_is_built_with_elementor( true );
|
||||
|
||||
// End BC.
|
||||
|
||||
Loading_Inspection_Manager::instance()->register_inspections();
|
||||
|
||||
// Send MIME Type header like WP admin-header.
|
||||
@header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
|
||||
|
||||
add_filter( 'show_admin_bar', '__return_false' );
|
||||
|
||||
// Remove all WordPress actions
|
||||
remove_all_actions( 'wp_head' );
|
||||
remove_all_actions( 'wp_print_styles' );
|
||||
remove_all_actions( 'wp_print_head_scripts' );
|
||||
remove_all_actions( 'wp_footer' );
|
||||
|
||||
// Handle `wp_head`
|
||||
add_action( 'wp_head', 'wp_enqueue_scripts', 1 );
|
||||
add_action( 'wp_head', 'wp_print_styles', 8 );
|
||||
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
|
||||
add_action( 'wp_head', 'wp_site_icon' );
|
||||
add_action( 'wp_head', [ $this, 'editor_head_trigger' ], 30 );
|
||||
|
||||
// Handle `wp_footer`
|
||||
add_action( 'wp_footer', 'wp_print_footer_scripts', 20 );
|
||||
add_action( 'wp_footer', 'wp_auth_check_html', 30 );
|
||||
add_action( 'wp_footer', [ $this, 'wp_footer' ] );
|
||||
|
||||
// Handle `wp_enqueue_scripts`
|
||||
remove_all_actions( 'wp_enqueue_scripts' );
|
||||
|
||||
// Also remove all scripts hooked into after_wp_tiny_mce.
|
||||
remove_all_actions( 'after_wp_tiny_mce' );
|
||||
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 999999 );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ], 999999 );
|
||||
|
||||
// Setup default heartbeat options
|
||||
add_filter( 'heartbeat_settings', function( $settings ) {
|
||||
$settings['interval'] = 15;
|
||||
return $settings;
|
||||
} );
|
||||
|
||||
// Tell to WP Cache plugins do not cache this request.
|
||||
Utils::do_not_cache();
|
||||
|
||||
do_action( 'elementor/editor/init' );
|
||||
|
||||
$this->get_loader()->print_root_template();
|
||||
|
||||
// From the action it's an empty string, from tests its `false`
|
||||
if ( false !== $die ) {
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve post ID.
|
||||
*
|
||||
* Get the ID of the current post.
|
||||
*
|
||||
* @since 1.8.0
|
||||
* @access public
|
||||
*
|
||||
* @return int Post ID.
|
||||
*/
|
||||
public function get_post_id() {
|
||||
return $this->post_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to new URL.
|
||||
*
|
||||
* Used as a fallback function for the old URL structure of Elementor page
|
||||
* edit URL.
|
||||
*
|
||||
* Fired by `template_redirect` action.
|
||||
*
|
||||
* @since 1.6.0
|
||||
* @access public
|
||||
*/
|
||||
public function redirect_to_new_url() {
|
||||
if ( ! isset( $_GET['elementor'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$document = Plugin::$instance->documents->get( get_the_ID() );
|
||||
|
||||
if ( ! $document ) {
|
||||
wp_die( esc_html__( 'Document not found.', 'elementor' ) );
|
||||
}
|
||||
|
||||
if ( ! $document->is_editable_by_current_user() || ! $document->is_built_with_elementor() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_safe_redirect( $document->get_edit_url() );
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the edit mode is active.
|
||||
*
|
||||
* Used to determine whether we are in the edit mode.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param int $post_id Optional. Post ID. Default is `null`, the current
|
||||
* post ID.
|
||||
*
|
||||
* @return bool Whether the edit mode is active.
|
||||
*/
|
||||
public function is_edit_mode( $post_id = null ) {
|
||||
if ( null !== $this->is_edit_mode ) {
|
||||
return $this->is_edit_mode;
|
||||
}
|
||||
|
||||
if ( empty( $post_id ) ) {
|
||||
$post_id = $this->post_id;
|
||||
}
|
||||
|
||||
$document = Plugin::$instance->documents->get( $post_id );
|
||||
|
||||
if ( ! $document || ! $document->is_editable_by_current_user() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var Module ajax */
|
||||
$ajax_data = Plugin::$instance->common->get_component( 'ajax' )->get_current_action_data();
|
||||
|
||||
if ( ! empty( $ajax_data ) && 'get_document_config' === $ajax_data['action'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ajax request as Editor mode
|
||||
$actions = [
|
||||
'elementor',
|
||||
|
||||
// Templates
|
||||
'elementor_get_templates',
|
||||
'elementor_save_template',
|
||||
'elementor_get_template',
|
||||
'elementor_delete_template',
|
||||
'elementor_import_template',
|
||||
'elementor_library_direct_actions',
|
||||
];
|
||||
|
||||
if ( isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], $actions ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock post.
|
||||
*
|
||||
* Mark the post as currently being edited by the current user.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param int $post_id The ID of the post being edited.
|
||||
*/
|
||||
public function lock_post( $post_id ) {
|
||||
if ( ! function_exists( 'wp_set_post_lock' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/post.php';
|
||||
}
|
||||
|
||||
wp_set_post_lock( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locked user.
|
||||
*
|
||||
* Check what user is currently editing the post.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param int $post_id The ID of the post being edited.
|
||||
*
|
||||
* @return \WP_User|false User information or false if the post is not locked.
|
||||
*/
|
||||
public function get_locked_user( $post_id ) {
|
||||
if ( ! function_exists( 'wp_check_post_lock' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/post.php';
|
||||
}
|
||||
|
||||
$locked_user = wp_check_post_lock( $post_id );
|
||||
if ( ! $locked_user ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get_user_by( 'id', $locked_user );
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTICE: This method not in use, it's here for backward compatibility.
|
||||
*
|
||||
* Print Editor Template.
|
||||
*
|
||||
* Include the wrapper template of the editor.
|
||||
*
|
||||
* @since 2.2.0
|
||||
* @access public
|
||||
*/
|
||||
public function print_editor_template() {
|
||||
include ELEMENTOR_PATH . 'includes/editor-templates/editor-wrapper.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts.
|
||||
*
|
||||
* Registers all the editor scripts and enqueues them.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
remove_action( 'wp_enqueue_scripts', [ $this, __FUNCTION__ ], 999999 );
|
||||
|
||||
global $wp_styles, $wp_scripts;
|
||||
|
||||
// Reset global variable
|
||||
$wp_styles = new \WP_Styles(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$wp_scripts = new \WP_Scripts(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
|
||||
$this->get_loader()->register_scripts();
|
||||
|
||||
/**
|
||||
* Before editor enqueue scripts.
|
||||
*
|
||||
* Fires before Elementor editor scripts are enqueued.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
do_action( 'elementor/editor/before_enqueue_scripts' );
|
||||
|
||||
// Tweak for WP Admin menu icons
|
||||
wp_print_styles( 'editor-buttons' );
|
||||
|
||||
$this->get_loader()->enqueue_scripts();
|
||||
|
||||
Plugin::$instance->controls_manager->enqueue_control_scripts();
|
||||
|
||||
/**
|
||||
* After editor enqueue scripts.
|
||||
*
|
||||
* Fires after Elementor editor scripts are enqueued.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
do_action( 'elementor/editor/after_enqueue_scripts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue styles.
|
||||
*
|
||||
* Registers all the editor styles and enqueues them.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
/**
|
||||
* Before editor enqueue styles.
|
||||
*
|
||||
* Fires before Elementor editor styles are enqueued.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
do_action( 'elementor/editor/before_enqueue_styles' );
|
||||
|
||||
$this->get_loader()->register_styles();
|
||||
$this->get_loader()->enqueue_styles();
|
||||
|
||||
$this->enqueue_theme_ui_styles();
|
||||
|
||||
$breakpoints = Plugin::$instance->breakpoints->get_breakpoints();
|
||||
|
||||
// The two breakpoints under 'tablet' need to be checked for values.
|
||||
if ( $breakpoints[ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE ]->is_custom() || $breakpoints[ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA ]->is_enabled() ) {
|
||||
wp_add_inline_style(
|
||||
'elementor-editor',
|
||||
'.elementor-device-tablet #elementor-preview-responsive-wrapper { width: ' . Plugin::$instance->breakpoints->get_device_min_breakpoint( Breakpoints_Manager::BREAKPOINT_KEY_TABLET ) . 'px; }'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* After editor enqueue styles.
|
||||
*
|
||||
* Fires after Elementor editor styles are enqueued.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
do_action( 'elementor/editor/after_enqueue_styles' );
|
||||
}
|
||||
|
||||
private function enqueue_theme_ui_styles() {
|
||||
$ui_theme_selected = SettingsManager::get_settings_managers( 'editorPreferences' )->get_model()->get_settings( 'ui_theme' );
|
||||
|
||||
$ui_themes = [
|
||||
'light',
|
||||
'dark',
|
||||
];
|
||||
|
||||
if ( 'auto' === $ui_theme_selected || ! in_array( $ui_theme_selected, $ui_themes, true ) ) {
|
||||
$ui_light_theme_media_queries = '(prefers-color-scheme: light)';
|
||||
$ui_dark_theme_media_queries = '(prefers-color-scheme: dark)';
|
||||
} else {
|
||||
$ui_light_theme_media_queries = 'none';
|
||||
$ui_dark_theme_media_queries = 'none';
|
||||
|
||||
if ( 'light' === $ui_theme_selected ) {
|
||||
$ui_light_theme_media_queries = 'all';
|
||||
} elseif ( 'dark' === $ui_theme_selected ) {
|
||||
$ui_dark_theme_media_queries = 'all';
|
||||
}
|
||||
}
|
||||
|
||||
$this->enqueue_theme_ui( 'light', $ui_light_theme_media_queries );
|
||||
$this->enqueue_theme_ui( 'dark', $ui_dark_theme_media_queries );
|
||||
}
|
||||
|
||||
private function enqueue_theme_ui( $ui_theme, $ui_theme_media_queries = 'all' ) {
|
||||
$suffix = Utils::is_script_debug() ? '' : '.min';
|
||||
|
||||
wp_enqueue_style(
|
||||
'e-theme-ui-' . $ui_theme,
|
||||
ELEMENTOR_ASSETS_URL . 'css/theme-' . $ui_theme . $suffix . '.css',
|
||||
[],
|
||||
ELEMENTOR_VERSION,
|
||||
$ui_theme_media_queries
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Editor head trigger.
|
||||
*
|
||||
* Fires the 'elementor/editor/wp_head' action in the head tag in Elementor
|
||||
* editor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function editor_head_trigger() {
|
||||
/**
|
||||
* Elementor editor head.
|
||||
*
|
||||
* Fires on Elementor editor head tag.
|
||||
*
|
||||
* Used to prints scripts or any other data in the head tag.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
do_action( 'elementor/editor/wp_head' );
|
||||
}
|
||||
|
||||
/**
|
||||
* WP footer.
|
||||
*
|
||||
* Prints Elementor editor with all the editor templates, and render controls,
|
||||
* widgets and content elements.
|
||||
*
|
||||
* Fired by `wp_footer` action.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function wp_footer() {
|
||||
$plugin = Plugin::$instance;
|
||||
|
||||
$plugin->controls_manager->render_controls();
|
||||
$plugin->widgets_manager->render_widgets_content();
|
||||
$plugin->elements_manager->render_elements_content();
|
||||
|
||||
$plugin->dynamic_tags->print_templates();
|
||||
|
||||
$this->get_loader()->register_additional_templates();
|
||||
|
||||
/**
|
||||
* Elementor editor footer.
|
||||
*
|
||||
* Fires on Elementor editor before closing the body tag.
|
||||
*
|
||||
* Used to prints scripts or any other HTML before closing the body tag.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
do_action( 'elementor/editor/footer' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set edit mode.
|
||||
*
|
||||
* Used to update the edit mode.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param bool $edit_mode Whether the edit mode is active.
|
||||
*/
|
||||
public function set_edit_mode( $edit_mode ) {
|
||||
$this->is_edit_mode = $edit_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Editor constructor.
|
||||
*
|
||||
* Initializing Elementor editor and redirect from old URL structure of
|
||||
* Elementor editor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
Plugin::$instance->data_manager_v2->register_controller( new Data\Globals\Controller() );
|
||||
|
||||
$this->notice_bar = new Notice_Bar();
|
||||
$this->promotion = new Promotion();
|
||||
|
||||
add_action( 'admin_action_elementor', [ $this, 'init' ] );
|
||||
add_action( 'template_redirect', [ $this, 'redirect_to_new_url' ] );
|
||||
|
||||
$this->register_editor_v2_experiment();
|
||||
|
||||
// Handle autocomplete feature for URL control.
|
||||
add_filter( 'wp_link_query_args', [ $this, 'filter_wp_link_query_args' ] );
|
||||
add_filter( 'wp_link_query', [ $this, 'filter_wp_link_query' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.2.0
|
||||
* @access public
|
||||
*/
|
||||
public function filter_wp_link_query_args( $query ) {
|
||||
$library_cpt_key = array_search( Source_Local::CPT, $query['post_type'], true );
|
||||
if ( false !== $library_cpt_key ) {
|
||||
unset( $query['post_type'][ $library_cpt_key ] );
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.2.0
|
||||
* @access public
|
||||
*/
|
||||
public function filter_wp_link_query( $results ) {
|
||||
|
||||
// PHPCS - The user data is not used.
|
||||
if ( isset( $_POST['editor'] ) && 'elementor' === $_POST['editor'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$post_type_object = get_post_type_object( 'post' );
|
||||
$post_label = $post_type_object->labels->singular_name;
|
||||
|
||||
foreach ( $results as & $result ) {
|
||||
if ( 'post' === get_post_type( $result['ID'] ) ) {
|
||||
$result['info'] = $post_label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function set_post_id( $post_id ) {
|
||||
$this->post_id = $post_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loader.
|
||||
*
|
||||
* @return Editor_Loader_Interface
|
||||
*/
|
||||
private function get_loader() {
|
||||
if ( ! $this->loader ) {
|
||||
$this->loader = Editor_Loader_Factory::create();
|
||||
|
||||
$this->loader->init();
|
||||
}
|
||||
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding Editor V2 experiment.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function register_editor_v2_experiment() {
|
||||
Plugin::$instance->experiments->add_feature( [
|
||||
'name' => static::EDITOR_V2_EXPERIMENT_NAME,
|
||||
'title' => esc_html__( 'Editor Top Bar', 'elementor' ),
|
||||
'description' => sprintf(
|
||||
'%1$s <a href="https://go.elementor.com/wp-dash-elementor-top-bar/" target="_blank">%2$s</a>',
|
||||
esc_html__( 'Get a sneak peek of the new Editor powered by React. The beautiful design and experimental layout of the Top bar are just some of the exciting tools on their way.', 'elementor' ),
|
||||
esc_html__( 'Learn more', 'elementor' )
|
||||
),
|
||||
'default' => Experiments_Manager::STATE_INACTIVE,
|
||||
'release_status' => Experiments_Manager::RELEASE_STATUS_BETA,
|
||||
'new_site' => [
|
||||
'default_active' => true,
|
||||
'minimum_installation_version' => '3.23.0',
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get elements presets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_elements_presets() {
|
||||
$element_types = Plugin::$instance->elements_manager->get_element_types();
|
||||
$presets = [];
|
||||
|
||||
foreach ( $element_types as $el_type => $element ) {
|
||||
$this->check_element_for_presets( $element, $el_type, $presets );
|
||||
}
|
||||
|
||||
return $presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function check_element_for_presets( $element, $el_type, &$presets ) {
|
||||
$element_presets = $element->get_panel_presets();
|
||||
|
||||
if ( empty( $element_presets ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $element_presets as $key => $preset ) {
|
||||
$this->maybe_add_preset( $el_type, $preset, $key, $presets );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function maybe_add_preset( $el_type, $preset, $key, &$presets ) {
|
||||
if ( $this->is_valid_preset( $el_type, $preset ) ) {
|
||||
$presets[ $key ] = $preset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_valid_preset( $el_type, $preset ) {
|
||||
return isset( $preset['replacements']['custom']['originalWidget'] )
|
||||
&& $el_type === $preset['replacements']['custom']['originalWidget'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader\Common;
|
||||
|
||||
use Elementor\Api;
|
||||
use Elementor\Core\Debug\Loading_Inspection_Manager;
|
||||
use Elementor\Core\Settings\Manager as SettingsManager;
|
||||
use Elementor\Icons_Manager;
|
||||
use Elementor\Modules\Apps\Module as AppsModule;
|
||||
use Elementor\Modules\EditorEvents\Module as EditorEventsModule;
|
||||
use Elementor\Modules\Home\Module as Home_Module;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Settings;
|
||||
use Elementor\Shapes;
|
||||
use Elementor\Tools;
|
||||
use Elementor\User;
|
||||
use Elementor\Utils;
|
||||
use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Editor_Common_Scripts_Settings {
|
||||
public static function get() {
|
||||
$settings = SettingsManager::get_settings_managers_config();
|
||||
// Moved to document since 2.9.0.
|
||||
unset( $settings['page'] );
|
||||
|
||||
$document = Plugin::$instance->documents->get_doc_or_auto_save( Plugin::$instance->editor->get_post_id() );
|
||||
$kits_manager = Plugin::$instance->kits_manager;
|
||||
|
||||
$page_title_selector = $kits_manager->get_current_settings( 'page_title_selector' );
|
||||
|
||||
$page_title_selector .= ', .elementor-page-title .elementor-heading-title';
|
||||
|
||||
$client_env = [
|
||||
'initial_document' => $document->get_config(),
|
||||
'version' => ELEMENTOR_VERSION,
|
||||
'home_url' => home_url(),
|
||||
'admin_settings_url' => admin_url( 'admin.php?page=' . Home_Module::get_elementor_settings_page_id() ),
|
||||
'admin_tools_url' => admin_url( 'admin.php?page=' . Tools::PAGE_ID ),
|
||||
'admin_apps_url' => admin_url( 'admin.php?page=' . AppsModule::PAGE_ID ),
|
||||
'autosave_interval' => AUTOSAVE_INTERVAL,
|
||||
'tabs' => Plugin::$instance->controls_manager->get_tabs(),
|
||||
'controls' => Plugin::$instance->controls_manager->get_controls_data(),
|
||||
'elements' => Plugin::$instance->elements_manager->get_element_types_config(),
|
||||
'globals' => [
|
||||
'defaults_enabled' => [
|
||||
'colors' => $kits_manager->is_custom_colors_enabled(),
|
||||
'typography' => $kits_manager->is_custom_typography_enabled(),
|
||||
],
|
||||
],
|
||||
'icons' => [
|
||||
'libraries' => Icons_Manager::get_icon_manager_tabs_config(),
|
||||
'goProURL' => 'https://go.elementor.com/go-pro-icon-library/',
|
||||
],
|
||||
'fa4_to_fa5_mapping_url' => ELEMENTOR_ASSETS_URL . 'lib/font-awesome/migration/mapping.js',
|
||||
'settings' => $settings,
|
||||
'wp_editor' => static::get_wp_editor_config(),
|
||||
'settings_page_link' => Settings::get_url(),
|
||||
'tools_page_link' => Tools::get_url(),
|
||||
'tools_page_nonce' => wp_create_nonce( 'tools-page-from-editor' ),
|
||||
'elementor_site' => 'https://go.elementor.com/about-elementor/',
|
||||
'docs_elementor_site' => 'https://go.elementor.com/docs/',
|
||||
'help_the_content_url' => 'https://go.elementor.com/the-content-missing/',
|
||||
'help_flexbox_bc_url' => 'https://go.elementor.com/flexbox-layout-bc/',
|
||||
'elementPromotionURL' => 'https://go.elementor.com/go-pro-%s',
|
||||
'dynamicPromotionURL' => 'https://go.elementor.com/go-pro-dynamic-tag',
|
||||
'additional_shapes' => Shapes::get_additional_shapes_for_config(),
|
||||
'user' => [
|
||||
'restrictions' => Plugin::$instance->role_manager->get_user_restrictions_array(),
|
||||
'is_administrator' => current_user_can( 'manage_options' ),
|
||||
'introduction' => User::get_introduction_meta(),
|
||||
'dismissed_editor_notices' => User::get_dismissed_editor_notices(),
|
||||
'locale' => get_user_locale(),
|
||||
],
|
||||
'preview' => [
|
||||
'help_preview_error_url' => 'https://go.elementor.com/preview-not-loaded/',
|
||||
'help_preview_http_error_url' => 'https://go.elementor.com/preview-not-loaded/#permissions',
|
||||
'help_preview_http_error_500_url' => 'https://go.elementor.com/500-error/',
|
||||
'debug_data' => Loading_Inspection_Manager::instance()->run_inspections(),
|
||||
],
|
||||
'locale' => get_locale(),
|
||||
'rich_editing_enabled' => filter_var( get_user_meta( get_current_user_id(), 'rich_editing', true ), FILTER_VALIDATE_BOOLEAN ),
|
||||
'page_title_selector' => $page_title_selector,
|
||||
'tinymceHasCustomConfig' => class_exists( 'Tinymce_Advanced' ) || class_exists( 'Advanced_Editor_Tools' ),
|
||||
'inlineEditing' => Plugin::$instance->widgets_manager->get_inline_editing_config(),
|
||||
'dynamicTags' => Plugin::$instance->dynamic_tags->get_config(),
|
||||
'ui' => [
|
||||
'defaultGenericFonts' => $kits_manager->get_current_settings( 'default_generic_fonts' ),
|
||||
],
|
||||
// Empty array for BC to avoid errors.
|
||||
'i18n' => [],
|
||||
// 'responsive' contains the custom breakpoints config introduced in Elementor v3.2.0
|
||||
'responsive' => [
|
||||
'breakpoints' => Plugin::$instance->breakpoints->get_breakpoints_config(),
|
||||
'icons_map' => Plugin::$instance->breakpoints->get_responsive_icons_classes_map(),
|
||||
],
|
||||
'promotion' => [
|
||||
'elements' => Plugin::$instance->editor->promotion->get_elements_promotion(),
|
||||
],
|
||||
'editor_events' => EditorEventsModule::get_editor_events_config(),
|
||||
'promotions' => [
|
||||
'notes' => Filtered_Promotions_Manager::get_filtered_promotion_data(
|
||||
[ 'upgrade_url' => 'https://go.elementor.com/go-pro-notes/' ],
|
||||
'elementor/panel/notes/custom_promotion',
|
||||
'upgrade_url'
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
if ( ! Utils::has_pro() && current_user_can( 'manage_options' ) ) {
|
||||
$client_env['promotionWidgets'] = Api::get_promotion_widgets();
|
||||
}
|
||||
|
||||
if ( Plugin::$instance->experiments->is_feature_active( 'container_grid' ) ) {
|
||||
$client_env['elementsPresets'] = Plugin::$instance->editor->get_elements_presets();
|
||||
}
|
||||
|
||||
static::bc_move_document_filters();
|
||||
|
||||
/**
|
||||
* Localize editor settings.
|
||||
*
|
||||
* Filters the editor localized settings.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $client_env Editor configuration.
|
||||
* @param int $post_id The ID of the current post being edited.
|
||||
*/
|
||||
return apply_filters( 'elementor/editor/localize_settings', $client_env );
|
||||
}
|
||||
|
||||
private static function bc_move_document_filters() {
|
||||
global $wp_filter;
|
||||
|
||||
$old_tag = 'elementor/editor/localize_settings';
|
||||
$new_tag = 'elementor/document/config';
|
||||
|
||||
if ( ! has_filter( $old_tag ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $wp_filter[ $old_tag ] as $priority => $filters ) {
|
||||
foreach ( $filters as $filter_id => $filter_args ) {
|
||||
if ( 2 === $filter_args['accepted_args'] ) {
|
||||
remove_filter( $old_tag, $filter_id, $priority );
|
||||
|
||||
add_filter( $new_tag, $filter_args['function'], $priority, 2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get WordPress editor config.
|
||||
*
|
||||
* Config the default WordPress editor with custom settings for Elementor use.
|
||||
*
|
||||
* @since 1.9.0
|
||||
* @access private
|
||||
*/
|
||||
private static function get_wp_editor_config() {
|
||||
// Remove all TinyMCE plugins.
|
||||
remove_all_filters( 'mce_buttons', 10 );
|
||||
remove_all_filters( 'mce_external_plugins', 10 );
|
||||
|
||||
if ( ! class_exists( '\_WP_Editors', false ) ) {
|
||||
require ABSPATH . WPINC . '/class-wp-editor.php';
|
||||
}
|
||||
|
||||
// WordPress 4.8 and higher
|
||||
if ( method_exists( '\_WP_Editors', 'print_tinymce_scripts' ) ) {
|
||||
\_WP_Editors::print_default_editor_scripts();
|
||||
\_WP_Editors::print_tinymce_scripts();
|
||||
}
|
||||
ob_start();
|
||||
|
||||
wp_editor(
|
||||
'%%EDITORCONTENT%%',
|
||||
'elementorwpeditor',
|
||||
[
|
||||
'editor_class' => 'elementor-wp-editor',
|
||||
'editor_height' => 250,
|
||||
'drag_drop_upload' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$config = ob_get_clean();
|
||||
|
||||
// Don't call \_WP_Editors methods again
|
||||
remove_action( 'admin_print_footer_scripts', [ '_WP_Editors', 'editor_js' ], 50 );
|
||||
remove_action( 'admin_print_footer_scripts', [ '_WP_Editors', 'print_default_editor_scripts' ], 45 );
|
||||
|
||||
\_WP_Editors::editor_js();
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader;
|
||||
|
||||
use Elementor\Core\Utils\Assets_Config_Provider;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Editor_Base_Loader implements Editor_Loader_Interface {
|
||||
/**
|
||||
* @var Collection
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var Assets_Config_Provider
|
||||
*/
|
||||
protected $assets_config_provider;
|
||||
|
||||
/**
|
||||
* @param Collection $config
|
||||
* @param Assets_Config_Provider $assets_config_provider\
|
||||
*/
|
||||
public function __construct( Collection $config, Assets_Config_Provider $assets_config_provider ) {
|
||||
$this->config = $config;
|
||||
$this->assets_config_provider = $assets_config_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_scripts() {
|
||||
$assets_url = $this->config->get( 'assets_url' );
|
||||
$min_suffix = $this->config->get( 'min_suffix' );
|
||||
|
||||
wp_register_script(
|
||||
'elementor-editor-modules',
|
||||
"{$assets_url}js/editor-modules{$min_suffix}.js",
|
||||
[ 'elementor-common-modules' ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'elementor-editor-document',
|
||||
"{$assets_url}js/editor-document{$min_suffix}.js",
|
||||
[ 'elementor-common-modules' ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Hack for waypoint with editor mode.
|
||||
wp_register_script(
|
||||
'elementor-waypoints',
|
||||
"{$assets_url}lib/waypoints/waypoints-for-editor.js",
|
||||
[ 'jquery' ],
|
||||
'4.0.2',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'perfect-scrollbar',
|
||||
"{$assets_url}lib/perfect-scrollbar/js/perfect-scrollbar{$min_suffix}.js",
|
||||
[],
|
||||
'1.4.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'jquery-easing',
|
||||
"{$assets_url}lib/jquery-easing/jquery-easing{$min_suffix}.js",
|
||||
[ 'jquery' ],
|
||||
'1.3.2',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'nprogress',
|
||||
"{$assets_url}lib/nprogress/nprogress{$min_suffix}.js",
|
||||
[],
|
||||
'0.2.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'tipsy',
|
||||
"{$assets_url}lib/tipsy/tipsy{$min_suffix}.js",
|
||||
[ 'jquery' ],
|
||||
'1.0.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'jquery-elementor-select2',
|
||||
"{$assets_url}lib/e-select2/js/e-select2.full{$min_suffix}.js",
|
||||
[ 'jquery' ],
|
||||
'4.0.6-rc.1',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'flatpickr',
|
||||
"{$assets_url}lib/flatpickr/flatpickr{$min_suffix}.js",
|
||||
[ 'jquery' ],
|
||||
'1.12.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'ace',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ace.js',
|
||||
[],
|
||||
'1.2.5',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'ace-language-tools',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ext-language_tools.js',
|
||||
[ 'ace' ],
|
||||
'1.2.5',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'jquery-hover-intent',
|
||||
"{$assets_url}lib/jquery-hover-intent/jquery-hover-intent{$min_suffix}.js",
|
||||
[],
|
||||
'1.0.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'nouislider',
|
||||
"{$assets_url}lib/nouislider/nouislider{$min_suffix}.js",
|
||||
[],
|
||||
'13.0.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'pickr',
|
||||
"{$assets_url}lib/pickr/pickr.min.js",
|
||||
[],
|
||||
'1.5.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'elementor-editor',
|
||||
"{$assets_url}js/editor{$min_suffix}.js",
|
||||
[
|
||||
'elementor-common',
|
||||
'elementor-editor-modules',
|
||||
'elementor-editor-document',
|
||||
'wp-auth-check',
|
||||
'jquery-ui-sortable',
|
||||
'jquery-ui-resizable',
|
||||
'perfect-scrollbar',
|
||||
'nprogress',
|
||||
'tipsy',
|
||||
'imagesloaded',
|
||||
'heartbeat',
|
||||
'jquery-elementor-select2',
|
||||
'flatpickr',
|
||||
'ace',
|
||||
'ace-language-tools',
|
||||
'jquery-hover-intent',
|
||||
'nouislider',
|
||||
'pickr',
|
||||
'react',
|
||||
'react-dom',
|
||||
],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_set_script_translations( 'elementor-editor', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_styles() {
|
||||
$assets_url = $this->config->get( 'assets_url' );
|
||||
$min_suffix = $this->config->get( 'min_suffix' );
|
||||
$direction_suffix = $this->config->get( 'direction_suffix' );
|
||||
|
||||
wp_register_style(
|
||||
'font-awesome',
|
||||
"{$assets_url}lib/font-awesome/css/font-awesome{$min_suffix}.css",
|
||||
[],
|
||||
'4.7.0'
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
'elementor-select2',
|
||||
"{$assets_url}lib/e-select2/css/e-select2{$min_suffix}.css",
|
||||
[],
|
||||
'4.0.6-rc.1'
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
'google-font-roboto',
|
||||
'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700',
|
||||
[],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
'flatpickr',
|
||||
"{$assets_url}lib/flatpickr/flatpickr{$min_suffix}.css",
|
||||
[],
|
||||
'1.12.0'
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
'pickr',
|
||||
"{$assets_url}lib/pickr/themes/monolith.min.css",
|
||||
[],
|
||||
'1.5.0'
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
'elementor-editor',
|
||||
"{$assets_url}css/editor{$direction_suffix}{$min_suffix}.css",
|
||||
[
|
||||
'elementor-common',
|
||||
'elementor-select2',
|
||||
'elementor-icons',
|
||||
'wp-auth-check',
|
||||
'google-font-roboto',
|
||||
'flatpickr',
|
||||
'pickr',
|
||||
],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
wp_enqueue_style( 'elementor-editor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_additional_templates() {
|
||||
$templates = [
|
||||
'global',
|
||||
'panel',
|
||||
'panel-elements',
|
||||
'repeater',
|
||||
'templates',
|
||||
'navigator',
|
||||
'hotkeys',
|
||||
];
|
||||
|
||||
foreach ( $templates as $template ) {
|
||||
Plugin::$instance->common->add_template( ELEMENTOR_PATH . "includes/editor-templates/{$template}.php" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader;
|
||||
|
||||
use Elementor\Core\Editor\Editor;
|
||||
use Elementor\Core\Editor\Loader\V1\Editor_V1_Loader;
|
||||
use Elementor\Core\Editor\Loader\V2\Editor_V2_Loader;
|
||||
use Elementor\Core\Utils\Assets_Config_Provider;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Editor_Loader_Factory {
|
||||
/**
|
||||
* @return Editor_Loader_Interface
|
||||
*/
|
||||
public static function create() {
|
||||
$is_editor_v2_active = Plugin::$instance->experiments->is_feature_active( Editor::EDITOR_V2_EXPERIMENT_NAME );
|
||||
|
||||
// Nonce verification is not required, using param for routing purposes.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$editor_version = Utils::get_super_global_value( $_GET, 'v' ) ?? ( $is_editor_v2_active ? '2' : '1' );
|
||||
|
||||
$config = new Collection( [
|
||||
'assets_url' => ELEMENTOR_ASSETS_URL,
|
||||
'min_suffix' => ( Utils::is_script_debug() || Utils::is_elementor_tests() ) ? '' : '.min',
|
||||
'direction_suffix' => is_rtl() ? '-rtl' : '',
|
||||
] );
|
||||
|
||||
$assets_config_provider = ( new Assets_Config_Provider() )
|
||||
->set_path_resolver( function ( $name ) {
|
||||
return ELEMENTOR_ASSETS_PATH . "js/packages/{$name}/{$name}.asset.php";
|
||||
} );
|
||||
|
||||
if ( '2' === $editor_version ) {
|
||||
return new Editor_V2_Loader( $config, $assets_config_provider );
|
||||
}
|
||||
|
||||
return new Editor_V1_Loader( $config, $assets_config_provider );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
interface Editor_Loader_Interface {
|
||||
/**
|
||||
* Init function purpose is to prepare some stuff that should be available for other methods
|
||||
* and register some hooks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init();
|
||||
|
||||
/**
|
||||
* Register all the scripts for the editor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_scripts();
|
||||
|
||||
/**
|
||||
* Enqueue all the scripts for the editor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts();
|
||||
|
||||
/**
|
||||
* Register all the styles for the editor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_styles();
|
||||
|
||||
/**
|
||||
* Enqueue all the styles for the editor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_styles();
|
||||
|
||||
/**
|
||||
* Print the actual initial html for the editor, later on, the scripts takeover and renders the JS apps.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function print_root_template();
|
||||
|
||||
/**
|
||||
* Register additional templates that are required for the marionette part of the application
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_additional_templates();
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader\V1;
|
||||
|
||||
use Elementor\Core\Editor\Loader\Common\Editor_Common_Scripts_Settings;
|
||||
use Elementor\Core\Editor\Loader\Editor_Base_Loader;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Editor_V1_Loader extends Editor_Base_Loader {
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Loading UI and Icons v2 scrips for the use of new features that should live in V1.
|
||||
$packages_to_register = [ 'ui', 'icons', 'query' ];
|
||||
|
||||
foreach ( $packages_to_register as $package ) {
|
||||
$this->assets_config_provider->load( $package );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_scripts() {
|
||||
parent::register_scripts();
|
||||
|
||||
$assets_url = $this->config->get( 'assets_url' );
|
||||
$min_suffix = $this->config->get( 'min_suffix' );
|
||||
|
||||
foreach ( $this->assets_config_provider->all() as $package => $config ) {
|
||||
wp_register_script(
|
||||
$config['handle'],
|
||||
"{$assets_url}js/packages/{$package}/{$package}{$min_suffix}.js",
|
||||
$config['deps'],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
wp_register_script(
|
||||
'elementor-responsive-bar',
|
||||
"{$assets_url}js/responsive-bar{$min_suffix}.js",
|
||||
[ 'elementor-editor' ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_set_script_translations( 'elementor-responsive-bar', 'elementor' );
|
||||
|
||||
wp_register_script(
|
||||
'elementor-editor-loader-v1',
|
||||
"{$assets_url}js/editor-loader-v1{$min_suffix}.js",
|
||||
[ 'elementor-editor' ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_script( 'elementor-responsive-bar' );
|
||||
|
||||
// Must be last.
|
||||
wp_enqueue_script( 'elementor-editor-loader-v1' );
|
||||
|
||||
Utils::print_js_config(
|
||||
'elementor-editor',
|
||||
'ElementorConfig',
|
||||
Editor_Common_Scripts_Settings::get()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_styles() {
|
||||
parent::register_styles();
|
||||
|
||||
$assets_url = $this->config->get( 'assets_url' );
|
||||
$min_suffix = $this->config->get( 'min_suffix' );
|
||||
|
||||
wp_register_style(
|
||||
'elementor-responsive-bar',
|
||||
"{$assets_url}css/responsive-bar{$min_suffix}.css",
|
||||
[],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
public function enqueue_styles() {
|
||||
parent::enqueue_styles();
|
||||
|
||||
wp_enqueue_style( 'elementor-responsive-bar' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function print_root_template() {
|
||||
// Exposing the path for the view part to render the body of the editor template.
|
||||
$body_file_path = __DIR__ . '/templates/editor-body-v1.view.php';
|
||||
|
||||
include ELEMENTOR_PATH . 'includes/editor-templates/editor-wrapper.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_additional_templates() {
|
||||
parent::register_additional_templates();
|
||||
|
||||
Plugin::$instance->common->add_template( ELEMENTOR_PATH . 'includes/editor-templates/responsive-bar.php' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
window.elementor.start();
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader\V1\Templates;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
$notice = Plugin::$instance->editor->notice_bar->get_notice();
|
||||
?>
|
||||
|
||||
<div id="elementor-loading">
|
||||
<div class="elementor-loader-wrapper">
|
||||
<div class="elementor-loader" aria-hidden="true">
|
||||
<div class="elementor-loader-boxes">
|
||||
<div class="elementor-loader-box"></div>
|
||||
<div class="elementor-loader-box"></div>
|
||||
<div class="elementor-loader-box"></div>
|
||||
<div class="elementor-loader-box"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="elementor-loading-title"><?php echo esc_html__( 'Loading', 'elementor' ); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="elementor-screen-only"><?php echo sprintf( esc_html__( 'Edit "%s" with Elementor', 'elementor' ), esc_html( get_the_title() ) ); ?></h1>
|
||||
|
||||
<div id="elementor-editor-wrapper">
|
||||
<aside id="elementor-panel" class="elementor-panel" aria-labelledby="elementor-panel-header-title"></aside>
|
||||
<main id="elementor-preview" aria-label="<?php echo esc_attr__( 'Preview', 'elementor' ); ?>">
|
||||
<div id="elementor-responsive-bar"></div>
|
||||
<div id="elementor-preview-responsive-wrapper" class="elementor-device-desktop elementor-device-rotate-portrait">
|
||||
<div id="elementor-preview-loading">
|
||||
<i class="eicon-loading eicon-animation-spin" aria-hidden="true"></i>
|
||||
</div>
|
||||
<?php if ( $notice ) {
|
||||
$notice->render();
|
||||
} // IFrame will be created here by the Javascript later. ?>
|
||||
</div>
|
||||
</main>
|
||||
<aside id="elementor-navigator" aria-labelledby="elementor-navigator__header__title"></aside>
|
||||
</div>
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader\V2;
|
||||
|
||||
use Elementor\Core\Editor\Loader\Common\Editor_Common_Scripts_Settings;
|
||||
use Elementor\Core\Editor\Loader\Editor_Base_Loader;
|
||||
use Elementor\Core\Utils\Assets_Translation_Loader;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Editor_V2_Loader extends Editor_Base_Loader {
|
||||
const APP_PACKAGE = 'editor';
|
||||
const ENV_PACKAGE = 'env';
|
||||
|
||||
/**
|
||||
* Packages that should be enqueued (the main app and the extensions of the app).
|
||||
*/
|
||||
const PACKAGES_TO_ENQUEUE = [
|
||||
// App
|
||||
self::APP_PACKAGE,
|
||||
|
||||
// Extensions
|
||||
'editor-app-bar',
|
||||
'editor-documents',
|
||||
'editor-panels',
|
||||
'editor-responsive',
|
||||
'editor-site-navigation',
|
||||
'editor-v1-adapters',
|
||||
];
|
||||
|
||||
/**
|
||||
* Packages that should only be registered, unless some other asset depends on them.
|
||||
*/
|
||||
const LIBS = [
|
||||
self::ENV_PACKAGE,
|
||||
'editor-app-bar-ui',
|
||||
'icons',
|
||||
'locations',
|
||||
'query',
|
||||
'store',
|
||||
'ui',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
$packages = array_merge( $this->get_packages_to_enqueue(), self::LIBS );
|
||||
|
||||
foreach ( $packages as $package ) {
|
||||
$this->assets_config_provider->load( $package );
|
||||
}
|
||||
|
||||
do_action( 'elementor/editor/v2/init' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_scripts() {
|
||||
parent::register_scripts();
|
||||
|
||||
$assets_url = $this->config->get( 'assets_url' );
|
||||
$min_suffix = $this->config->get( 'min_suffix' );
|
||||
|
||||
foreach ( $this->assets_config_provider->all() as $package => $config ) {
|
||||
if ( self::ENV_PACKAGE === $package ) {
|
||||
wp_register_script(
|
||||
'elementor-editor-environment-v2',
|
||||
"{$assets_url}js/editor-environment-v2{$min_suffix}.js",
|
||||
[ $config['handle'] ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ( static::APP_PACKAGE === $package ) {
|
||||
wp_register_script(
|
||||
'elementor-editor-loader-v2',
|
||||
"{$assets_url}js/editor-loader-v2{$min_suffix}.js",
|
||||
[ 'elementor-editor', $config['handle'] ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
wp_register_script(
|
||||
$config['handle'],
|
||||
"{$assets_url}js/packages/{$package}/{$package}{$min_suffix}.js",
|
||||
$config['deps'],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$packages_handles = $this->assets_config_provider->pluck( 'handle' )->all();
|
||||
|
||||
Assets_Translation_Loader::for_handles( $packages_handles, 'elementor' );
|
||||
|
||||
do_action( 'elementor/editor/v2/scripts/register' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
do_action( 'elementor/editor/v2/scripts/enqueue/before' );
|
||||
|
||||
wp_enqueue_script( 'elementor-editor-environment-v2' );
|
||||
|
||||
$env_config = $this->assets_config_provider->get( self::ENV_PACKAGE );
|
||||
|
||||
if ( $env_config ) {
|
||||
$client_env = apply_filters( 'elementor/editor/v2/scripts/env', [] );
|
||||
|
||||
Utils::print_js_config(
|
||||
$env_config['handle'],
|
||||
'elementorEditorV2Env',
|
||||
$client_env
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $this->assets_config_provider->only( $this->get_packages_to_enqueue() ) as $config ) {
|
||||
wp_enqueue_script( $config['handle'] );
|
||||
}
|
||||
|
||||
do_action( 'elementor/editor/v2/scripts/enqueue' );
|
||||
|
||||
Utils::print_js_config(
|
||||
'elementor-editor',
|
||||
'ElementorConfig',
|
||||
Editor_Common_Scripts_Settings::get()
|
||||
);
|
||||
|
||||
// Must be last.
|
||||
wp_enqueue_script( 'elementor-editor-loader-v2' );
|
||||
|
||||
do_action( 'elementor/editor/v2/scripts/enqueue/after' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_styles() {
|
||||
parent::register_styles();
|
||||
|
||||
$assets_url = $this->config->get( 'assets_url' );
|
||||
$min_suffix = $this->config->get( 'min_suffix' );
|
||||
|
||||
wp_register_style(
|
||||
'elementor-editor-v2-overrides',
|
||||
"{$assets_url}css/editor-v2-overrides{$min_suffix}.css",
|
||||
[ 'elementor-editor' ],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
|
||||
do_action( 'elementor/editor/v2/styles/register' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
parent::enqueue_styles();
|
||||
|
||||
wp_enqueue_style( 'elementor-editor-v2-overrides' );
|
||||
|
||||
do_action( 'elementor/editor/v2/styles/enqueue' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function print_root_template() {
|
||||
// Exposing the path for the view part to render the body of the editor template.
|
||||
$body_file_path = __DIR__ . '/templates/editor-body-v2.view.php';
|
||||
|
||||
include ELEMENTOR_PATH . 'includes/editor-templates/editor-wrapper.php';
|
||||
}
|
||||
|
||||
private function get_packages_to_enqueue() : array {
|
||||
return apply_filters( 'elementor/editor/v2/packages', self::PACKAGES_TO_ENQUEUE );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
if ( ! window.elementorV2?.env ) {
|
||||
throw new Error( 'The "@elementor/env" package was not loaded.' );
|
||||
}
|
||||
|
||||
window.elementorV2.env.initEnv( window.elementorEditorV2Env );
|
||||
@@ -0,0 +1,17 @@
|
||||
window.__elementorEditorV1LoadingPromise = new Promise( ( resolve ) => {
|
||||
window.addEventListener( 'elementor/init', () => {
|
||||
resolve();
|
||||
}, { once: true } );
|
||||
} );
|
||||
|
||||
window.elementor.start();
|
||||
|
||||
if ( ! window.elementorV2?.editor ) {
|
||||
throw new Error( 'The "@elementor/editor" package was not loaded.' );
|
||||
}
|
||||
|
||||
window.elementorV2
|
||||
.editor
|
||||
.init(
|
||||
document.getElementById( 'elementor-editor-wrapper-v2' ),
|
||||
);
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Here should be only styles that related to the Editor v1, and should be overridden when using the Editor v2.
|
||||
*/
|
||||
|
||||
body {
|
||||
--editor-v2-top-bar-height: 48px;
|
||||
}
|
||||
|
||||
#elementor-editor-wrapper {
|
||||
height: calc(100vh - var(--editor-v2-top-bar-height));
|
||||
}
|
||||
|
||||
body.elementor-navigator-docked #elementor-navigator {
|
||||
height: calc(100% - var(--editor-v2-top-bar-height));
|
||||
top: var(--editor-v2-top-bar-height);
|
||||
}
|
||||
|
||||
.elementor-panel #elementor-panel-header-menu-button,
|
||||
.elementor-panel #elementor-panel-header-add-button,
|
||||
.elementor-panel #elementor-panel-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.elementor-panel #elementor-panel-header {
|
||||
font-weight: 700;
|
||||
background-color: var( --e-a-bg-default );
|
||||
color: var( --e-a-color-txt-accent );
|
||||
border-block-end: var( --e-a-border );
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
// Make the MCE full-screen work properly with the top bar.
|
||||
.elementor-control-type-wysiwyg .mce-fullscreen {
|
||||
inset: var(--editor-v2-top-bar-height) 0 0 0;
|
||||
|
||||
& > .mce-container-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
& > .mce-edit-area {
|
||||
flex-grow: 1;
|
||||
|
||||
& > iframe {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor\Loader\V2\Templates;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
$notice = Plugin::$instance->editor->notice_bar->get_notice();
|
||||
?>
|
||||
|
||||
<div id="elementor-loading">
|
||||
<div class="elementor-loader-wrapper">
|
||||
<div class="elementor-loader" aria-hidden="true">
|
||||
<div class="elementor-loader-boxes">
|
||||
<div class="elementor-loader-box"></div>
|
||||
<div class="elementor-loader-box"></div>
|
||||
<div class="elementor-loader-box"></div>
|
||||
<div class="elementor-loader-box"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="elementor-loading-title"><?php echo esc_html__( 'Loading', 'elementor' ); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="elementor-screen-only"><?php echo sprintf( esc_html__( 'Edit "%s" with Elementor', 'elementor' ), esc_html( get_the_title() ) ); ?></h1>
|
||||
|
||||
<div id="elementor-editor-wrapper-v2"></div>
|
||||
|
||||
<div id="elementor-editor-wrapper">
|
||||
<aside id="elementor-panel" class="elementor-panel" aria-labelledby="elementor-panel-header-title"></aside>
|
||||
<main id="elementor-preview" aria-label="<?php echo esc_attr__( 'Preview', 'elementor' ); ?>">
|
||||
<div id="elementor-preview-responsive-wrapper" class="elementor-device-desktop elementor-device-rotate-portrait">
|
||||
<div id="elementor-preview-loading">
|
||||
<i class="eicon-loading eicon-animation-spin" aria-hidden="true"></i>
|
||||
</div>
|
||||
<?php if ( $notice ) {
|
||||
$notice->render();
|
||||
} // IFrame will be created here by the Javascript later. ?>
|
||||
</div>
|
||||
</main>
|
||||
<aside id="elementor-navigator" aria-labelledby="elementor-navigator__header__title"></aside>
|
||||
</div>
|
||||
148
wp-content/plugins/elementor/core/editor/notice-bar.php
Normal file
148
wp-content/plugins/elementor/core/editor/notice-bar.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Notice_Bar extends Base_Object {
|
||||
|
||||
protected function get_init_settings() {
|
||||
if ( Plugin::$instance->get_install_time() > strtotime( '-1 days' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$upgrade_url = 'https://go.elementor.com/go-pro-editor-notice-bar/';
|
||||
|
||||
$config = [
|
||||
'description' => $this->get_description(),
|
||||
'upgrade_text' => $this->get_upgrade_text(),
|
||||
'upgrade_url' => $upgrade_url,
|
||||
];
|
||||
|
||||
$config = Filtered_Promotions_Manager::get_filtered_promotion_data( $config, 'elementor/notice-bar/custom_promotion', 'upgrade_url' );
|
||||
|
||||
return [
|
||||
'muted_period' => 14,
|
||||
'option_key' => '_elementor_editor_upgrade_notice_dismissed',
|
||||
'message' => $config['description'] ?? $this->get_description(),
|
||||
'action_title' => $config['upgrade_text'] ?? $this->get_upgrade_text(),
|
||||
'action_url' => $config['upgrade_url'] ?? $upgrade_url,
|
||||
];
|
||||
}
|
||||
|
||||
public function get_upgrade_text() {
|
||||
return esc_html__( 'Upgrade Now', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_description() {
|
||||
return esc_html__( 'Unleash the full power of Elementor\'s features and web creation tools.', 'elementor' );
|
||||
}
|
||||
|
||||
final public function get_notice() {
|
||||
if ( ! $this->has_access_to_notice() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$settings = $this->get_settings();
|
||||
|
||||
if ( empty( $settings['option_key'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$dismissed_time = get_option( $settings['option_key'] );
|
||||
|
||||
if ( $dismissed_time ) {
|
||||
if ( $dismissed_time > strtotime( '-' . $settings['muted_period'] . ' days' ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->set_notice_dismissed();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function render_action( $type ) {
|
||||
$settings = $this->get_settings();
|
||||
|
||||
// TODO: Make the API better. The bad naming is because of BC.
|
||||
$prefix_map = [
|
||||
'primary' => '',
|
||||
'secondary' => 'secondary_',
|
||||
];
|
||||
|
||||
$prefix = $prefix_map[ $type ];
|
||||
|
||||
$action_title = "{$prefix}action_title";
|
||||
$action_url = "{$prefix}action_url";
|
||||
$action_message = "{$prefix}message";
|
||||
$action_target = "{$prefix}action_target";
|
||||
|
||||
if ( empty( $settings[ $action_title ] ) || empty( $settings[ $action_url ] ) || empty( $settings[ $action_message ] ) ) {
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="e-notice-bar__message <?php echo esc_attr( "e-notice-bar__{$type}_message" ); ?>">
|
||||
<?php Utils::print_unescaped_internal_string( sprintf( $settings[ $action_message ], $settings[ $action_url ] ) ); ?>
|
||||
</div>
|
||||
|
||||
<div class="e-notice-bar__action <?php echo esc_attr( "e-notice-bar__{$type}_action" ); ?>">
|
||||
<a href="<?php Utils::print_unescaped_internal_string( $settings[ $action_url ] ); ?>"
|
||||
target="<?php echo empty( $settings[ $action_target ] ) ? '_blank' : esc_attr( $settings[ $action_target ] ); ?>"
|
||||
>
|
||||
<?php Utils::print_unescaped_internal_string( $settings[ $action_title ] ); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$settings = $this->get_settings();
|
||||
|
||||
$icon = empty( $settings['icon'] )
|
||||
? 'eicon-elementor-square'
|
||||
: esc_attr( $settings['icon'] );
|
||||
|
||||
?>
|
||||
<div id="e-notice-bar" class="e-notice-bar">
|
||||
<i class="e-notice-bar__icon <?php echo esc_attr( $icon ); ?>"></i>
|
||||
|
||||
<?php
|
||||
$this->render_action( 'primary' );
|
||||
$this->render_action( 'secondary' );
|
||||
?>
|
||||
|
||||
<i id="e-notice-bar__close" class="e-notice-bar__close eicon-close"></i>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
|
||||
}
|
||||
|
||||
public function set_notice_dismissed() {
|
||||
if ( ! $this->has_access_to_notice() ) {
|
||||
throw new \Exception( 'Access denied' );
|
||||
}
|
||||
|
||||
update_option( $this->get_settings( 'option_key' ), time() );
|
||||
}
|
||||
|
||||
public function register_ajax_actions( Ajax $ajax ) {
|
||||
$ajax->register_ajax_action( 'notice_bar_dismiss', [ $this, 'set_notice_dismissed' ] );
|
||||
}
|
||||
|
||||
private function has_access_to_notice() {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
}
|
||||
63
wp-content/plugins/elementor/core/editor/promotion.php
Normal file
63
wp-content/plugins/elementor/core/editor/promotion.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Editor;
|
||||
|
||||
use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Promotion {
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function get_elements_promotion() {
|
||||
return Filtered_Promotions_Manager::get_filtered_promotion_data(
|
||||
$this->get_promotion_data(),
|
||||
'elementor/editor/promotion/get_elements_promotion',
|
||||
'action_button',
|
||||
'url'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function get_action_button_content(): array {
|
||||
$has_pro = Utils::has_pro();
|
||||
return $has_pro ? [
|
||||
'text' => __( 'Connect & Activate', 'elementor' ),
|
||||
'url' => admin_url( 'admin.php?page=elementor-license' ),
|
||||
] : [
|
||||
'text' => __( 'Upgrade Now', 'elementor' ),
|
||||
'url' => 'https://go.elementor.com/go-pro-%s',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_promotion_url(): string {
|
||||
return Utils::has_pro()
|
||||
? admin_url( 'admin.php?page=elementor-license' )
|
||||
: 'https://go.elementor.com/go-pro-%s';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function get_promotion_data(): array {
|
||||
return [
|
||||
/* translators: %s: Widget title. */
|
||||
'title' => __( '%s Widget', 'elementor' ),
|
||||
/* translators: %s: Widget title. */
|
||||
'content' => __(
|
||||
'Use %s widget and dozens more pro features to extend your toolbox and build sites faster and better.',
|
||||
'elementor'
|
||||
),
|
||||
'action_button' => $this->get_action_button_content(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Experiments\Exceptions;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Dependency_Exception extends \Exception {
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Experiments;
|
||||
|
||||
use Elementor\Modules\System_Info\Reporters\Base;
|
||||
use Elementor\Plugin;
|
||||
|
||||
/**
|
||||
* Elementor experiments report.
|
||||
*
|
||||
* Elementor experiment report handler class responsible for generating a report for
|
||||
* the experiments included in Elementor and their status.
|
||||
*/
|
||||
class Experiments_Reporter extends Base {
|
||||
|
||||
/**
|
||||
* Get experiments reporter title.
|
||||
*
|
||||
* @return string Reporter title.
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Elementor Experiments', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get experiments report fields.
|
||||
*
|
||||
* @return array Required report fields with field ID and field label.
|
||||
*/
|
||||
public function get_fields() {
|
||||
return [
|
||||
'experiments' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Experiments.
|
||||
*/
|
||||
public function get_experiments() {
|
||||
$result = [];
|
||||
|
||||
$experiments_manager = Plugin::$instance->experiments;
|
||||
|
||||
// TODO: Those keys should be at `$experiments_manager`.
|
||||
$tracking_keys = [
|
||||
'default',
|
||||
'state',
|
||||
'tags',
|
||||
];
|
||||
|
||||
foreach ( $experiments_manager->get_features() as $feature_name => $feature_data ) {
|
||||
$data_to_collect = [];
|
||||
|
||||
// Extract only tracking keys.
|
||||
foreach ( $tracking_keys as $tracking_key ) {
|
||||
if ( empty( $feature_data[ $tracking_key ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data_to_collect[ $tracking_key ] = $feature_data[ $tracking_key ];
|
||||
}
|
||||
|
||||
$result[ $feature_name ] = $data_to_collect;
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => $result,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Raw Experiments.
|
||||
*
|
||||
* Retrieve a string containing the list of Elementor experiments and each experiment's status (active/inactive).
|
||||
* The string is formatted in a non-table structure, and it is meant for export/download of the system info reports.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_raw_experiments() {
|
||||
$experiments = Plugin::$instance->experiments->get_features();
|
||||
|
||||
$output = '';
|
||||
|
||||
$is_first_item = true;
|
||||
|
||||
foreach ( $experiments as $experiment ) {
|
||||
// If the state is default, add the default state to the string.
|
||||
$state = Plugin::$instance->experiments->get_feature_state_label( $experiment );
|
||||
|
||||
// The first item automatically has a tab character before it. Add tabs only to the rest of the items.
|
||||
if ( ! $is_first_item ) {
|
||||
$output .= "\t";
|
||||
}
|
||||
|
||||
$title = isset( $experiment['title'] ) ? $experiment['title'] : $experiment['name'];
|
||||
|
||||
$output .= $title . ': ' . $state . PHP_EOL;
|
||||
|
||||
$is_first_item = false;
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => $output,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML Experiments.
|
||||
*
|
||||
* Retrieve the list of Elementor experiments and each experiment's status (active/inactive), in HTML table format.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_html_experiments() {
|
||||
$experiments = Plugin::$instance->experiments->get_features();
|
||||
|
||||
$output = '';
|
||||
|
||||
foreach ( $experiments as $experiment ) {
|
||||
// If the state is default, add the default state to the string.
|
||||
$state = Plugin::$instance->experiments->get_feature_state_label( $experiment );
|
||||
|
||||
$title = isset( $experiment['title'] ) ? $experiment['title'] : $experiment['name'];
|
||||
|
||||
$output .= '<tr><td>' . esc_html( $title ) . ': </td>';
|
||||
$output .= '<td>' . esc_html( $state ) . '</td>';
|
||||
$output .= '</tr>';
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => $output,
|
||||
];
|
||||
}
|
||||
}
|
||||
1034
wp-content/plugins/elementor/core/experiments/manager.php
Normal file
1034
wp-content/plugins/elementor/core/experiments/manager.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Experiments;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Non_Existing_Dependency {
|
||||
|
||||
private $feature_id;
|
||||
|
||||
public function __construct( $feature_id ) {
|
||||
$this->feature_id = $feature_id;
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return $this->feature_id;
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return $this->feature_id;
|
||||
}
|
||||
|
||||
public function is_hidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function instance( $feature_id ) {
|
||||
return new static( $feature_id );
|
||||
}
|
||||
}
|
||||
195
wp-content/plugins/elementor/core/experiments/wp-cli.php
Normal file
195
wp-content/plugins/elementor/core/experiments/wp-cli.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Experiments;
|
||||
|
||||
use Elementor\Core\Experiments\Manager as Experiments_Manager;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Wp_Cli extends \WP_CLI_Command {
|
||||
|
||||
/**
|
||||
* Activate an Experiment
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* 1. wp elementor experiments activate container
|
||||
* - This will activate the Container experiment.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array|null $assoc_args - Arguments from WP CLI command.
|
||||
*/
|
||||
public function activate( $args, $assoc_args ) {
|
||||
if ( empty( $args[0] ) ) {
|
||||
\WP_CLI::error( 'Please specify an experiment.' );
|
||||
}
|
||||
|
||||
$is_network = $this->is_network( $assoc_args );
|
||||
|
||||
$experiments = $this->parse_experiments( $args[0] );
|
||||
$plural = $this->get_plural( $experiments );
|
||||
$success = 'Experiment' . $plural . ' activated successfully';
|
||||
$error = 'Cannot activate experiment' . $plural;
|
||||
|
||||
if ( $is_network ) {
|
||||
$success .= " for site {$site}";
|
||||
$error .= " for site {$site}";
|
||||
}
|
||||
|
||||
$experiments_manager = Plugin::instance()->experiments;
|
||||
if ( ! $this->check_experiments_exist( $experiments_manager, $experiments ) ) {
|
||||
\WP_CLI::error( 'Experiments do not exist' . $args[0] );
|
||||
}
|
||||
|
||||
if ( $is_network ) {
|
||||
$this->foreach_sites( $this->update_experiment_state, $experiments, Experiments_Manager::STATE_ACTIVE, $is_network, $success, $error );
|
||||
} else {
|
||||
$this->update_experiment_state( $experiments, Experiments_Manager::STATE_ACTIVE, $is_network, $success, $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate an Experiment
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* 1. wp elementor experiments deactivate container
|
||||
* - This will deactivate the Container experiment.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array|null $assoc_args - Arguments from WP CLI command.
|
||||
*/
|
||||
public function deactivate( $args, $assoc_args ) {
|
||||
if ( empty( $args[0] ) ) {
|
||||
\WP_CLI::error( 'Please specify an experiment.' );
|
||||
}
|
||||
|
||||
$is_network = $this->is_network( $assoc_args );
|
||||
|
||||
$experiments = $this->parse_experiments( $args[0] );
|
||||
$plural = $this->get_plural( $experiments );
|
||||
$success = 'Experiment' . $plural . ' deactivated successfully';
|
||||
$error = 'Cannot deactivate experiment' . $plural;
|
||||
|
||||
$experiments_manager = Plugin::instance()->experiments;
|
||||
if ( ! $this->check_experiments_exist( $experiments_manager, $experiments ) ) {
|
||||
\WP_CLI::error( 'Experiments do not exist' );
|
||||
}
|
||||
|
||||
if ( $is_network ) {
|
||||
$this->foreach_sites( $this->update_experiment_state, $experiments, Experiments_Manager::STATE_INACTIVE, $is_network, $success, $error );
|
||||
} else {
|
||||
$this->update_experiment_state( $experiments, Experiments_Manager::STATE_INACTIVE, $is_network, $success, $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Experiment Status
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* 1. wp elementor experiments status container
|
||||
* - This will return the status of Container experiment. (active/inactive)
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
public function status( $args ) {
|
||||
if ( empty( $args[0] ) ) {
|
||||
\WP_CLI::error( 'Please specify an experiment.' );
|
||||
}
|
||||
|
||||
$experiments_manager = Plugin::$instance->experiments;
|
||||
$experiments_status = $experiments_manager->is_feature_active( $args[0] ) ? 'active' : 'inactive';
|
||||
|
||||
\WP_CLI::line( $experiments_status );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current website is a multisite.
|
||||
*
|
||||
* @param array|null $assoc_args - Arguments from WP CLI command.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_network( $assoc_args ) {
|
||||
return ! empty( $assoc_args['network'] ) && is_multisite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over network sites and execute a callback.
|
||||
*
|
||||
* @param callable $callback - Callback to execute. Gets the site name & id as parameters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function foreach_sites( callable $callback, $experiments, $state, $is_network, $success, $error ) {
|
||||
$blog_ids = get_sites( [
|
||||
'fields' => 'ids',
|
||||
'number' => 0,
|
||||
] );
|
||||
|
||||
foreach ( $blog_ids as $blog_id ) {
|
||||
switch_to_blog( $blog_id );
|
||||
|
||||
$callback( get_option( 'home' ), $experiments, $state, $is_network, $success, $error );
|
||||
|
||||
restore_current_blog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $experiments_str comma delimeted string of experiments
|
||||
*
|
||||
* @return array array of experiments
|
||||
*/
|
||||
private function parse_experiments( $experiments_str ) {
|
||||
return explode( ',', $experiments_str );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $experiments experiments
|
||||
*
|
||||
* @return string plural
|
||||
*/
|
||||
private function get_plural( $experiments ) {
|
||||
return count( $experiments ) > 0 ? 's' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Experiments_Manager $experiments_manager manager
|
||||
* @param array $experiments experiments
|
||||
*
|
||||
* @return bool true when all experiments exist, otherwise false
|
||||
*/
|
||||
private function check_experiments_exist( $experiments_manager, $experiments ) {
|
||||
foreach ( $experiments as $experiment ) {
|
||||
$feature = $experiments_manager->get_features( $experiment );
|
||||
if ( ! $feature ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function update_experiment_state( $experiments, $state, $is_network, $success_message, $error_message, $site_id = '' ) {
|
||||
if ( $is_network ) {
|
||||
$success_message .= " for site {$site}";
|
||||
$error_message .= " for site {$site}";
|
||||
}
|
||||
|
||||
$experiments_manager = Plugin::instance()->experiments;
|
||||
foreach ( $experiments as $experiment ) {
|
||||
$option = $experiments_manager->get_feature_option_key( $experiment );
|
||||
update_option( $option, $state );
|
||||
}
|
||||
|
||||
try {
|
||||
\WP_CLI::success( $success_message );
|
||||
} catch ( \Exception $e ) {
|
||||
\WP_CLI::error( $error_message );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Experiments;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Wrap_Core_Dependency {
|
||||
|
||||
private $feature_data;
|
||||
|
||||
public function __construct( $feature_data ) {
|
||||
$this->feature_data = $feature_data;
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return $this->feature_data['name'];
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return $this->feature_data['title'];
|
||||
}
|
||||
|
||||
public function is_hidden() {
|
||||
return $this->feature_data['hidden'];
|
||||
}
|
||||
|
||||
public static function instance( $feature_data ) {
|
||||
return new static( $feature_data );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Files\Assets;
|
||||
|
||||
use Elementor\Core\Files\File_Types\Svg;
|
||||
use Elementor\Core\Files\Uploads_Manager;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Files Upload Handler
|
||||
*
|
||||
* @deprecated 3.5.0 Use `Elementor\Core\Files\Uploads_Manager` class instead.
|
||||
*/
|
||||
abstract class Files_Upload_Handler {
|
||||
|
||||
/**
|
||||
* @deprecated 3.5.0
|
||||
*/
|
||||
const OPTION_KEY = 'elementor_unfiltered_files_upload';
|
||||
|
||||
/**
|
||||
* @deprecated 3.5.0
|
||||
*/
|
||||
abstract public function get_mime_type();
|
||||
|
||||
/**
|
||||
* @deprecated 3.5.0
|
||||
*/
|
||||
abstract public function get_file_type();
|
||||
|
||||
/**
|
||||
* Is Elementor Media Upload
|
||||
*
|
||||
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()` instead.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_elementor_media_upload() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()' );
|
||||
|
||||
return Plugin::$instance->uploads_manager->is_elementor_media_upload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Enabled
|
||||
*
|
||||
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()` instead.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
final public static function is_enabled() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()' );
|
||||
|
||||
return Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()` instead.
|
||||
*/
|
||||
final public function support_unfiltered_files_upload( $existing_mimes ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->support_unfiltered_file_uploads()' );
|
||||
|
||||
return Plugin::$instance->uploads_manager->support_unfiltered_elementor_file_uploads( $existing_mimes );
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_upload_prefilter
|
||||
*
|
||||
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()` instead.
|
||||
*
|
||||
* @param $file
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle_upload_prefilter( $file ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()' );
|
||||
|
||||
return Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload( $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* is_file_should_handled
|
||||
*
|
||||
* @deprecated 3.5.0
|
||||
*
|
||||
* @param $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_file_should_handled( $file ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
|
||||
|
||||
$ext = pathinfo( $file['name'], PATHINFO_EXTENSION );
|
||||
|
||||
return $this->is_elementor_media_upload() && $this->get_file_type() === $ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* file_sanitizer_can_run
|
||||
*
|
||||
* @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg::file_sanitizer_can_run()` instead.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function file_sanitizer_can_run() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Core\Files\File_Types\Svg::file_sanitizer_can_run()' );
|
||||
|
||||
return Svg::file_sanitizer_can_run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check filetype and ext
|
||||
*
|
||||
* A workaround for upload validation which relies on a PHP extension (fileinfo)
|
||||
* with inconsistent reporting behaviour.
|
||||
* ref: https://core.trac.wordpress.org/ticket/39550
|
||||
* ref: https://core.trac.wordpress.org/ticket/40175
|
||||
*
|
||||
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->check_filetype_and_ext()` instead.
|
||||
*
|
||||
* @param $data
|
||||
* @param $file
|
||||
* @param $filename
|
||||
* @param $mimes
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function check_filetype_and_ext( $data, $file, $filename, $mimes ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->check_filetype_and_ext()' );
|
||||
|
||||
Plugin::$instance->uploads_manager->check_filetype_and_ext( $data, $file, $filename, $mimes );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Files\Assets\Json;
|
||||
|
||||
use Elementor\Core\Files\Assets\Files_Upload_Handler;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Json Handler
|
||||
*
|
||||
* @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg` instead, accessed by calling `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );`
|
||||
*/
|
||||
class Json_Handler extends Files_Upload_Handler {
|
||||
|
||||
/**
|
||||
* @deprecated 3.5.0
|
||||
*/
|
||||
public static function get_name() {
|
||||
return 'json-handler';
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.5.0
|
||||
*/
|
||||
public function get_mime_type() {
|
||||
return 'application/json';
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.5.0
|
||||
*/
|
||||
public function get_file_type() {
|
||||
return 'json';
|
||||
}
|
||||
}
|
||||
65
wp-content/plugins/elementor/core/files/assets/manager.php
Normal file
65
wp-content/plugins/elementor/core/files/assets/manager.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Files\Assets;
|
||||
|
||||
use Elementor\Core\Files\Assets\Svg\Svg_Handler;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor files manager.
|
||||
*
|
||||
* Elementor files manager handler class is responsible for creating files.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
class Manager {
|
||||
|
||||
/**
|
||||
* Holds registered asset types
|
||||
* @var array
|
||||
*/
|
||||
protected $asset_types = [];
|
||||
|
||||
/**
|
||||
* Assets manager constructor.
|
||||
*
|
||||
* Initializing the Elementor assets manager.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->register_asset_types();
|
||||
/**
|
||||
* Elementor files assets registered.
|
||||
*
|
||||
* Fires after Elementor registers assets types
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
do_action( 'elementor/core/files/assets/assets_registered', $this );
|
||||
}
|
||||
|
||||
public function get_asset( $name ) {
|
||||
return isset( $this->asset_types[ $name ] ) ? $this->asset_types[ $name ] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Asset
|
||||
* @param $instance
|
||||
*/
|
||||
public function add_asset( $instance ) {
|
||||
$this->asset_types[ $instance::get_name() ] = $instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register Asset Types
|
||||
*
|
||||
* Registers Elementor Asset Types
|
||||
*/
|
||||
private function register_asset_types() {
|
||||
$this->add_asset( new Svg_Handler() );
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user