first commit
This commit is contained in:
1114
wp-content/plugins/elementor/core/admin/admin-notices.php
Normal file
1114
wp-content/plugins/elementor/core/admin/admin-notices.php
Normal file
File diff suppressed because it is too large
Load Diff
1252
wp-content/plugins/elementor/core/admin/admin.php
Normal file
1252
wp-content/plugins/elementor/core/admin/admin.php
Normal file
File diff suppressed because it is too large
Load Diff
192
wp-content/plugins/elementor/core/admin/canary-deployment.php
Normal file
192
wp-content/plugins/elementor/core/admin/canary-deployment.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?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.MisspelledInText
|
||||
// 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' ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu;
|
||||
|
||||
use Elementor\Core\Admin\EditorOneMenu\Menu\Editor_One_Custom_Elements_Menu;
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Legacy_Submenu_Interceptor;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Data_Provider;
|
||||
use Elementor\Modules\EditorOne\Classes\Slug_Normalizer;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Elementor_One_Menu_Manager {
|
||||
|
||||
private Menu_Data_Provider $menu_data_provider;
|
||||
|
||||
private bool $is_pro_module_enabled = false;
|
||||
|
||||
private Legacy_Submenu_Interceptor $legacy_submenu_interceptor;
|
||||
|
||||
public function __construct() {
|
||||
$this->menu_data_provider = Menu_Data_Provider::instance();
|
||||
$this->legacy_submenu_interceptor = new Legacy_Submenu_Interceptor(
|
||||
$this->menu_data_provider,
|
||||
new Slug_Normalizer()
|
||||
);
|
||||
$this->register_actions();
|
||||
}
|
||||
|
||||
private function register_actions(): void {
|
||||
add_action( 'init', [ $this, 'check_if_pro_module_is_enabled' ] );
|
||||
add_action( 'admin_menu', [ $this, 'register_elementor_home_submenus' ], 9 );
|
||||
|
||||
add_action( 'admin_menu', function () {
|
||||
do_action( 'elementor/editor-one/menu/register', $this->menu_data_provider );
|
||||
} );
|
||||
|
||||
add_action( 'admin_menu', [ $this, 'register_pro_submenus' ], 100 );
|
||||
|
||||
add_action( 'admin_menu', [ $this, 'intercept_legacy_submenus' ], 10003 );
|
||||
add_action( 'admin_menu', [ $this, 'register_flyout_items_as_hidden_submenus' ], 10004 );
|
||||
add_action( 'admin_menu', [ $this, 'remove_all_submenus_for_edit_posts_users' ], 10005 );
|
||||
add_action( 'admin_menu', [ $this, 'override_elementor_page_for_edit_posts_users' ], 1006 );
|
||||
add_filter( 'add_menu_classes', [ $this, 'fix_theme_builder_submenu_url' ] );
|
||||
add_action( 'admin_head', [ $this, 'hide_flyout_items_from_wp_menu' ] );
|
||||
add_action( 'admin_head', [ $this, 'hide_legacy_templates_menu' ] );
|
||||
add_action( 'admin_head', [ $this, 'hide_old_elementor_menu' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_menu_assets' ] );
|
||||
add_action( 'admin_print_scripts-elementor_page_elementor-editor', [ $this, 'enqueue_home_screen_on_editor_page' ] );
|
||||
}
|
||||
|
||||
public function check_if_pro_module_is_enabled(): void {
|
||||
$this->is_pro_module_enabled = apply_filters( 'elementor/modules/editor-one/is_pro_module_enabled', false );
|
||||
|
||||
if ( ! $this->is_pro_module_enabled && Utils::has_pro() ) {
|
||||
$this->menu_data_provider->register_menu( new Editor_One_Custom_Elements_Menu() );
|
||||
}
|
||||
}
|
||||
|
||||
public function register_elementor_home_submenus(): void {
|
||||
add_submenu_page(
|
||||
Menu_Config::ELEMENTOR_HOME_MENU_SLUG,
|
||||
esc_html__( 'Editor', 'elementor' ),
|
||||
esc_html__( 'Editor', 'elementor' ),
|
||||
Menu_Config::CAPABILITY_EDIT_POSTS,
|
||||
Menu_Config::ELEMENTOR_MENU_SLUG,
|
||||
[ $this, 'render_editor_page' ],
|
||||
20
|
||||
);
|
||||
|
||||
do_action( 'elementor/editor-one/menu/register_submenus' );
|
||||
}
|
||||
|
||||
public function register_pro_submenus(): void {
|
||||
if ( ! $this->is_pro_module_enabled &&
|
||||
Utils::has_pro() &&
|
||||
class_exists( '\ElementorPro\License\API' ) &&
|
||||
\ElementorPro\License\API::is_license_active()
|
||||
) {
|
||||
add_submenu_page(
|
||||
Menu_Config::ELEMENTOR_HOME_MENU_SLUG,
|
||||
esc_html__( 'Theme Builder', 'elementor' ),
|
||||
esc_html__( 'Theme Builder', 'elementor' ),
|
||||
Menu_Config::CAPABILITY_EDIT_POSTS,
|
||||
'elementor-theme-builder',
|
||||
'',
|
||||
70
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
Menu_Config::ELEMENTOR_HOME_MENU_SLUG,
|
||||
esc_html__( 'Submissions', 'elementor' ),
|
||||
esc_html__( 'Submissions', 'elementor' ),
|
||||
'edit_posts',
|
||||
'e-form-submissions',
|
||||
'',
|
||||
80
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function remove_all_submenus_for_edit_posts_users(): void {
|
||||
$user_capabilities = Menu_Data_Provider::get_current_user_capabilities();
|
||||
|
||||
if ( ! $user_capabilities['is_edit_posts_user'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $submenu;
|
||||
|
||||
if ( empty( $submenu[ Menu_Config::ELEMENTOR_MENU_SLUG ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$submenu_items = $submenu[ Menu_Config::ELEMENTOR_MENU_SLUG ];
|
||||
|
||||
foreach ( $submenu_items as $index => $submenu_item ) {
|
||||
if ( 0 === $index ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$submenu_slug = $submenu_item[2] ?? '';
|
||||
if ( ! empty( $submenu_slug ) ) {
|
||||
remove_submenu_page( Menu_Config::ELEMENTOR_MENU_SLUG, $submenu_slug );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function render_editor_page(): void {
|
||||
Plugin::instance()->settings->display_home_screen();
|
||||
}
|
||||
|
||||
public function override_elementor_page_for_edit_posts_users(): void {
|
||||
$user_capabilities = Menu_Data_Provider::get_current_user_capabilities();
|
||||
|
||||
if ( ! $user_capabilities['is_edit_posts_user'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ?? '';
|
||||
if ( Menu_Config::ELEMENTOR_MENU_SLUG !== $page ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$templates_url = admin_url( 'edit.php?post_type=elementor_library&tabs_group=library' );
|
||||
wp_safe_redirect( $templates_url );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function enqueue_home_screen_on_editor_page(): void {
|
||||
$home_module = Plugin::instance()->modules_manager->get_modules( 'home' );
|
||||
|
||||
if ( $home_module && method_exists( $home_module, 'enqueue_home_screen_scripts' ) ) {
|
||||
$home_module->enqueue_home_screen_scripts();
|
||||
}
|
||||
}
|
||||
|
||||
public function fix_theme_builder_submenu_url( $menu ) {
|
||||
global $submenu;
|
||||
|
||||
$menu_slugs = [ Menu_Config::ELEMENTOR_HOME_MENU_SLUG ];
|
||||
|
||||
foreach ( $menu_slugs as $menu_slug ) {
|
||||
if ( empty( $submenu[ $menu_slug ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $submenu[ $menu_slug ] as &$item ) {
|
||||
if ( 'elementor-theme-builder' === $item[2] ) {
|
||||
$item[2] = $this->get_theme_builder_url(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
private function get_theme_builder_url(): string {
|
||||
return $this->menu_data_provider->get_theme_builder_url();
|
||||
}
|
||||
|
||||
public function hide_legacy_templates_menu(): void {
|
||||
?>
|
||||
<style type="text/css">
|
||||
#menu-posts-elementor_library {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function hide_old_elementor_menu(): void {
|
||||
$this->remove_elementor_separator();
|
||||
?>
|
||||
<style type="text/css">
|
||||
#toplevel_page_elementor {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function remove_elementor_separator(): void {
|
||||
global $menu;
|
||||
|
||||
foreach ( $menu as $key => $item ) {
|
||||
if ( isset( $item[2] ) && 'separator-elementor' === $item[2] ) {
|
||||
unset( $menu[ $key ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function register_flyout_items_as_hidden_submenus(): void {
|
||||
$hooks = [];
|
||||
|
||||
$this->iterate_all_flyout_items( function( string $item_slug, Menu_Item_Interface $item ) use ( &$hooks ) {
|
||||
$hook = $this->register_hidden_submenu( $item_slug, $item );
|
||||
|
||||
if ( $hook ) {
|
||||
$hooks[ $item_slug ] = $hook;
|
||||
}
|
||||
} );
|
||||
|
||||
do_action( 'elementor/editor-one/menu/after_register_hidden_submenus', $hooks );
|
||||
}
|
||||
|
||||
private function register_hidden_submenu( string $item_slug, Menu_Item_Interface $item ) {
|
||||
$original_parent = $this->get_original_parent_slug( $item );
|
||||
$parent_slug = $this->resolve_hidden_submenu_parent( $original_parent );
|
||||
$has_page = method_exists( $item, 'render' );
|
||||
$page_title = $has_page ? $item->get_page_title() : '';
|
||||
$callback = $has_page ? [ $item, 'render' ] : '';
|
||||
$capability = $item->get_capability();
|
||||
$position = $item->get_position();
|
||||
|
||||
return add_submenu_page(
|
||||
$parent_slug,
|
||||
$page_title,
|
||||
$item->get_label(),
|
||||
$capability,
|
||||
$item_slug,
|
||||
$callback,
|
||||
$position
|
||||
);
|
||||
}
|
||||
|
||||
private function resolve_hidden_submenu_parent( ?string $parent_slug ): string {
|
||||
$default_parent_slug = Menu_Config::ELEMENTOR_HOME_MENU_SLUG;
|
||||
if ( empty( $parent_slug ) ) {
|
||||
return $default_parent_slug;
|
||||
}
|
||||
|
||||
$elementor_parent_slugs = [
|
||||
Menu_Config::EDITOR_GROUP_ID => true,
|
||||
Menu_Config::EDITOR_MENU_SLUG => true,
|
||||
Menu_Config::TEMPLATES_GROUP_ID => true,
|
||||
Menu_Config::LEGACY_TEMPLATES_SLUG => true,
|
||||
Menu_Config::SETTINGS_GROUP_ID => true,
|
||||
Menu_Config::CUSTOM_ELEMENTS_GROUP_ID => true,
|
||||
Menu_Config::SYSTEM_GROUP_ID => true,
|
||||
];
|
||||
|
||||
if ( isset( $elementor_parent_slugs[ $parent_slug ] ) ) {
|
||||
return $default_parent_slug;
|
||||
}
|
||||
|
||||
return $parent_slug;
|
||||
}
|
||||
|
||||
private function iterate_all_flyout_items( callable $callback ): void {
|
||||
$level3_items = $this->menu_data_provider->get_level3_items();
|
||||
$level4_items = $this->menu_data_provider->get_level4_items();
|
||||
|
||||
$all_items = array_merge_recursive( $level3_items, $level4_items );
|
||||
|
||||
foreach ( $all_items as $group_items ) {
|
||||
foreach ( $group_items as $item_slug => $item ) {
|
||||
$callback( $item_slug, $item );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_original_parent_slug( $item ): ?string {
|
||||
return $item->get_parent_slug();
|
||||
}
|
||||
|
||||
public function hide_flyout_items_from_wp_menu(): void {
|
||||
$protected_wp_menu_slugs = [
|
||||
Menu_Config::EDITOR_MENU_SLUG,
|
||||
'elementor-theme-builder',
|
||||
'e-form-submissions',
|
||||
];
|
||||
|
||||
$this->iterate_all_flyout_items( function( string $item_slug, Menu_Item_Interface $item ) use ( $protected_wp_menu_slugs ) {
|
||||
if ( in_array( $item_slug, $protected_wp_menu_slugs, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$original_parent = $this->get_original_parent_slug( $item );
|
||||
$parent_slug = $this->resolve_hidden_submenu_parent( $original_parent );
|
||||
remove_submenu_page( $parent_slug, $item_slug );
|
||||
} );
|
||||
}
|
||||
|
||||
public function intercept_legacy_submenus(): void {
|
||||
$this->legacy_submenu_interceptor->intercept_all( $this->is_pro_module_enabled );
|
||||
}
|
||||
|
||||
public function enqueue_admin_menu_assets(): void {
|
||||
|
||||
$min_suffix = Utils::is_script_debug() ? '' : '.min';
|
||||
|
||||
wp_enqueue_style(
|
||||
'elementor-admin-menu',
|
||||
ELEMENTOR_ASSETS_URL . 'css/modules/editor-one/admin-menu' . $min_suffix . '.css',
|
||||
[],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
|
||||
$config = [
|
||||
'editorFlyout' => $this->menu_data_provider->get_third_level_data(
|
||||
Menu_Data_Provider::THIRD_LEVEL_FLYOUT_MENU
|
||||
),
|
||||
];
|
||||
|
||||
wp_enqueue_script(
|
||||
'editor-one-menu',
|
||||
ELEMENTOR_ASSETS_URL . 'js/editor-one-menu' . $min_suffix . '.js',
|
||||
[],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'editor-one-menu',
|
||||
'editorOneMenuConfig',
|
||||
$config
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Interfaces;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
interface Menu_Item_Interface {
|
||||
|
||||
public function get_capability(): string;
|
||||
|
||||
public function get_label(): string;
|
||||
|
||||
public function get_parent_slug(): string;
|
||||
|
||||
public function is_visible(): bool;
|
||||
|
||||
public function get_position(): int;
|
||||
|
||||
public function get_slug(): string;
|
||||
|
||||
public function get_group_id(): string;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Interfaces;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
interface Menu_Item_Third_Level_Interface extends Menu_Item_Interface {
|
||||
|
||||
public function get_icon(): string;
|
||||
|
||||
public function has_children(): bool;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Interfaces;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
interface Menu_Item_With_Custom_Url_Interface {
|
||||
|
||||
public function get_menu_url(): string;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Menu;
|
||||
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
abstract class Abstract_Level4_Menu_Item implements Menu_Item_Interface {
|
||||
|
||||
public function get_capability(): string {
|
||||
return Menu_Config::CAPABILITY_MANAGE_OPTIONS;
|
||||
}
|
||||
|
||||
public function get_parent_slug(): string {
|
||||
return Menu_Config::ELEMENTOR_MENU_SLUG;
|
||||
}
|
||||
|
||||
public function is_visible(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract public function get_label(): string;
|
||||
|
||||
abstract public function get_position(): int;
|
||||
|
||||
abstract public function get_slug(): string;
|
||||
|
||||
abstract public function get_group_id(): string;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Menu;
|
||||
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Third_Level_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
abstract class Abstract_Menu_Item implements Menu_Item_Third_Level_Interface {
|
||||
|
||||
public function get_capability(): string {
|
||||
return Menu_Config::CAPABILITY_MANAGE_OPTIONS;
|
||||
}
|
||||
|
||||
public function get_parent_slug(): string {
|
||||
return Menu_Config::ELEMENTOR_MENU_SLUG;
|
||||
}
|
||||
|
||||
public function is_visible(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_group_id(): string {
|
||||
return Menu_Config::EDITOR_GROUP_ID;
|
||||
}
|
||||
|
||||
public function has_children(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract public function get_label(): string;
|
||||
|
||||
abstract public function get_position(): int;
|
||||
|
||||
abstract public function get_slug(): string;
|
||||
|
||||
abstract public function get_icon(): string;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Menu;
|
||||
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Third_Level_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Editor_One_Custom_Elements_Menu implements Menu_Item_Third_Level_Interface {
|
||||
|
||||
public function get_capability(): string {
|
||||
return Menu_Config::CAPABILITY_MANAGE_OPTIONS;
|
||||
}
|
||||
|
||||
public function get_parent_slug(): string {
|
||||
return Menu_Config::ELEMENTOR_MENU_SLUG;
|
||||
}
|
||||
|
||||
public function is_visible(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_label(): string {
|
||||
return esc_html__( 'Custom Elements', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_position(): int {
|
||||
return 70;
|
||||
}
|
||||
|
||||
public function get_slug(): string {
|
||||
return 'elementor-custom-elements';
|
||||
}
|
||||
|
||||
public function get_icon(): string {
|
||||
return 'adjustments';
|
||||
}
|
||||
|
||||
public function get_group_id(): string {
|
||||
return Menu_Config::CUSTOM_ELEMENTS_GROUP_ID;
|
||||
}
|
||||
|
||||
public function has_children(): bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Menu;
|
||||
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Legacy_Submenu_Item_Not_Mapped implements Menu_Item_Interface {
|
||||
|
||||
private $submenu_data;
|
||||
|
||||
private $parent_slug;
|
||||
|
||||
private $position;
|
||||
|
||||
public function __construct( array $submenu_data, ?string $parent_slug = null, ?int $position = 100 ) {
|
||||
$this->submenu_data = $submenu_data;
|
||||
$this->parent_slug = $parent_slug ?? Menu_Config::ELEMENTOR_MENU_SLUG;
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
public function get_label(): string {
|
||||
return $this->submenu_data[0] ?? '';
|
||||
}
|
||||
|
||||
public function get_capability(): string {
|
||||
return $this->submenu_data[1] ?? Menu_Config::CAPABILITY_MANAGE_OPTIONS;
|
||||
}
|
||||
|
||||
public function get_slug(): string {
|
||||
return $this->submenu_data[2] ?? '';
|
||||
}
|
||||
|
||||
public function get_parent_slug(): string {
|
||||
return $this->parent_slug;
|
||||
}
|
||||
|
||||
public function is_visible(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_page_title(): string {
|
||||
return $this->submenu_data[3] ?? $this->get_label();
|
||||
}
|
||||
|
||||
public function get_position(): int {
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function get_group_id(): string {
|
||||
return Menu_Config::THIRD_PARTY_GROUP_ID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Menu;
|
||||
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Third_Level_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Legacy_Submenu_Item implements Menu_Item_Third_Level_Interface {
|
||||
|
||||
private $submenu_data;
|
||||
|
||||
private $parent_slug;
|
||||
|
||||
private $position;
|
||||
|
||||
public function __construct( array $submenu_data, ?string $parent_slug = null, ?int $position = 100 ) {
|
||||
$this->submenu_data = $submenu_data;
|
||||
$this->parent_slug = $parent_slug ?? Menu_Config::ELEMENTOR_MENU_SLUG;
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
public function get_label(): string {
|
||||
return $this->submenu_data[0] ?? '';
|
||||
}
|
||||
|
||||
public function get_capability(): string {
|
||||
return $this->submenu_data[1] ?? Menu_Config::CAPABILITY_MANAGE_OPTIONS;
|
||||
}
|
||||
|
||||
public function get_slug(): string {
|
||||
return $this->submenu_data[2] ?? '';
|
||||
}
|
||||
|
||||
public function get_parent_slug(): string {
|
||||
return $this->parent_slug;
|
||||
}
|
||||
|
||||
public function is_visible(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_page_title(): string {
|
||||
return $this->submenu_data[3] ?? $this->get_label();
|
||||
}
|
||||
|
||||
public function get_position(): int {
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function get_group_id(): string {
|
||||
return $this->submenu_data[4] ?? Menu_Config::EDITOR_GROUP_ID;
|
||||
}
|
||||
|
||||
public function get_icon(): string {
|
||||
$item_slug = $this->get_slug();
|
||||
$icon = Menu_Config::get_attribute_mapping()[ $item_slug ]['icon'] ?? 'admin-generic';
|
||||
|
||||
return $icon;
|
||||
}
|
||||
|
||||
public function has_children(): bool {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Admin\EditorOneMenu\Menu;
|
||||
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Third_Level_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Third_Party_Pages_Menu implements Menu_Item_Third_Level_Interface {
|
||||
|
||||
public function get_capability(): string {
|
||||
return Menu_Config::CAPABILITY_EDIT_POSTS;
|
||||
}
|
||||
|
||||
public function get_parent_slug(): string {
|
||||
return Menu_Config::ELEMENTOR_HOME_MENU_SLUG;
|
||||
}
|
||||
|
||||
public function is_visible(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_label(): string {
|
||||
return esc_html__( 'Addons', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_position(): int {
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function get_slug(): string {
|
||||
return 'elementor-third-party-pages';
|
||||
}
|
||||
|
||||
public function get_icon(): string {
|
||||
return 'extension';
|
||||
}
|
||||
|
||||
public function get_group_id(): string {
|
||||
return Menu_Config::THIRD_PARTY_GROUP_ID;
|
||||
}
|
||||
|
||||
public function has_children(): bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
182
wp-content/plugins/elementor/core/admin/feedback.php
Normal file
182
wp-content/plugins/elementor/core/admin/feedback.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?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 = Utils::is_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' );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
107
wp-content/plugins/elementor/core/admin/ui/components/button.php
Normal file
107
wp-content/plugins/elementor/core/admin/ui/components/button.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?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, true ) ) {
|
||||
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,35 @@
|
||||
<?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';
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
* @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,168 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\BackgroundProcess;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link 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: [])
|
||||
*
|
||||
* @var array
|
||||
* @access protected
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* Initiate new async request
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->identifier = $this->prefix . '_' . $this->action;
|
||||
|
||||
add_action( 'wp_ajax_' . $this->identifier, [ $this, 'maybe_handle' ] );
|
||||
add_action( 'wp_ajax_nopriv_' . $this->identifier, [ $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 [
|
||||
'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 [
|
||||
'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,518 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base\BackgroundProcess;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link 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, [ $this, 'handle_cron_healthcheck' ] );
|
||||
add_filter( 'cron_schedules', [ $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() . wp_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' ] = [
|
||||
'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,92 @@
|
||||
<?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;
|
||||
}
|
||||
/**
|
||||
* @param $flag
|
||||
* @return void
|
||||
* // 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;
|
||||
}
|
||||
}
|
||||
389
wp-content/plugins/elementor/core/base/background-task.php
Normal file
389
wp-content/plugins/elementor/core/base/background-task.php
Normal file
@@ -0,0 +1,389 @@
|
||||
<?php
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @package Elementor\Core\Base
|
||||
*/
|
||||
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Core\Base\BackgroundProcess\WP_Background_Process;
|
||||
|
||||
|
||||
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' ] = [
|
||||
'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();
|
||||
}
|
||||
}
|
||||
194
wp-content/plugins/elementor/core/base/base-object.php
Normal file
194
wp-content/plugins/elementor/core/base/base-object.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
2112
wp-content/plugins/elementor/core/base/document.php
Normal file
2112
wp-content/plugins/elementor/core/base/document.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,204 @@
|
||||
<?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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $page_assets;
|
||||
|
||||
/**
|
||||
* Default value must be empty.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
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 );
|
||||
|
||||
$element_assets_depend = [
|
||||
'styles' => $element_data->get_style_depends(),
|
||||
'scripts' => array_merge( $element_data->get_script_depends(), $element_data->get_global_scripts() ),
|
||||
];
|
||||
|
||||
if ( $element_assets_depend ) {
|
||||
foreach ( $element_assets_depend as $assets_type => $assets ) {
|
||||
if ( empty( $assets ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! isset( $element_assets[ $assets_type ] ) ) {
|
||||
$element_assets[ $assets_type ] = [];
|
||||
}
|
||||
|
||||
foreach ( $assets as $asset_name ) {
|
||||
if ( ! in_array( $asset_name, $element_assets[ $assets_type ], true ) ) {
|
||||
$element_assets[ $assets_type ][] = $asset_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,82 @@
|
||||
<?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.
|
||||
*
|
||||
* @var \Elementor\Core\Document
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
358
wp-content/plugins/elementor/core/base/module.php
Normal file
358
wp-content/plugins/elementor/core/base/module.php
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Base;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
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 $this 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 = Utils::is_script_debug() || Utils::is_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 Frontend File URL
|
||||
*
|
||||
* Returns the URL for the CSS file to be loaded in the front end. If requested via the second parameter, a custom
|
||||
* file is generated based on a passed template file name. Otherwise, the URL for the default CSS file is returned.
|
||||
*
|
||||
* @since 3.24.0
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param string $file_name
|
||||
* @param boolean $has_custom_breakpoints
|
||||
*
|
||||
* @return string frontend file URL
|
||||
*/
|
||||
public function get_frontend_file_url( $file_name, $has_custom_breakpoints ) {
|
||||
return Plugin::$instance->frontend->get_frontend_file_url( $file_name, $has_custom_breakpoints );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,283 @@
|
||||
<?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_text_mapping( string $platform ): string {
|
||||
static::init_social_networks_array_if_empty();
|
||||
|
||||
if ( isset( self::$social_networks[ $platform ]['text'] ) ) {
|
||||
return self::$social_networks[ $platform ]['text'];
|
||||
}
|
||||
|
||||
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,298 @@
|
||||
<?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 $control_name, string $default_tag = 'h2' ): void {
|
||||
$this->add_control(
|
||||
$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_tag,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_value = '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_value,
|
||||
'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 ); ?>"
|
||||
aria-hidden="true"
|
||||
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 );
|
||||
}
|
||||
}
|
||||
306
wp-content/plugins/elementor/core/common/app.php
Normal file
306
wp-content/plugins/elementor/core/common/app.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<?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\Common\Modules\EventsManager\Module as Events_Manager;
|
||||
use Elementor\Core\Files\Uploads_Manager;
|
||||
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' ], 9 );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ], 9 );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ], 9 );
|
||||
|
||||
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() );
|
||||
|
||||
Plugin::$instance->experiments->add_feature( Events_Manager::get_experimental_data() );
|
||||
|
||||
if ( Plugin::$instance->experiments->is_feature_active( Events_Manager::EXPERIMENT_NAME ) ) {
|
||||
$this->add_component( 'events-manager', new Events_Manager() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
$all_experimental_features = Plugin::$instance->experiments->get_features();
|
||||
|
||||
$active_experimental_features = array_fill_keys( array_keys( $active_experimental_features ), true );
|
||||
$all_experimental_features = array_map(
|
||||
function( $feature ) {
|
||||
return Plugin::$instance->experiments->is_feature_active( $feature['name'] );
|
||||
},
|
||||
$all_experimental_features
|
||||
);
|
||||
|
||||
$config = [
|
||||
'version' => ELEMENTOR_VERSION,
|
||||
'isRTL' => is_rtl(),
|
||||
'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
|
||||
'isElementorDebug' => Utils::is_elementor_debug(),
|
||||
'activeModules' => array_keys( $this->get_components() ),
|
||||
'experimentalFeatures' => $active_experimental_features,
|
||||
'allExperimentalFeatures' => $all_experimental_features,
|
||||
'urls' => [
|
||||
'assets' => ELEMENTOR_ASSETS_URL,
|
||||
'rest' => get_rest_url(),
|
||||
],
|
||||
'filesUpload' => [
|
||||
'unfilteredFiles' => Uploads_Manager::are_unfiltered_uploads_enabled(),
|
||||
],
|
||||
'editor_events' => Events_Manager::get_editor_events_config(),
|
||||
];
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
316
wp-content/plugins/elementor/core/common/modules/ajax/module.php
Normal file
316
wp-content/plugins/elementor/core/common/modules/ajax/module.php
Normal file
@@ -0,0 +1,316 @@
|
||||
<?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();
|
||||
}
|
||||
|
||||
header( 'Content-Type: application/json; charset=UTF-8' );
|
||||
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,73 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Connect\AdminMenuItems;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Interfaces\Admin_Menu_Item_With_Page;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Base_App;
|
||||
use Elementor\Core\Admin\EditorOneMenu\Interfaces\Menu_Item_Interface;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Config;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Editor_One_Connect_Menu implements Menu_Item_Interface, Admin_Menu_Item_With_Page {
|
||||
|
||||
public function get_capability(): string {
|
||||
return 'edit_posts';
|
||||
}
|
||||
|
||||
public function get_parent_slug(): string {
|
||||
return Menu_Config::ELEMENTOR_MENU_SLUG;
|
||||
}
|
||||
|
||||
public function is_visible(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_label(): string {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_position(): int {
|
||||
return 999;
|
||||
}
|
||||
|
||||
public function get_slug(): string {
|
||||
return 'elementor-connect';
|
||||
}
|
||||
|
||||
public function get_group_id(): string {
|
||||
return Menu_Config::SYSTEM_GROUP_ID;
|
||||
}
|
||||
|
||||
public function get_page_title() {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
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,141 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Admin_Menu_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Settings;
|
||||
use Elementor\Utils;
|
||||
use Elementor\Modules\EditorOne\Classes\Menu_Data_Provider;
|
||||
use Elementor\Core\Common\Modules\Connect\AdminMenuItems\Editor_One_Connect_Menu;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Admin {
|
||||
|
||||
const PAGE_ID = 'elementor-connect';
|
||||
|
||||
public static $url = '';
|
||||
|
||||
private function get_valid_redirect_to_from_request() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only reading a URL parameter.
|
||||
$raw = Utils::get_super_global_value( $_GET, 'redirect_to' );
|
||||
|
||||
if ( ! $raw ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$raw = esc_url_raw( $raw );
|
||||
|
||||
$validated = wp_validate_redirect( $raw, '' );
|
||||
if ( ! $validated ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$admin_host = wp_parse_url( admin_url(), PHP_URL_HOST );
|
||||
$dest_host = wp_parse_url( $validated, PHP_URL_HOST );
|
||||
if ( $dest_host && $admin_host && ! hash_equals( $admin_host, $dest_host ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $validated;
|
||||
}
|
||||
|
||||
public function register_admin_menu( Admin_Menu_Manager $admin_menu ) {
|
||||
if ( ! $this->is_editor_one_active() ) {
|
||||
$admin_menu->register( static::PAGE_ID, new Connect_Menu_Item() );
|
||||
}
|
||||
}
|
||||
|
||||
public function register_editor_one_menu( Menu_Data_Provider $menu_data_provider ) {
|
||||
$menu_data_provider->register_menu( new Editor_One_Connect_Menu() );
|
||||
}
|
||||
|
||||
private function is_editor_one_active(): bool {
|
||||
return (bool) Plugin::instance()->modules_manager->get_modules( 'editor-one' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function on_load_page() {
|
||||
if ( ! $this->user_has_enough_permissions() ) {
|
||||
wp_die( 'You do not have sufficient permissions to access this page.', 'You do not have sufficient permissions to access this page.', [
|
||||
'back_link' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
// Allow a per-request default landing URL when provided via a safe `redirect_to` parameter.
|
||||
$maybe_redirect_to = $this->get_valid_redirect_to_from_request();
|
||||
if ( $maybe_redirect_to ) {
|
||||
self::$url = $maybe_redirect_to;
|
||||
}
|
||||
|
||||
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 ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function user_has_enough_permissions() {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( 'library' === Utils::get_super_global_value( $_GET, 'app' ) ) {
|
||||
return current_user_can( 'edit_posts' );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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/editor-one/menu/register', [ $this, 'register_editor_one_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 );
|
||||
|
||||
add_action( 'elementor/editor-one/menu/after_register_hidden_submenus', function ( array $hooks ) {
|
||||
if ( ! empty( $hooks[ static::PAGE_ID ] ) ) {
|
||||
add_action( 'load-' . $hooks[ static::PAGE_ID ], [ $this, 'on_load_page' ] );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,865 @@
|
||||
<?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 = '';
|
||||
|
||||
printf(
|
||||
'%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() {
|
||||
$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_value = null ) {
|
||||
$this->init_data();
|
||||
|
||||
return isset( $this->data[ $key ] ) ? $this->data[ $key ] : $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_value = '' ) {
|
||||
$new_value = $this->get( $key, $default_value );
|
||||
|
||||
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 ) && empty( $options['with_error_data'] ) ) {
|
||||
// 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 ( 201 === $response_code ) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
protected 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' ),
|
||||
] );
|
||||
|
||||
$utm_campaign = get_transient( 'elementor_core_campaign' );
|
||||
|
||||
if ( ! empty( $utm_campaign ) ) {
|
||||
foreach ( [ 'source', 'medium', 'campaign' ] as $key ) {
|
||||
if ( ! empty( $utm_campaign[ $key ] ) ) {
|
||||
$query_params->offsetSet( 'utm_' . $key, $utm_campaign[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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':
|
||||
case 'rest':
|
||||
$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 ); ?>
|
||||
);
|
||||
|
||||
opener.dispatchEvent( new CustomEvent( 'elementor/connect/success' ),
|
||||
<?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':
|
||||
case 'rest':
|
||||
$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' );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
$val = Utils::get_super_global_value( $_REQUEST, 'redirect_to' );
|
||||
if ( $val ) {
|
||||
$redirect_uri = add_query_arg( [ 'redirect_to' => $val ], $redirect_uri );
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case 'rest':
|
||||
// After `wp_send_json` the script will die.
|
||||
$this->delete( 'notices' );
|
||||
wp_send_json( $notices );
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
protected 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' );
|
||||
|
||||
if ( $mode ) {
|
||||
$allowed_auth_modes = [
|
||||
'popup',
|
||||
];
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
$allowed_auth_modes[] = 'cli';
|
||||
}
|
||||
|
||||
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
|
||||
$allowed_auth_modes[] = 'rest';
|
||||
}
|
||||
|
||||
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,94 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Feedback extends Common_App {
|
||||
|
||||
const EXPERIMENT_NAME = 'in_editor_feedback';
|
||||
|
||||
const FEEDBACK_ENDPOINT = 'https://my.elementor.com/feedback/api/v1';
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
Plugin::$instance->experiments->add_feature([
|
||||
'name' => self::EXPERIMENT_NAME,
|
||||
'title' => esc_html__( 'In-Editor Feedback', 'elementor' ),
|
||||
'description' => esc_html__( 'Enable in-editor feedback submission.', 'elementor' ),
|
||||
'hidden' => true,
|
||||
'release_status' => Plugin::$instance->experiments::RELEASE_STATUS_BETA,
|
||||
'default' => Plugin::$instance->experiments::STATE_INACTIVE,
|
||||
'new_site' => [
|
||||
'default_active' => false,
|
||||
'minimum_installation_version' => '3.35.0',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return esc_html__( 'Product Feedback', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_slug() {
|
||||
return 'product-feedback';
|
||||
}
|
||||
|
||||
protected function get_base_connect_info() {
|
||||
return [
|
||||
'app' => 'editor',
|
||||
'access_token' => $this->get( 'access_token' ),
|
||||
'client_id' => $this->get( 'client_id' ),
|
||||
'endpoint' => 'taxonomies',
|
||||
'local_id' => get_current_user_id(),
|
||||
'site_key' => $this->get_site_key(),
|
||||
'home_url' => trailingslashit( home_url() ),
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_api_url() {
|
||||
return static::FEEDBACK_ENDPOINT . '/' . $this->get_slug();
|
||||
}
|
||||
|
||||
protected function get_generated_urls( $endpoint ) {
|
||||
return [ $this->get_api_url() ];
|
||||
}
|
||||
|
||||
public function submit( $body ) {
|
||||
$is_active = Plugin::instance()->experiments->is_feature_active( self::EXPERIMENT_NAME );
|
||||
if ( ! $is_active ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'data' => [
|
||||
'message' => 'In-Editor Feedback is not active.',
|
||||
],
|
||||
];
|
||||
}
|
||||
$connect_info = $this->get_base_connect_info();
|
||||
$merged_body = array_merge( $connect_info, $body );
|
||||
$signature = $this->generate_signature( $merged_body );
|
||||
$headers = [
|
||||
'access-token' => $connect_info['access_token'],
|
||||
'app' => 'library',
|
||||
'client-id' => $connect_info['client_id'],
|
||||
'endpoint' => 'taxonomies',
|
||||
'home-url' => $connect_info['home_url'],
|
||||
'local-id' => $connect_info['local_id'],
|
||||
'site-key' => $this->get_site_key(),
|
||||
'X-Elementor-Signature' => $signature,
|
||||
];
|
||||
$response = wp_remote_post( $this->get_api_url(), [
|
||||
'headers' => $headers,
|
||||
'body' => $body,
|
||||
]);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?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' );
|
||||
$user_id = $this->get_user_id();
|
||||
$user_roles = $this->get_user_roles();
|
||||
$user = $this->get( 'user' );
|
||||
|
||||
return array_replace_recursive( $settings, [
|
||||
'library_connect' => [
|
||||
'is_connected' => $is_connected,
|
||||
'user_id' => $user_id,
|
||||
'user_roles' => $user_roles,
|
||||
'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,
|
||||
'plan_type' => ConnectModule::ACCESS_TIER_FREE,
|
||||
'user_email' => $user->email ?? null,
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
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' ] );
|
||||
}
|
||||
|
||||
private function get_user_id() {
|
||||
$token = $this->get( 'access_token' );
|
||||
|
||||
if ( ! is_string( $token ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parts = explode( '.', $token );
|
||||
|
||||
if ( count( $parts ) !== 3 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$payload_encoded = $parts[1];
|
||||
|
||||
$payload_encoded = str_pad( $payload_encoded, strlen( $payload_encoded ) + ( 4 - strlen( $payload_encoded ) % 4 ) % 4, '=' );
|
||||
|
||||
$payload_json = base64_decode( strtr( $payload_encoded, '-_', '+/' ), true );
|
||||
|
||||
$payload = json_decode( $payload_json, true );
|
||||
|
||||
if ( ! isset( $payload['sub'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $payload['sub'];
|
||||
} catch ( Exception $e ) {
|
||||
error_log( 'JWT Decoding Error: ' . $e->getMessage() );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function get_user_roles() {
|
||||
$user = wp_get_current_user();
|
||||
return $user->roles ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_popup_success_event_data() {
|
||||
return [
|
||||
'access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
'plan_type' => ConnectModule::ACCESS_TIER_FREE,
|
||||
'tracking_opted_in' => $this->get( 'data_share_opted_in' ) ?? false,
|
||||
'user_id' => $this->get_user_id(),
|
||||
];
|
||||
}
|
||||
|
||||
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,271 @@
|
||||
<?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\Feedback;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
use WP_User_Query;
|
||||
use Elementor\Core\Common\Modules\Connect\Rest\Rest_Api;
|
||||
|
||||
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';
|
||||
const ACCESS_TIER_PRO_LEGACY = 'pro';
|
||||
|
||||
/**
|
||||
* @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(),
|
||||
'feedback' => Feedback::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_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register app.
|
||||
*
|
||||
* Registers an app.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $slug App slug.
|
||||
* @param string $class_name App full class name.
|
||||
*
|
||||
* @return self The updated apps manager instance.
|
||||
*/
|
||||
public function register_app( $slug, $class_name ) {
|
||||
$this->registered_apps[ $slug ] = $class_name;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function register_rest_routes() {
|
||||
$rest_api = new Rest_Api();
|
||||
$rest_api->register_routes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
# Elementor Library Connect REST API
|
||||
|
||||
This module provides REST API endpoints for connecting and disconnecting your WordPress site to the Elementor Library, similar in purpose to the [Elementor CLI Library Connect command](https://developers.elementor.com/docs/cli/library-connect/).
|
||||
|
||||
## Overview
|
||||
|
||||
The REST API allows programmatic connection and disconnection to the Elementor Library, which is useful for automation, integrations, and testing.
|
||||
**Note:** The REST API is intended for internal and advanced use, mirroring the functionality of the CLI command.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### 1. Connect to Elementor Library
|
||||
|
||||
- **URL:** `/index.php?rest_route=/elementor/v1/library/connect`
|
||||
- **Method:** `POST`
|
||||
- **Permissions:** Requires the `manage_options` capability (typically administrators).
|
||||
- **Body Parameters:**
|
||||
- `token` (string, required): The connect token from your Elementor account dashboard.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```http
|
||||
POST /index.php?rest_route=/elementor/v1/library/connect
|
||||
Content-Type: application/json
|
||||
Authorization: Basic {{encoded_wp_credentials}}
|
||||
|
||||
{
|
||||
"token": "YOUR_CLI_TOKEN"
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Connected successfully."
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "elementor_library_not_connected",
|
||||
"message": "Failed to connect to Elementor Library.",
|
||||
"data": {
|
||||
"status": 500
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Disconnect from Elementor Library
|
||||
|
||||
- **URL:** `/index.php?rest_route=/elementor/v1/library/connect`
|
||||
- **Method:** `DELETE`
|
||||
- **Permissions:** Requires the `manage_options` capability.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```http
|
||||
DELETE /index.php?rest_route=/elementor/v1/library/connect
|
||||
Authorization: Basic {{encoded_wp_credentials}}
|
||||
```
|
||||
|
||||
#### Example Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Disconnected successfully."
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "elementor_library_disconnect_error",
|
||||
"message": "Error message here",
|
||||
"data": {
|
||||
"status": 500
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Permissions
|
||||
|
||||
All endpoints require the user to have the `manage_options` capability.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Errors are returned as standard WordPress REST API error objects, with a `code`, `message`, and HTTP status.
|
||||
|
||||
## Reference
|
||||
|
||||
- For CLI usage and more context, see the [Elementor CLI Library Connect documentation](https://developers.elementor.com/docs/cli/library-connect/).
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Rest;
|
||||
|
||||
use Elementor\Plugin;
|
||||
use WP_Http;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor Library Connect REST API.
|
||||
*
|
||||
* REST API controller for handling library connect operations.
|
||||
*/
|
||||
class Rest_Api {
|
||||
|
||||
/**
|
||||
* REST API namespace.
|
||||
*/
|
||||
const REST_NAMESPACE = 'elementor/v1';
|
||||
|
||||
/**
|
||||
* REST API base.
|
||||
*/
|
||||
const REST_BASE = 'library';
|
||||
|
||||
/**
|
||||
* Authentication mode.
|
||||
*/
|
||||
const AUTH_MODE = 'rest';
|
||||
|
||||
/**
|
||||
* Register REST API routes.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route(
|
||||
self::REST_NAMESPACE,
|
||||
self::REST_BASE . '/connect',
|
||||
[
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [ $this, 'connect' ],
|
||||
'permission_callback' => [ $this, 'connect_permissions_check' ],
|
||||
'args' => [
|
||||
'token' => [
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'description' => 'Connect CLI token',
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
register_rest_route(
|
||||
self::REST_NAMESPACE,
|
||||
self::REST_BASE . '/connect',
|
||||
[
|
||||
[
|
||||
'methods' => \WP_REST_Server::DELETABLE,
|
||||
'callback' => [ $this, 'disconnect' ],
|
||||
'permission_callback' => [ $this, 'connect_permissions_check' ],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
public function connect( \WP_REST_Request $request ) {
|
||||
$app = $this->get_connect_app();
|
||||
if ( ! $app ) {
|
||||
return $this->elementor_library_app_not_available();
|
||||
}
|
||||
|
||||
$app->set_auth_mode( self::AUTH_MODE );
|
||||
$_REQUEST['mode'] = self::AUTH_MODE;
|
||||
$_REQUEST['token'] = $request->get_param( 'token' );
|
||||
|
||||
try {
|
||||
$app->action_authorize();
|
||||
$app->action_get_token();
|
||||
|
||||
if ( $app->is_connected() ) {
|
||||
return $this->success_response(
|
||||
[ 'message' => __( 'Connected successfully.', 'elementor' ) ],
|
||||
WP_Http::CREATED );
|
||||
} else {
|
||||
return $this->error_response(
|
||||
'elementor_library_not_connected',
|
||||
__( 'Failed to connect to Elementor Library.', 'elementor' ),
|
||||
WP_Http::INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
return $this->error_response(
|
||||
'elementor_library_connect_error',
|
||||
$e->getMessage(),
|
||||
WP_Http::INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function disconnect( \WP_REST_Request $request ) {
|
||||
$app = $this->get_connect_app();
|
||||
if ( ! $app ) {
|
||||
return $this->elementor_library_app_not_available();
|
||||
}
|
||||
|
||||
$app->set_auth_mode( self::AUTH_MODE );
|
||||
$_REQUEST['mode'] = self::AUTH_MODE;
|
||||
|
||||
try {
|
||||
$app->action_disconnect();
|
||||
return $this->success_response(
|
||||
[ 'message' => __( 'Disconnected successfully.', 'elementor' ) ],
|
||||
WP_Http::OK
|
||||
);
|
||||
} catch ( \Exception $e ) {
|
||||
return $this->error_response(
|
||||
'elementor_library_disconnect_error',
|
||||
$e->getMessage(),
|
||||
WP_Http::INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function connect_permissions_check( \WP_REST_Request $request ) {
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
private function route_wrapper( callable $cb ) {
|
||||
try {
|
||||
$response = $cb();
|
||||
} catch ( \Exception $e ) {
|
||||
return $this->error_response(
|
||||
'unexpected_error',
|
||||
__( 'Something went wrong', 'elementor' ),
|
||||
WP_Http::INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function error_response( $code, $message, $status = WP_Http::BAD_REQUEST ) {
|
||||
return new \WP_Error(
|
||||
$code,
|
||||
$message,
|
||||
[ 'status' => $status ]
|
||||
);
|
||||
}
|
||||
|
||||
private function success_response( $data = [], $status = WP_Http::OK ) {
|
||||
$response = rest_ensure_response( array_merge( [ 'success' => true ], $data ) );
|
||||
$response->set_status( $status );
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function elementor_library_app_not_available() {
|
||||
return $this->error_response(
|
||||
'elementor_library_app_not_available',
|
||||
__( 'Elementor Library app is not available.', 'elementor' ),
|
||||
WP_Http::INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connect app.
|
||||
*
|
||||
* @return \Elementor\Core\Common\Modules\Connect\Apps\Library|null
|
||||
*/
|
||||
public function get_connect_app() {
|
||||
$connect = Plugin::$instance->common->get_component( 'connect' );
|
||||
if ( ! $connect ) {
|
||||
return null;
|
||||
}
|
||||
$app = $connect->get_app( 'library' );
|
||||
if ( ! $app ) {
|
||||
$connect->init();
|
||||
$app = $connect->get_app( 'library' );
|
||||
}
|
||||
return $app;
|
||||
}
|
||||
}
|
||||
@@ -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'] = wp_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,109 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\EventsManager;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Base_App;
|
||||
use Elementor\Core\Experiments\Manager as Experiments_Manager;
|
||||
use Elementor\Utils;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tracker;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Module extends BaseModule {
|
||||
|
||||
const EXPERIMENT_NAME = 'editor_events';
|
||||
|
||||
const REMOTE_MIXPANEL_CONFIG_URL = 'https://assets.elementor.com/mixpanel/v1/mixpanel.json';
|
||||
|
||||
public function get_name() {
|
||||
return 'events-manager';
|
||||
}
|
||||
|
||||
public static function get_editor_events_config() {
|
||||
$can_send_events = ! empty( ELEMENTOR_EDITOR_EVENTS_MIXPANEL_TOKEN ) &&
|
||||
Tracker::is_allow_track() &&
|
||||
! Tracker::has_terms_changed( '2025-07-07' ) &&
|
||||
Plugin::$instance->experiments->is_feature_active( self::EXPERIMENT_NAME );
|
||||
|
||||
$session_replays = [];
|
||||
$is_flags_enabled = false;
|
||||
|
||||
if ( $can_send_events ) {
|
||||
$mixpanel_config = self::get_remote_mixpanel_config();
|
||||
$session_replays = $mixpanel_config[0]['sessionReplays'] ?? [];
|
||||
$is_flags_enabled = $mixpanel_config[0]['flags'] ?? false;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'can_send_events' => $can_send_events,
|
||||
'elementor_version' => ELEMENTOR_VERSION,
|
||||
'site_url' => hash( 'sha256', get_site_url() ),
|
||||
'wp_version' => get_bloginfo( 'version' ),
|
||||
'user_agent' => esc_html( Utils::get_super_global_value( $_SERVER, 'HTTP_USER_AGENT' ) ),
|
||||
'site_language' => get_locale(),
|
||||
'site_key' => get_option( Base_App::OPTION_CONNECT_SITE_KEY ),
|
||||
'subscription_id' => self::get_subscription_id(),
|
||||
'subscription' => self::get_subscription(),
|
||||
'token' => ELEMENTOR_EDITOR_EVENTS_MIXPANEL_TOKEN,
|
||||
'session_replays' => $session_replays,
|
||||
'flags_enabled' => $is_flags_enabled,
|
||||
'isEditorOneActive' => Plugin::$instance->experiments->is_feature_active( 'e_editor_one' ),
|
||||
];
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
public static function get_experimental_data(): array {
|
||||
return [
|
||||
'name' => static::EXPERIMENT_NAME,
|
||||
'title' => esc_html__( 'Elementor Editor Events', 'elementor' ),
|
||||
'description' => esc_html__( 'Editor events processing', 'elementor' ),
|
||||
'hidden' => true,
|
||||
'release_status' => Experiments_Manager::RELEASE_STATUS_ALPHA,
|
||||
'default' => Experiments_Manager::STATE_INACTIVE,
|
||||
'new_site' => [
|
||||
'default_active' => true,
|
||||
'minimum_installation_version' => '3.32.0',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private static function get_subscription_id() {
|
||||
$subscription = self::get_subscription();
|
||||
|
||||
return $subscription['subscription_id'] ?? null;
|
||||
}
|
||||
|
||||
private static function get_subscription() {
|
||||
if ( ! Utils::has_pro() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$license_data = get_option( '_elementor_pro_license_v2_data' );
|
||||
if ( ! isset( $license_data['value'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return json_decode( $license_data['value'], true );
|
||||
}
|
||||
|
||||
private static function get_remote_mixpanel_config() {
|
||||
$data = wp_remote_get( static::REMOTE_MIXPANEL_CONFIG_URL );
|
||||
|
||||
if ( is_wp_error( $data ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = json_decode( wp_remote_retrieve_body( $data ), true );
|
||||
|
||||
if ( empty( $data['mixpanel'] ) || ! is_array( $data['mixpanel'] ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $data['mixpanel'];
|
||||
}
|
||||
}
|
||||
@@ -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,119 @@
|
||||
<?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(
|
||||
/* translators: %s: Post type singular name. */
|
||||
__( '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__( 'Website Templates', 'elementor' ),
|
||||
'icon' => 'kit-parts',
|
||||
'url' => Plugin::$instance->app->get_base_url() . '&source=finder#/kit-library',
|
||||
'keywords' => [ 'Website Templates', 'kit library', 'kit', 'library', 'site parts', 'parts', 'assets', 'templates' ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?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' ],
|
||||
],
|
||||
'integrations' => [
|
||||
'title' => esc_html__( 'Integrations', 'elementor' ),
|
||||
'url' => ElementorSettings::get_settings_tab_url( 'integrations' ),
|
||||
'keywords' => [ 'integrations', 'settings', 'elementor' ],
|
||||
],
|
||||
'advanced' => [
|
||||
'title' => esc_html__( 'Advanced', 'elementor' ),
|
||||
'url' => ElementorSettings::get_settings_tab_url( 'advanced' ),
|
||||
'keywords' => [ 'advanced', 'settings', 'elementor' ],
|
||||
],
|
||||
'performance' => [
|
||||
'title' => esc_html__( 'Performance', 'elementor' ),
|
||||
'url' => ElementorSettings::get_settings_tab_url( 'performance' ),
|
||||
'keywords' => [ 'performance', '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,132 @@
|
||||
<?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
|
||||
*
|
||||
* @throws \Exception If finder category registration fails or validation errors occur.
|
||||
*/
|
||||
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>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Database;
|
||||
|
||||
use Elementor\Core\Utils\Collection;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Base_Database_Updater {
|
||||
public function up( $force = false ) {
|
||||
$installed_version = $this->get_installed_version();
|
||||
|
||||
if ( ! $force && $this->get_db_version() <= $installed_version ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$migrations = new Collection( $this->get_migrations() );
|
||||
|
||||
if ( ! $force ) {
|
||||
$migrations = $migrations->filter( function ( $_, $version ) use ( $installed_version ) {
|
||||
return $version > $installed_version;
|
||||
} );
|
||||
}
|
||||
|
||||
$migrations->map( function ( Base_Migration $migration, $version ) {
|
||||
$migration->up();
|
||||
|
||||
$this->update_db_version_option( $version );
|
||||
} );
|
||||
|
||||
$this->update_db_version_option( $this->get_db_version() );
|
||||
}
|
||||
|
||||
public function register() {
|
||||
add_action( 'admin_init', function () {
|
||||
$this->up();
|
||||
} );
|
||||
}
|
||||
|
||||
protected function update_db_version_option( $version ) {
|
||||
update_option( $this->get_db_version_option_name(), $version );
|
||||
}
|
||||
|
||||
protected function get_installed_version() {
|
||||
return intval( get_option( $this->get_db_version_option_name() ) );
|
||||
}
|
||||
|
||||
abstract protected function get_db_version();
|
||||
|
||||
abstract protected function get_db_version_option_name(): string;
|
||||
|
||||
abstract protected function get_migrations(): array;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Database;
|
||||
|
||||
abstract class Base_Migration {
|
||||
/**
|
||||
* Runs when upgrading the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function up();
|
||||
}
|
||||
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,25 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Debug\Classes;
|
||||
|
||||
class Shop_Page_Edit extends Inspection_Base {
|
||||
|
||||
public function run() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_name() {
|
||||
return 'shop-page-edit';
|
||||
}
|
||||
|
||||
public function get_message() {
|
||||
return esc_html__( 'You are trying to edit the Shop Page although it is a Product Archive. Use the Theme Builder to create your Shop Archive template instead', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_help_doc_url() {
|
||||
return 'https://elementor.com/help/the-content-area-was-not-found-error/#error-appears-on-woocommerce-pages';
|
||||
}
|
||||
|
||||
public function get_header_message() {
|
||||
return esc_html__( 'Sorry, The content area was not been found on your page', 'elementor' );
|
||||
}
|
||||
}
|
||||
@@ -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,61 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Debug;
|
||||
|
||||
use Elementor\Core\Debug\Classes\Inspection_Base;
|
||||
use Elementor\Core\Debug\Classes\Shop_Page_Edit;
|
||||
use Elementor\Core\Debug\Classes\Theme_Missing;
|
||||
use Elementor\Core\Debug\Classes\Htaccess;
|
||||
use Elementor\Utils;
|
||||
|
||||
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();
|
||||
|
||||
$is_editing_shop_page = Utils::get_super_global_value( $_GET, 'post' ) == get_option( 'woocommerce_shop_page_id' );
|
||||
if ( $is_editing_shop_page ) {
|
||||
$this->inspections['shop-page-edit'] = new Shop_Page_Edit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
332
wp-content/plugins/elementor/core/document-types/page-base.php
Normal file
332
wp-content/plugins/elementor/core/document-types/page-base.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?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(
|
||||
/* translators: 1: Plural label. */
|
||||
__( 'All %s', 'elementor' ),
|
||||
$plural_label
|
||||
),
|
||||
'add_new' => esc_html__( 'Add New', 'elementor' ),
|
||||
'add_new_item' => sprintf(
|
||||
/* translators: %s: Singular label. */
|
||||
__( 'Add New %s', 'elementor' ),
|
||||
$singular_label
|
||||
),
|
||||
'edit_item' => sprintf(
|
||||
/* translators: %s: Singular label. */
|
||||
__( 'Edit %s', 'elementor' ),
|
||||
$singular_label
|
||||
),
|
||||
'new_item' => sprintf(
|
||||
/* translators: %s: Singular label. */
|
||||
__( 'New %s', 'elementor' ),
|
||||
$singular_label
|
||||
),
|
||||
'view_item' => sprintf(
|
||||
/* translators: %s: Singular label. */
|
||||
__( 'View %s', 'elementor' ),
|
||||
$singular_label
|
||||
),
|
||||
'search_items' => sprintf(
|
||||
/* translators: %s: Plural label. */
|
||||
__( 'Search %s', 'elementor' ),
|
||||
$plural_label
|
||||
),
|
||||
'not_found' => sprintf(
|
||||
/* translators: %s: Plural label. */
|
||||
__( 'No %s found.', 'elementor' ),
|
||||
strtolower( $plural_label )
|
||||
),
|
||||
'not_found_in_trash' => sprintf(
|
||||
/* translators: %s: Plural label. */
|
||||
__( '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 If the post ID is not set.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
123
wp-content/plugins/elementor/core/document-types/page.php
Normal file
123
wp-content/plugins/elementor/core/document-types/page.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
namespace Elementor\Core\DocumentTypes;
|
||||
|
||||
use Elementor\Core\Base\Document;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Page extends PageBase {
|
||||
|
||||
const URL_TYPE = 'site_settings';
|
||||
|
||||
const SITE_IDENTITY_TAB = 'settings-site-identity';
|
||||
|
||||
/**
|
||||
* 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' );
|
||||
}
|
||||
|
||||
public static function get_site_settings_url_config( $active_tab_id = null ) {
|
||||
$existing_elementor_page = self::get_elementor_page();
|
||||
$site_settings_url = $existing_elementor_page
|
||||
? self::get_elementor_edit_url( $existing_elementor_page->ID, [ 'active-tab' => $active_tab_id ] )
|
||||
: self::get_create_new_editor_page_url( $active_tab_id );
|
||||
|
||||
return [
|
||||
'new_page' => empty( $existing_elementor_page ),
|
||||
'url' => $site_settings_url,
|
||||
'type' => static::URL_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
public static function get_create_new_editor_page_url( $active_tab = null ): string {
|
||||
$active_kit_id = Plugin::$instance->kits_manager->get_active_id();
|
||||
$args = [];
|
||||
|
||||
if ( ! empty( $active_kit_id ) ) {
|
||||
$args['active-document'] = $active_kit_id;
|
||||
}
|
||||
|
||||
if ( $active_tab ) {
|
||||
$args['active-tab'] = $active_tab;
|
||||
}
|
||||
|
||||
return add_query_arg( $args, Plugin::$instance->documents->get_create_new_post_url( 'page' ) );
|
||||
}
|
||||
|
||||
private static function get_elementor_edit_url( int $post_id, $args = [] ): string {
|
||||
$page = new self( [ 'post_id' => $post_id ] );
|
||||
$url = add_query_arg( $args, $page->get_edit_url() );
|
||||
|
||||
if ( Plugin::$instance->kits_manager->get_active_id() ) {
|
||||
return $url . '#e:run:panel/global/open';
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public static function get_elementor_page() {
|
||||
return get_pages( [
|
||||
'post_status' => [ 'publish', 'draft' ],
|
||||
'meta_key' => Document::BUILT_WITH_ELEMENTOR_META_KEY,
|
||||
'sort_order' => 'asc',
|
||||
'sort_column' => 'post_date',
|
||||
'number' => 1,
|
||||
] )[0] ?? null;
|
||||
}
|
||||
}
|
||||
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' );
|
||||
}
|
||||
}
|
||||
821
wp-content/plugins/elementor/core/documents-manager.php
Normal file
821
wp-content/plugins/elementor/core/documents-manager.php
Normal file
@@ -0,0 +1,821 @@
|
||||
<?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' ] );
|
||||
add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_name 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_name ) {
|
||||
$this->types[ $type ] = $class_name;
|
||||
|
||||
$cpt = $class_name::get_property( 'cpt' );
|
||||
|
||||
if ( $cpt ) {
|
||||
foreach ( $cpt as $post_type ) {
|
||||
$this->cpt[ $post_type ] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $class_name::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.
|
||||
*
|
||||
* @param string $id
|
||||
* @return Document
|
||||
* @throws \Exception If the document is not found or the current user is not allowed to edit it.
|
||||
* @since 3.13.0
|
||||
*/
|
||||
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 string $id
|
||||
* @return void
|
||||
* @throws \Exception If the document is not found or the current user is not allowed to edit it.
|
||||
*/
|
||||
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();
|
||||
$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 array $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 array $request
|
||||
*
|
||||
* @return bool True if changes discarded, False otherwise.
|
||||
* @throws \Exception If current user don't have permissions to edit the post or the post is not using Elementor.
|
||||
*
|
||||
* @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';
|
||||
}
|
||||
|
||||
public function register_rest_routes() {
|
||||
register_rest_route('elementor/v1/documents', '/(?P<id>\d+)/media/import', [
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => function( $request ) {
|
||||
$post_id = $request->get_param( 'id' );
|
||||
|
||||
try {
|
||||
$document = $this->get_with_permissions( $post_id );
|
||||
|
||||
$elements_data = $document->get_elements_data();
|
||||
|
||||
$import_data = $document->get_import_data( [
|
||||
'content' => $elements_data,
|
||||
] );
|
||||
|
||||
$document->save( [
|
||||
'elements' => $import_data['content'],
|
||||
] );
|
||||
|
||||
return new \WP_REST_Response( [
|
||||
'success' => true,
|
||||
'document_saved' => true,
|
||||
], 200 );
|
||||
|
||||
} catch ( \Exception $e ) {
|
||||
return new \WP_Error(
|
||||
'elementor_import_error',
|
||||
$e->getMessage(),
|
||||
[ 'status' => 500 ]
|
||||
);
|
||||
}
|
||||
},
|
||||
'permission_callback' => function() {
|
||||
return current_user_can( 'manage_options' );
|
||||
},
|
||||
'args' => [
|
||||
'id' => [
|
||||
'required' => true,
|
||||
'validate_callback' => function( $param ) {
|
||||
return is_numeric( $param );
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
196
wp-content/plugins/elementor/core/dynamic-tags/base-tag.php
Normal file
196
wp-content/plugins/elementor/core/dynamic-tags/base-tag.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?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();
|
||||
|
||||
public function get_atomic_group() {
|
||||
return $this->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(),
|
||||
'atomic_group' => $this->get_atomic_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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
537
wp-content/plugins/elementor/core/dynamic-tags/manager.php
Normal file
537
wp-content/plugins/elementor/core/dynamic-tags/manager.php
Normal file
@@ -0,0 +1,537 @@
|
||||
<?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__';
|
||||
|
||||
const CONTROL_OPTION_KEYS = [ 'id', 'label' ];
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
|
||||
private function normalize_settings( $value ) {
|
||||
if ( $this->is_typed_value_wrapper( $value ) ) {
|
||||
return $this->normalize_settings( $value['value'] );
|
||||
}
|
||||
|
||||
if ( $this->is_id_label_option( $value ) ) {
|
||||
return $this->normalize_settings( $value['id'] );
|
||||
}
|
||||
|
||||
if ( is_array( $value ) ) {
|
||||
foreach ( $value as $k => $v ) {
|
||||
$value[ $k ] = $this->normalize_settings( $v );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( is_object( $value ) ) {
|
||||
return $this->normalize_settings( get_object_vars( $value ) );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function is_typed_value_wrapper( $value ) {
|
||||
return is_array( $value ) && isset( $value['$$type'], $value['value'] );
|
||||
}
|
||||
|
||||
private function is_id_label_option( $value ) {
|
||||
if ( ! is_array( $value ) || ! array_key_exists( 'id', $value ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$keys = array_keys( $value );
|
||||
|
||||
return empty( array_diff( $keys, self::CONTROL_OPTION_KEYS ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @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' => $this->normalize_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, $this->normalize_settings( $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_name
|
||||
*/
|
||||
public function register_tag( $class_name ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
|
||||
__METHOD__,
|
||||
'3.5.0',
|
||||
'register()'
|
||||
);
|
||||
|
||||
/** @var Base_Tag $tag */
|
||||
$instance = new $class_name();
|
||||
|
||||
$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 or 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 If the item is not found.
|
||||
*/
|
||||
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' => sanitize_text_field( $item['title'] ?? '' ),
|
||||
'color' => sanitize_text_field( $item['value'] ?? '' ),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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' => sanitize_text_field( $item['title'] ?? '' ),
|
||||
];
|
||||
|
||||
$value = $item['value'];
|
||||
|
||||
unset( $value['_id'], $value['title'] );
|
||||
|
||||
foreach ( $value as $key => $dirty_value ) {
|
||||
$db_format[ $key ] = is_string( $dirty_value ) ? sanitize_text_field( $dirty_value ) : $dirty_value ?? '';
|
||||
}
|
||||
|
||||
return $db_format;
|
||||
}
|
||||
}
|
||||
663
wp-content/plugins/elementor/core/editor/editor.php
Normal file
663
wp-content/plugins/elementor/core/editor/editor.php
Normal file
@@ -0,0 +1,663 @@
|
||||
<?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\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';
|
||||
|
||||
/**
|
||||
* 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 $to_die Optional. Whether to die at the end. Default is `true`.
|
||||
*/
|
||||
public function init( $to_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 !== $to_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' ] );
|
||||
|
||||
// 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' ] );
|
||||
|
||||
add_filter( 'replace_editor', [ $this, 'filter_replace_editor' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals to WordPress that Elementor is replacing the block editor on its own editor page,
|
||||
* so that block-editor-specific behaviour (e.g. WP 7.0 COOP/COEP isolation headers) is not
|
||||
* applied when the Elementor editor is active.
|
||||
*
|
||||
* @param bool $replace Whether the editor is being replaced.
|
||||
* @param \WP_Post $post The post being edited.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function filter_replace_editor( $replace, $post ) {
|
||||
if ( isset( $_REQUEST['action'] ) && 'elementor' === $_REQUEST['action'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return true;
|
||||
}
|
||||
|
||||
return $replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,264 @@
|
||||
<?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\Group_Control_Typography;
|
||||
use Elementor\Icons_Manager;
|
||||
use Elementor\Modules\Apps\Module as AppsModule;
|
||||
use Elementor\Core\Common\Modules\EventsManager\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\Hints;
|
||||
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' );
|
||||
$top_bar_connect_app = Plugin::$instance->common->get_component( 'connect' )->get_app( 'activate' ) ?? Plugin::$instance->common->get_component( 'connect' )->get_app( 'library' );
|
||||
|
||||
$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(),
|
||||
'top_bar' => [
|
||||
'connect_url' => $top_bar_connect_app->get_admin_url( 'authorize', [
|
||||
'utm_source' => 'editor-app',
|
||||
'utm_campaign' => 'connect-account',
|
||||
'utm_medium' => 'wp-dash',
|
||||
'utm_term' => '1.0.0',
|
||||
'utm_content' => 'cta-link',
|
||||
'source' => 'generic',
|
||||
'mode' => 'popup',
|
||||
] ),
|
||||
'my_elementor_url' => 'https://go.elementor.com/wp-dash-top-bar-account/',
|
||||
],
|
||||
],
|
||||
'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(),
|
||||
'integration' => [
|
||||
'ally-accessibility' => Hints::get_ally_action_data(),
|
||||
],
|
||||
],
|
||||
'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'
|
||||
),
|
||||
],
|
||||
'fontVariableRanges' => Group_Control_Typography::get_font_variable_ranges(),
|
||||
];
|
||||
|
||||
if ( Plugin::$instance->experiments->is_feature_active( 'container' ) ) {
|
||||
$client_env['elementsPresets'] = Plugin::$instance->editor->get_elements_presets();
|
||||
}
|
||||
|
||||
$is_admin_user_without_pro = current_user_can( 'manage_options' ) && ! Utils::has_pro();
|
||||
if ( $is_admin_user_without_pro ) {
|
||||
$client_env['integrationWidgets'] = array_merge(
|
||||
( isset( $client_env['integrationWidgets'] ) && is_array( $client_env['integrationWidgets'] ) ?
|
||||
$client_env['integrationWidgets'] :
|
||||
[] ), [
|
||||
[
|
||||
'categories' => '[ "general" ]',
|
||||
'icon' => 'eicon-accessibility',
|
||||
'name' => 'ally-accessibility',
|
||||
'title' => esc_html__( 'Ally Accessibility', 'elementor' ),
|
||||
'keywords' => [
|
||||
'Accessibility',
|
||||
'Usability',
|
||||
'Inclusive',
|
||||
'Statement',
|
||||
'WCAG',
|
||||
'Ally',
|
||||
'Complaince',
|
||||
],
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
$client_env = apply_filters( 'elementor/editor/localize_settings', $client_env );
|
||||
|
||||
if ( $is_admin_user_without_pro ) {
|
||||
$client_env = self::ensure_pro_widgets( $client_env );
|
||||
}
|
||||
|
||||
if ( ! empty( $client_env['promotionWidgets'] ) && is_array( $client_env['promotionWidgets'] ) ) {
|
||||
$client_env['promotionWidgets'] = self::ensure_numeric_keys( $client_env['promotionWidgets'] );
|
||||
}
|
||||
|
||||
return $client_env;
|
||||
}
|
||||
|
||||
private static function ensure_pro_widgets( array $client_env ) {
|
||||
$pro_widgets = Api::get_promotion_widgets();
|
||||
if ( ! isset( $client_env['promotionWidgets'] ) ) {
|
||||
$client_env['promotionWidgets'] = $pro_widgets;
|
||||
} else {
|
||||
$client_env['promotionWidgets'] = array_merge( $pro_widgets, $client_env['promotionWidgets'] );
|
||||
}
|
||||
return $client_env;
|
||||
}
|
||||
|
||||
private static function ensure_numeric_keys( array $base_array ) {
|
||||
return array_values( $base_array );
|
||||
}
|
||||
|
||||
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,288 @@
|
||||
<?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
|
||||
);
|
||||
|
||||
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' ],
|
||||
'4.6.13',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'ace',
|
||||
'https://cdn.jsdelivr.net/npm/ace-builds@1.43.2/src-min-noconflict/ace.min.js',
|
||||
[],
|
||||
'1.43.2',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'ace-language-tools',
|
||||
'https://cdn.jsdelivr.net/npm/ace-builds@1.43.2/src-min-noconflict/ext-language_tools.js',
|
||||
[ 'ace' ],
|
||||
'1.43.2',
|
||||
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.8.2',
|
||||
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' );
|
||||
|
||||
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' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
wp_enqueue_script( 'elementor-responsive-bar' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @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",
|
||||
[],
|
||||
'4.6.13'
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
'pickr',
|
||||
"{$assets_url}lib/pickr/themes/monolith.min.css",
|
||||
[],
|
||||
'1.8.2'
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
wp_register_style(
|
||||
'elementor-responsive-bar',
|
||||
"{$assets_url}css/responsive-bar{$min_suffix}.css",
|
||||
[],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_styles() {
|
||||
wp_enqueue_style( 'elementor-editor' );
|
||||
|
||||
wp_enqueue_style( 'elementor-responsive-bar' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_additional_templates() {
|
||||
$templates = [
|
||||
'global',
|
||||
'panel',
|
||||
'panel-elements',
|
||||
'repeater',
|
||||
'templates',
|
||||
'navigator',
|
||||
'hotkeys',
|
||||
'responsive-bar',
|
||||
];
|
||||
|
||||
$templates = apply_filters( 'elementor/editor/templates', $templates );
|
||||
|
||||
foreach ( $templates as $template ) {
|
||||
Plugin::$instance->common->add_template( ELEMENTOR_PATH . "includes/editor-templates/{$template}.php" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?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() {
|
||||
$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 ( static::should_use_v2_loader() ) {
|
||||
return new Editor_V2_Loader( $config, $assets_config_provider );
|
||||
}
|
||||
|
||||
return new Editor_V1_Loader( $config, $assets_config_provider );
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are v2 packages enqueued, we should use the V2 loader.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function should_use_v2_loader() {
|
||||
return ! empty( Editor_V2_Loader::get_packages_to_enqueue() );
|
||||
}
|
||||
}
|
||||
@@ -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,88 @@
|
||||
<?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-editor-loader-v1',
|
||||
"{$assets_url}js/editor-loader-v1{$min_suffix}.js",
|
||||
[ 'elementor-editor' ],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
parent::enqueue_scripts();
|
||||
|
||||
// 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 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();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user