first commit

This commit is contained in:
2026-03-26 13:48:22 +01:00
commit 6af83e92ed
7795 changed files with 2766332 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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' ] );
}
}

View File

@@ -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
);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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' ] );
}
}

View File

@@ -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 );
}
}
}
}

View 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;
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View 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 );
}
}

View File

@@ -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();
}

View 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 );
}
}