first commit
This commit is contained in:
290
wp-content/plugins/elementor/core/common/app.php
Normal file
290
wp-content/plugins/elementor/core/common/app.php
Normal file
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common;
|
||||
|
||||
use Elementor\Core\Base\App as BaseApp;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Core\Common\Modules\Finder\Module as Finder;
|
||||
use Elementor\Core\Common\Modules\Connect\Module as Connect;
|
||||
use Elementor\Core\Common\Modules\EventTracker\Module as Event_Tracker;
|
||||
use Elementor\Core\Files\Uploads_Manager;
|
||||
use Elementor\Core\Settings\Manager as SettingsManager;
|
||||
use Elementor\Icons_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* App
|
||||
*
|
||||
* Elementor's common app that groups shared functionality, components and configuration
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class App extends BaseApp {
|
||||
|
||||
private $templates = [];
|
||||
|
||||
/**
|
||||
* App constructor.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->add_default_templates();
|
||||
|
||||
add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'register_scripts' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] );
|
||||
|
||||
add_action( 'elementor/editor/before_enqueue_styles', [ $this, 'register_styles' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'register_styles' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'register_styles' ], 9 );
|
||||
|
||||
add_action( 'elementor/editor/footer', [ $this, 'print_templates' ] );
|
||||
add_action( 'admin_footer', [ $this, 'print_templates' ] );
|
||||
add_action( 'wp_footer', [ $this, 'print_templates' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Init components
|
||||
*
|
||||
* Initializing common components.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function init_components() {
|
||||
$this->add_component( 'ajax', new Ajax() );
|
||||
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
if ( ! is_customize_preview() ) {
|
||||
$this->add_component( 'finder', new Finder() );
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_component( 'connect', new Connect() );
|
||||
|
||||
$this->add_component( 'event-tracker', new Event_Tracker() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* Retrieve the app name.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string Common app name.
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'common';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register scripts.
|
||||
*
|
||||
* Register common scripts.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_scripts() {
|
||||
wp_register_script(
|
||||
'elementor-common-modules',
|
||||
$this->get_js_assets_url( 'common-modules' ),
|
||||
[],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'backbone-marionette',
|
||||
$this->get_js_assets_url( 'backbone.marionette', 'assets/lib/backbone/' ),
|
||||
[
|
||||
'backbone',
|
||||
],
|
||||
'2.4.5.e1',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'backbone-radio',
|
||||
$this->get_js_assets_url( 'backbone.radio', 'assets/lib/backbone/' ),
|
||||
[
|
||||
'backbone',
|
||||
],
|
||||
'1.0.4',
|
||||
true
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'elementor-dialog',
|
||||
$this->get_js_assets_url( 'dialog', 'assets/lib/dialog/' ),
|
||||
[
|
||||
'jquery-ui-position',
|
||||
],
|
||||
'4.9.0',
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'elementor-common',
|
||||
$this->get_js_assets_url( 'common' ),
|
||||
[
|
||||
'jquery',
|
||||
'jquery-ui-draggable',
|
||||
'backbone-marionette',
|
||||
'backbone-radio',
|
||||
'elementor-common-modules',
|
||||
'elementor-web-cli',
|
||||
'elementor-dialog',
|
||||
'wp-api-request',
|
||||
'elementor-dev-tools',
|
||||
],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_set_script_translations( 'elementor-common', 'elementor' );
|
||||
|
||||
$this->print_config();
|
||||
|
||||
// Used for external plugins.
|
||||
do_action( 'elementor/common/after_register_scripts', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register styles.
|
||||
*
|
||||
* Register common styles.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_styles() {
|
||||
wp_register_style(
|
||||
'elementor-icons',
|
||||
$this->get_css_assets_url( 'elementor-icons', 'assets/lib/eicons/css/' ),
|
||||
[],
|
||||
Icons_Manager::ELEMENTOR_ICONS_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'elementor-common',
|
||||
$this->get_css_assets_url( 'common', null, 'default', true ),
|
||||
[
|
||||
'elementor-icons',
|
||||
],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'e-theme-ui-light',
|
||||
$this->get_css_assets_url( 'theme-light' ),
|
||||
[],
|
||||
ELEMENTOR_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add template.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $template Can be either a link to template file or template
|
||||
* HTML content.
|
||||
* @param string $type Optional. Whether to handle the template as path
|
||||
* or text. Default is `path`.
|
||||
*/
|
||||
public function add_template( $template, $type = 'path' ) {
|
||||
if ( 'path' === $type ) {
|
||||
ob_start();
|
||||
|
||||
include $template;
|
||||
|
||||
$template = ob_get_clean();
|
||||
}
|
||||
|
||||
$this->templates[] = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print Templates
|
||||
*
|
||||
* Prints all registered templates.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function print_templates() {
|
||||
foreach ( $this->templates as $template ) {
|
||||
echo $template; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* Define the default/initial settings of the common app.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
$active_experimental_features = Plugin::$instance->experiments->get_active_features();
|
||||
|
||||
$active_experimental_features = array_fill_keys( array_keys( $active_experimental_features ), true );
|
||||
|
||||
$config = [
|
||||
'version' => ELEMENTOR_VERSION,
|
||||
'isRTL' => is_rtl(),
|
||||
'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
|
||||
'isElementorDebug' => ( defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG ),
|
||||
'activeModules' => array_keys( $this->get_components() ),
|
||||
'experimentalFeatures' => $active_experimental_features,
|
||||
'urls' => [
|
||||
'assets' => ELEMENTOR_ASSETS_URL,
|
||||
'rest' => get_rest_url(),
|
||||
],
|
||||
'filesUpload' => [
|
||||
'unfilteredFiles' => Uploads_Manager::are_unfiltered_uploads_enabled(),
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Localize common settings.
|
||||
*
|
||||
* Filters the editor localized settings.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $config Common configuration.
|
||||
*/
|
||||
return apply_filters( 'elementor/common/localize_settings', $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add default templates.
|
||||
*
|
||||
* Register common app default templates.
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*/
|
||||
private function add_default_templates() {
|
||||
$default_templates = [
|
||||
'includes/editor-templates/library-layout.php',
|
||||
];
|
||||
|
||||
foreach ( $default_templates as $template ) {
|
||||
$this->add_template( ELEMENTOR_PATH . $template );
|
||||
}
|
||||
}
|
||||
}
|
||||
325
wp-content/plugins/elementor/core/common/modules/ajax/module.php
Normal file
325
wp-content/plugins/elementor/core/common/modules/ajax/module.php
Normal file
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Ajax;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Utils\Exceptions;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor ajax manager.
|
||||
*
|
||||
* Elementor ajax manager handler class is responsible for handling Elementor
|
||||
* ajax requests, ajax responses and registering actions applied on them.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
const NONCE_KEY = 'elementor_ajax';
|
||||
|
||||
/**
|
||||
* Ajax actions.
|
||||
*
|
||||
* Holds all the register ajax action.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ajax_actions = [];
|
||||
|
||||
/**
|
||||
* Ajax requests.
|
||||
*
|
||||
* Holds all the register ajax requests.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $requests = [];
|
||||
|
||||
/**
|
||||
* Ajax response data.
|
||||
*
|
||||
* Holds all the response data for all the ajax requests.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $response_data = [];
|
||||
|
||||
/**
|
||||
* Current ajax action ID.
|
||||
*
|
||||
* Holds all the ID for the current ajax action.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access private
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $current_action_id = null;
|
||||
|
||||
/**
|
||||
* Ajax manager constructor.
|
||||
*
|
||||
* Initializing Elementor ajax manager.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_elementor_ajax', [ $this, 'handle_ajax_request' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module name.
|
||||
*
|
||||
* Retrieve the module name.
|
||||
*
|
||||
* @since 1.7.0
|
||||
* @access public
|
||||
*
|
||||
* @return string Module name.
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'ajax';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ajax action.
|
||||
*
|
||||
* Add new actions for a specific ajax request and the callback function to
|
||||
* be handle the response.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $tag Ajax request name/tag.
|
||||
* @param callable $callback The callback function.
|
||||
*/
|
||||
public function register_ajax_action( $tag, $callback ) {
|
||||
if ( ! did_action( 'elementor/ajax/register_actions' ) ) {
|
||||
_doing_it_wrong( __METHOD__, esc_html( sprintf( 'Use `%s` hook to register ajax action.', 'elementor/ajax/register_actions' ) ), '2.0.0' );
|
||||
}
|
||||
|
||||
$this->ajax_actions[ $tag ] = compact( 'tag', 'callback' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ajax request.
|
||||
*
|
||||
* Verify ajax nonce, and run all the registered actions for this request.
|
||||
*
|
||||
* Fired by `wp_ajax_elementor_ajax` action.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function handle_ajax_request() {
|
||||
if ( ! $this->verify_request_nonce() ) {
|
||||
$this->add_response_data( false, esc_html__( 'Token Expired.', 'elementor' ) )
|
||||
->send_error( Exceptions::UNAUTHORIZED );
|
||||
}
|
||||
|
||||
$editor_post_id = 0;
|
||||
|
||||
if ( ! empty( $_REQUEST['editor_post_id'] ) ) {
|
||||
$editor_post_id = absint( $_REQUEST['editor_post_id'] );
|
||||
|
||||
Plugin::$instance->db->switch_to_post( $editor_post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ajax actions.
|
||||
*
|
||||
* Fires when an ajax request is received and verified.
|
||||
*
|
||||
* Used to register new ajax action handles.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param self $this An instance of ajax manager.
|
||||
*/
|
||||
do_action( 'elementor/ajax/register_actions', $this );
|
||||
|
||||
if ( ! empty( $_REQUEST['actions'] ) ) {
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, each action should sanitize its own data.
|
||||
$this->requests = json_decode( wp_unslash( $_REQUEST['actions'] ), true );
|
||||
}
|
||||
|
||||
foreach ( $this->requests as $id => $action_data ) {
|
||||
$this->current_action_id = $id;
|
||||
|
||||
if ( ! isset( $this->ajax_actions[ $action_data['action'] ] ) ) {
|
||||
$this->add_response_data( false, esc_html__( 'Action not found.', 'elementor' ), Exceptions::BAD_REQUEST );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $editor_post_id ) {
|
||||
$action_data['data']['editor_post_id'] = $editor_post_id;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $action_data['data'] ?? [];
|
||||
$results = call_user_func( $this->ajax_actions[ $action_data['action'] ]['callback'], $data, $this );
|
||||
|
||||
if ( false === $results ) {
|
||||
$this->add_response_data( false );
|
||||
} else {
|
||||
$this->add_response_data( true, $results );
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
$this->add_response_data( false, $e->getMessage(), $e->getCode() );
|
||||
}
|
||||
}
|
||||
|
||||
$this->current_action_id = null;
|
||||
|
||||
$this->send_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current action data.
|
||||
*
|
||||
* Retrieve the data for the current ajax request.
|
||||
*
|
||||
* @since 2.0.1
|
||||
* @access public
|
||||
*
|
||||
* @return bool|mixed Ajax request data if action exist, False otherwise.
|
||||
*/
|
||||
public function get_current_action_data() {
|
||||
if ( ! $this->current_action_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->requests[ $this->current_action_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create nonce.
|
||||
*
|
||||
* Creates a cryptographic token to
|
||||
* give the user an access to Elementor ajax actions.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string The nonce token.
|
||||
*/
|
||||
public function create_nonce() {
|
||||
return wp_create_nonce( self::NONCE_KEY );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify request nonce.
|
||||
*
|
||||
* Whether the request nonce verified or not.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return bool True if request nonce verified, False otherwise.
|
||||
*/
|
||||
public function verify_request_nonce() {
|
||||
return wp_verify_nonce( Utils::get_super_global_value( $_REQUEST, '_nonce' ), self::NONCE_KEY );
|
||||
}
|
||||
|
||||
protected function get_init_settings() {
|
||||
return [
|
||||
'url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => $this->create_nonce(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax success response.
|
||||
*
|
||||
* Send a JSON response data back to the ajax request, indicating success.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*/
|
||||
private function send_success() {
|
||||
$response = [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'responses' => $this->response_data,
|
||||
],
|
||||
];
|
||||
|
||||
$json = wp_json_encode( $response );
|
||||
|
||||
while ( ob_get_status() ) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
if ( function_exists( 'gzencode' ) ) {
|
||||
$response = gzencode( $json );
|
||||
|
||||
header( 'Content-Type: application/json; charset=utf-8' );
|
||||
header( 'Content-Encoding: gzip' );
|
||||
header( 'Content-Length: ' . strlen( $response ) );
|
||||
|
||||
echo $response; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} else {
|
||||
echo $json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
wp_die( '', '', [ 'response' => null ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax failure response.
|
||||
*
|
||||
* Send a JSON response data back to the ajax request, indicating failure.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @param null $code
|
||||
*/
|
||||
private function send_error( $code = null ) {
|
||||
wp_send_json_error( [
|
||||
'responses' => $this->response_data,
|
||||
], $code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add response data.
|
||||
*
|
||||
* Add new response data to the array of all the ajax requests.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @access protected
|
||||
*
|
||||
* @param bool $success True if the requests returned successfully, False
|
||||
* otherwise.
|
||||
* @param mixed $data Optional. Response data. Default is null.
|
||||
*
|
||||
* @param int $code Optional. Response code. Default is 200.
|
||||
*
|
||||
* @return Module An instance of ajax manager.
|
||||
*/
|
||||
private function add_response_data( $success, $data = null, $code = 200 ) {
|
||||
$this->response_data[ $this->current_action_id ] = [
|
||||
'success' => $success,
|
||||
'code' => $code,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Admin_Menu_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Settings;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Admin {
|
||||
|
||||
const PAGE_ID = 'elementor-connect';
|
||||
|
||||
public static $url = '';
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_admin_menu( Admin_Menu_Manager $admin_menu ) {
|
||||
$admin_menu->register( static::PAGE_ID, new Connect_Menu_Item() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function on_load_page() {
|
||||
if ( isset( $_GET['action'], $_GET['app'] ) ) {
|
||||
$manager = Plugin::$instance->common->get_component( 'connect' );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$app_slug = Utils::get_super_global_value( $_GET, 'app' );
|
||||
$app = $manager->get_app( $app_slug );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$action = Utils::get_super_global_value( $_GET, 'action' );
|
||||
|
||||
$nonce_action = $app_slug . $action;
|
||||
|
||||
if ( ! $app ) {
|
||||
wp_die( 'Unknown app: ' . esc_attr( $app_slug ) );
|
||||
}
|
||||
|
||||
if ( ! wp_verify_nonce( Utils::get_super_global_value( $_GET, 'nonce' ), $nonce_action ) ) {
|
||||
wp_die( 'Invalid Nonce', 'Invalid Nonce', [
|
||||
'back_link' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
$method = 'action_' . $action;
|
||||
|
||||
if ( method_exists( $app, $method ) ) {
|
||||
call_user_func( [ $app, $method ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
self::$url = admin_url( 'admin.php?page=' . self::PAGE_ID );
|
||||
|
||||
add_action( 'elementor/admin/menu/register', [ $this, 'register_admin_menu' ] );
|
||||
|
||||
add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) {
|
||||
if ( ! empty( $hooks[ static::PAGE_ID ] ) ) {
|
||||
add_action( 'load-' . $hooks[ static::PAGE_ID ], [ $this, 'on_load_page' ] );
|
||||
}
|
||||
}, 10, 2 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
use Elementor\Core\Admin\Admin_Notices;
|
||||
use Elementor\Core\Common\Modules\Connect\Admin;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Core\Utils\Http;
|
||||
use Elementor\Core\Utils\Str;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tracker;
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base_App {
|
||||
|
||||
const OPTION_NAME_PREFIX = 'elementor_connect_';
|
||||
|
||||
const OPTION_CONNECT_SITE_KEY = self::OPTION_NAME_PREFIX . 'site_key';
|
||||
|
||||
const SITE_URL = 'https://my.elementor.com/connect/v1';
|
||||
|
||||
const API_URL = 'https://my.elementor.com/api/connect/v1';
|
||||
|
||||
const HTTP_RETURN_TYPE_OBJECT = 'object';
|
||||
const HTTP_RETURN_TYPE_ARRAY = 'array';
|
||||
|
||||
protected $data = [];
|
||||
|
||||
protected $auth_mode = '';
|
||||
|
||||
/**
|
||||
* @var Http
|
||||
*/
|
||||
protected $http;
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
* @abstract
|
||||
* TODO: make it public.
|
||||
*/
|
||||
abstract protected function get_slug();
|
||||
|
||||
/**
|
||||
* @since 2.8.0
|
||||
* @access public
|
||||
* TODO: make it abstract.
|
||||
*/
|
||||
public function get_title() {
|
||||
return $this->get_slug();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
* @abstract
|
||||
*/
|
||||
abstract protected function update_settings();
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function get_class_name() {
|
||||
return get_called_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* @access public
|
||||
* @abstract
|
||||
*/
|
||||
public function render_admin_widget() {
|
||||
// PHPCS - the method get_title return a plain string.
|
||||
echo '<h2>' . $this->get_title() . '</h2>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
if ( $this->is_connected() ) {
|
||||
$remote_user = $this->get( 'user' );
|
||||
$title = sprintf(
|
||||
/* translators: %s: Remote user. */
|
||||
esc_html__( 'Connected as %s', 'elementor' ),
|
||||
'<strong>' . esc_html( $remote_user->email ) . '</strong>'
|
||||
);
|
||||
$label = esc_html__( 'Disconnect', 'elementor' );
|
||||
$url = $this->get_admin_url( 'disconnect' );
|
||||
$attr = '';
|
||||
|
||||
echo sprintf(
|
||||
'%s <a %s href="%s">%s</a>',
|
||||
// PHPCS - the variable $title is already escaped above.
|
||||
$title, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
// PHPCS - the variable $attr is a plain string.
|
||||
$attr, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
esc_attr( $url ),
|
||||
esc_html( $label )
|
||||
);
|
||||
} else {
|
||||
echo 'Not Connected';
|
||||
}
|
||||
|
||||
echo '<hr>';
|
||||
|
||||
$this->print_app_info();
|
||||
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
printf( '<div><a href="%s">%s</a></div>', esc_url( $this->get_admin_url( 'reset' ) ), esc_html__( 'Reset Data', 'elementor' ) );
|
||||
}
|
||||
|
||||
echo '<hr>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_option_name() {
|
||||
return static::OPTION_NAME_PREFIX . $this->get_slug();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function admin_notice() {
|
||||
$notices = $this->get( 'notices' );
|
||||
|
||||
if ( ! $notices ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->print_notices( $notices );
|
||||
|
||||
$this->delete( 'notices' );
|
||||
}
|
||||
|
||||
|
||||
public function get_app_token_from_cli_token( $cli_token ) {
|
||||
$response = $this->request( 'get_app_token_from_cli_token', [
|
||||
'cli_token' => $cli_token,
|
||||
] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
// PHPCS - the variable $response does not contain a user input value.
|
||||
wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
// Use state as usual.
|
||||
$_REQUEST['state'] = $this->get( 'state' );
|
||||
$_REQUEST['code'] = $response->code;
|
||||
}
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_authorize() {
|
||||
if ( $this->is_connected() ) {
|
||||
$this->add_notice( esc_html__( 'Already connected.', 'elementor' ), 'info' );
|
||||
$this->redirect_to_admin_page();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_client_id();
|
||||
$this->set_request_state();
|
||||
|
||||
$this->redirect_to_remote_authorize_url();
|
||||
}
|
||||
|
||||
public function action_reset() {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
delete_option( static::OPTION_CONNECT_SITE_KEY );
|
||||
delete_option( 'elementor_remote_info_library' );
|
||||
}
|
||||
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_get_token() {
|
||||
if ( $this->is_connected() ) {
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
//phpcs:ignore WordPress.Security.NonceVerification.Recommended - The user as been authorized before in 'connect'.
|
||||
$state = Utils::get_super_global_value( $_REQUEST, 'state' );
|
||||
|
||||
if ( $state !== $this->get( 'state' ) ) {
|
||||
$this->add_notice( 'Get Token: Invalid Request.', 'error' );
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
$response = $this->request( 'get_token', [
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => Utils::get_super_global_value( $_REQUEST, 'code' ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
'redirect_uri' => rawurlencode( $this->get_admin_url( 'get_token' ) ),
|
||||
'client_id' => $this->get( 'client_id' ),
|
||||
] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$notice = 'Cannot Get Token:' . $response->get_error_message();
|
||||
$this->add_notice( $notice, 'error' );
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
$this->delete( 'state' );
|
||||
$this->set( (array) $response );
|
||||
|
||||
if ( ! empty( $response->data_share_opted_in ) && current_user_can( 'manage_options' ) ) {
|
||||
Tracker::set_opt_in( true );
|
||||
}
|
||||
|
||||
$this->after_connect();
|
||||
|
||||
// Add the notice *after* the method `after_connect`, so an app can redirect without the notice.
|
||||
$this->add_notice( esc_html__( 'Connected successfully.', 'elementor' ) );
|
||||
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_disconnect() {
|
||||
if ( $this->is_connected() ) {
|
||||
$this->disconnect();
|
||||
$this->add_notice( esc_html__( 'Disconnected successfully.', 'elementor' ) );
|
||||
}
|
||||
|
||||
$this->redirect_to_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.8.0
|
||||
* @access public
|
||||
*/
|
||||
public function action_reconnect() {
|
||||
$this->disconnect();
|
||||
|
||||
$this->action_authorize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_admin_url( $action, $params = [] ) {
|
||||
$params = [
|
||||
'app' => $this->get_slug(),
|
||||
'action' => $action,
|
||||
'nonce' => wp_create_nonce( $this->get_slug() . $action ),
|
||||
] + $params;
|
||||
|
||||
$admin_url = Str::encode_idn_url( get_admin_url() );
|
||||
$admin_url .= 'admin.php?page=' . Admin::PAGE_ID;
|
||||
|
||||
return add_query_arg( $params, $admin_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function is_connected() {
|
||||
return (bool) $this->get( 'access_token' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init() {}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init_data() {}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function after_connect() {}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get( $key, $default = null ) {
|
||||
$this->init_data();
|
||||
|
||||
return isset( $this->data[ $key ] ) ? $this->data[ $key ] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function set( $key, $value = null ) {
|
||||
$this->init_data();
|
||||
|
||||
if ( is_array( $key ) ) {
|
||||
$this->data = array_replace_recursive( $this->data, $key );
|
||||
} else {
|
||||
$this->data[ $key ] = $value;
|
||||
}
|
||||
|
||||
$this->update_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function delete( $key = null ) {
|
||||
$this->init_data();
|
||||
|
||||
if ( $key ) {
|
||||
unset( $this->data[ $key ] );
|
||||
} else {
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
$this->update_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function add( $key, $value, $default = '' ) {
|
||||
$new_value = $this->get( $key, $default );
|
||||
|
||||
if ( is_array( $new_value ) ) {
|
||||
$new_value[] = $value;
|
||||
} elseif ( is_string( $new_value ) ) {
|
||||
$new_value .= $value;
|
||||
} elseif ( is_numeric( $new_value ) ) {
|
||||
$new_value += $value;
|
||||
}
|
||||
|
||||
$this->set( $key, $new_value );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function add_notice( $content, $type = 'success' ) {
|
||||
$this->add( 'notices', compact( 'content', 'type' ), [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $action
|
||||
* @param array $request_body
|
||||
* @param false $as_array
|
||||
*
|
||||
* @return mixed|\WP_Error
|
||||
*/
|
||||
protected function request( $action, $request_body = [], $as_array = false ) {
|
||||
$request_body = $this->get_connect_info() + $request_body;
|
||||
|
||||
return $this->http_request(
|
||||
'POST',
|
||||
$action,
|
||||
[
|
||||
'timeout' => 25,
|
||||
'body' => $request_body,
|
||||
'headers' => $this->is_connected() ?
|
||||
[ 'X-Elementor-Signature' => $this->generate_signature( $request_body ) ] :
|
||||
[],
|
||||
],
|
||||
[
|
||||
'return_type' => $as_array ? static::HTTP_RETURN_TYPE_ARRAY : static::HTTP_RETURN_TYPE_OBJECT,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Base Connect Info
|
||||
*
|
||||
* Returns an array of connect info.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_base_connect_info() {
|
||||
return [
|
||||
'app' => $this->get_slug(),
|
||||
'access_token' => $this->get( 'access_token' ),
|
||||
'client_id' => $this->get( 'client_id' ),
|
||||
'local_id' => get_current_user_id(),
|
||||
'site_key' => $this->get_site_key(),
|
||||
'home_url' => trailingslashit( home_url() ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the connect information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_connect_info() {
|
||||
$connect_info = $this->get_base_connect_info();
|
||||
|
||||
$additional_info = [];
|
||||
|
||||
/**
|
||||
* Additional connect info.
|
||||
*
|
||||
* Filters the connection information when connecting to Elementor servers.
|
||||
* This hook can be used to add more information or add more data.
|
||||
*
|
||||
* @param array $additional_info Additional connecting information array.
|
||||
* @param Base_App $this The base app instance.
|
||||
*/
|
||||
$additional_info = apply_filters( 'elementor/connect/additional-connect-info', $additional_info, $this );
|
||||
|
||||
return array_merge( $connect_info, $additional_info );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $endpoint
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function generate_authentication_headers( $endpoint ) {
|
||||
$connect_info = ( new Collection( $this->get_connect_info() ) )
|
||||
->map_with_keys( function ( $value, $key ) {
|
||||
// For bc `get_connect_info` returns the connect info with underscore,
|
||||
// headers with underscore are not valid, so all the keys with underscore will be replaced to hyphen.
|
||||
return [ str_replace( '_', '-', $key ) => $value ];
|
||||
} )
|
||||
->replace_recursive( [ 'endpoint' => $endpoint ] )
|
||||
->sort_keys();
|
||||
|
||||
return $connect_info
|
||||
->merge( [ 'X-Elementor-Signature' => $this->generate_signature( $connect_info->all() ) ] )
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an http request
|
||||
*
|
||||
* @param $method
|
||||
* @param $endpoint
|
||||
* @param array $args
|
||||
* @param array $options
|
||||
*
|
||||
* @return mixed|\WP_Error
|
||||
*/
|
||||
protected function http_request( $method, $endpoint, $args = [], $options = [] ) {
|
||||
$options = wp_parse_args( $options, [
|
||||
'return_type' => static::HTTP_RETURN_TYPE_OBJECT,
|
||||
] );
|
||||
|
||||
$args = array_replace_recursive( [
|
||||
'headers' => $this->is_connected() ? $this->generate_authentication_headers( $endpoint ) : [],
|
||||
'method' => $method,
|
||||
'timeout' => 10,
|
||||
], $args );
|
||||
|
||||
$response = $this->http->request_with_fallback(
|
||||
$this->get_generated_urls( $endpoint ),
|
||||
$args
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
// PHPCS - the variable $response does not contain a user input value.
|
||||
wp_die( $response, [ 'back_link' => true ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( ! $response_code ) {
|
||||
return new \WP_Error( 500, 'No Response' );
|
||||
}
|
||||
|
||||
// Server sent a success message without content.
|
||||
if ( 'null' === $body ) {
|
||||
$body = true;
|
||||
}
|
||||
|
||||
$body = json_decode( $body, static::HTTP_RETURN_TYPE_ARRAY === $options['return_type'] );
|
||||
|
||||
if ( false === $body ) {
|
||||
return new \WP_Error( 422, 'Wrong Server Response' );
|
||||
}
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
// In case $as_array = true.
|
||||
$body = (object) $body;
|
||||
|
||||
$message = isset( $body->message ) ? $body->message : wp_remote_retrieve_response_message( $response );
|
||||
$code = (int) ( isset( $body->code ) ? $body->code : $response_code );
|
||||
|
||||
if ( ! $code ) {
|
||||
$code = $response_code;
|
||||
}
|
||||
|
||||
if ( 401 === $code ) {
|
||||
$this->delete();
|
||||
|
||||
$should_retry = ! in_array( $this->auth_mode, [ 'xhr', 'cli' ], true );
|
||||
|
||||
if ( $should_retry ) {
|
||||
$this->action_authorize();
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $options['with_error_data'] ) && true === $options['with_error_data'] ) {
|
||||
return new \WP_Error( $code, $message, $body );
|
||||
}
|
||||
|
||||
return new \WP_Error( $code, $message );
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a signature for the http request
|
||||
*
|
||||
* @param array $payload
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
private function generate_signature( $payload = [] ) {
|
||||
return hash_hmac(
|
||||
'sha256',
|
||||
wp_json_encode( $payload, JSON_NUMERIC_CHECK ),
|
||||
$this->get( 'access_token_secret' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_api_url() {
|
||||
return static::API_URL . '/' . $this->get_slug();
|
||||
}
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_remote_site_url() {
|
||||
return static::SITE_URL . '/' . $this->get_slug();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_remote_authorize_url() {
|
||||
$redirect_uri = $this->get_auth_redirect_uri();
|
||||
|
||||
$allowed_query_params_to_propagate = [
|
||||
'utm_source',
|
||||
'utm_medium',
|
||||
'utm_campaign',
|
||||
'utm_term',
|
||||
'utm_content',
|
||||
'source',
|
||||
'screen_hint',
|
||||
];
|
||||
|
||||
$query_params = ( new Collection( $_GET ) ) // phpcs:ignore
|
||||
->only( $allowed_query_params_to_propagate )
|
||||
->merge( [
|
||||
'action' => 'authorize',
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->get( 'client_id' ),
|
||||
'auth_secret' => $this->get( 'auth_secret' ),
|
||||
'state' => $this->get( 'state' ),
|
||||
'redirect_uri' => rawurlencode( $redirect_uri ),
|
||||
'may_share_data' => current_user_can( 'manage_options' ) && ! Tracker::is_allow_track(),
|
||||
'reconnect_nonce' => wp_create_nonce( $this->get_slug() . 'reconnect' ),
|
||||
] );
|
||||
|
||||
return add_query_arg( $query_params->all(), $this->get_remote_site_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function redirect_to_admin_page( $url = '' ) {
|
||||
if ( ! $url ) {
|
||||
$url = Admin::$url;
|
||||
}
|
||||
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'popup':
|
||||
$this->print_popup_close_script( $url );
|
||||
break;
|
||||
|
||||
case 'cli':
|
||||
$this->admin_notice();
|
||||
die;
|
||||
|
||||
default:
|
||||
wp_safe_redirect( $url );
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function set_client_id() {
|
||||
$source = Utils::get_super_global_value( $_REQUEST, 'source' ) ?? ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
$response = $this->request(
|
||||
'get_client_id',
|
||||
[
|
||||
'source' => esc_attr( $source ),
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
// PHPCS - the variable $response does not contain a user input value.
|
||||
wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$this->set( 'client_id', $response->client_id );
|
||||
$this->set( 'auth_secret', $response->auth_secret );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function set_request_state() {
|
||||
$this->set( 'state', wp_generate_password( 12, false ) );
|
||||
}
|
||||
|
||||
protected function get_popup_success_event_data() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function print_popup_close_script( $url ) {
|
||||
$data = $this->get_popup_success_event_data();
|
||||
|
||||
?>
|
||||
<script>
|
||||
if ( opener && opener !== window ) {
|
||||
opener.jQuery( 'body' ).trigger(
|
||||
'elementor/connect/success/<?php echo esc_attr( Utils::get_super_global_value( $_REQUEST, 'callback_id' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. ?>',
|
||||
<?php echo wp_json_encode( $data ); ?>
|
||||
);
|
||||
|
||||
window.close();
|
||||
opener.focus();
|
||||
} else {
|
||||
location = '<?php echo esc_url( $url ); ?>';
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function disconnect() {
|
||||
if ( $this->is_connected() ) {
|
||||
// Try update the server, but not needed to handle errors.
|
||||
$this->request( 'disconnect' );
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
public function get_site_key() {
|
||||
$site_key = get_option( static::OPTION_CONNECT_SITE_KEY );
|
||||
|
||||
if ( ! $site_key ) {
|
||||
$site_key = md5( uniqid( wp_generate_password() ) );
|
||||
update_option( static::OPTION_CONNECT_SITE_KEY, $site_key );
|
||||
}
|
||||
|
||||
return $site_key;
|
||||
}
|
||||
|
||||
protected function redirect_to_remote_authorize_url() {
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'cli':
|
||||
$this->get_app_token_from_cli_token( Utils::get_super_global_value( $_REQUEST, 'token' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
return;
|
||||
default:
|
||||
wp_redirect( $this->get_remote_authorize_url() ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Safe redirect is used here.
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
protected function get_auth_redirect_uri() {
|
||||
$redirect_uri = $this->get_admin_url( 'get_token' );
|
||||
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'popup':
|
||||
$redirect_uri = add_query_arg( [
|
||||
'mode' => 'popup',
|
||||
'callback_id' => esc_attr( Utils::get_super_global_value( $_REQUEST, 'callback_id' ) ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
], $redirect_uri );
|
||||
break;
|
||||
}
|
||||
|
||||
return $redirect_uri;
|
||||
}
|
||||
|
||||
|
||||
protected function print_notices( $notices ) {
|
||||
switch ( $this->auth_mode ) {
|
||||
case 'cli':
|
||||
foreach ( $notices as $notice ) {
|
||||
printf( '[%s] %s', wp_kses_post( $notice['type'] ), wp_kses_post( $notice['content'] ) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/**
|
||||
* @var Admin_Notices $admin_notices
|
||||
*/
|
||||
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
|
||||
|
||||
foreach ( $notices as $notice ) {
|
||||
$options = [
|
||||
'description' => wp_kses_post( wpautop( $notice['content'] ) ),
|
||||
'type' => $notice['type'],
|
||||
'icon' => false,
|
||||
];
|
||||
|
||||
$admin_notices->print_admin_notice( $options );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function get_app_info() {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function print_app_info() {
|
||||
$app_info = $this->get_app_info();
|
||||
|
||||
foreach ( $app_info as $key => $item ) {
|
||||
if ( $item['value'] ) {
|
||||
$status = 'Exist';
|
||||
$color = 'green';
|
||||
} else {
|
||||
$status = 'Empty';
|
||||
$color = 'red';
|
||||
}
|
||||
|
||||
// PHPCS - the values of $item['label'], $color, $status are plain strings.
|
||||
printf( '%s: <strong style="color:%s">%s</strong><br>', $item['label'], $color, $status ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function get_generated_urls( $endpoint ) {
|
||||
$base_urls = $this->get_api_url();
|
||||
|
||||
if ( ! is_array( $base_urls ) ) {
|
||||
$base_urls = [ $base_urls ];
|
||||
}
|
||||
|
||||
return array_map( function ( $base_url ) use ( $endpoint ) {
|
||||
return trailingslashit( $base_url ) . $endpoint;
|
||||
}, $base_urls );
|
||||
}
|
||||
|
||||
private function init_auth_mode() {
|
||||
$is_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
|
||||
$is_ajax = wp_doing_ajax();
|
||||
|
||||
if ( $is_rest || $is_ajax ) {
|
||||
// Set default to 'xhr' if rest or ajax request.
|
||||
$this->set_auth_mode( 'xhr' );
|
||||
}
|
||||
|
||||
$mode = Utils::get_super_global_value( $_REQUEST, 'mode' ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
|
||||
|
||||
if ( $mode ) {
|
||||
$allowed_auth_modes = [
|
||||
'popup',
|
||||
];
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
$allowed_auth_modes[] = 'cli';
|
||||
}
|
||||
|
||||
if ( in_array( $mode, $allowed_auth_modes, true ) ) {
|
||||
$this->set_auth_mode( $mode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function set_auth_mode( $mode ) {
|
||||
$this->auth_mode = $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_notices', [ $this, 'admin_notice' ] );
|
||||
|
||||
$this->init_auth_mode();
|
||||
|
||||
$this->http = new Http();
|
||||
|
||||
/**
|
||||
* Allow extended apps to customize the __construct without call parent::__construct.
|
||||
*/
|
||||
$this->init();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Base_User_App extends Base_App {
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function update_settings() {
|
||||
update_user_option( get_current_user_id(), $this->get_option_name(), $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init_data() {
|
||||
$this->data = get_user_option( $this->get_option_name() );
|
||||
|
||||
if ( ! $this->data ) {
|
||||
$this->data = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class Common_App extends Base_User_App {
|
||||
const OPTION_CONNECT_COMMON_DATA_KEY = self::OPTION_NAME_PREFIX . 'common_data';
|
||||
|
||||
protected static $common_data = null;
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_option_name() {
|
||||
return static::OPTION_NAME_PREFIX . 'common_data';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function init_data() {
|
||||
if ( is_null( self::$common_data ) ) {
|
||||
self::$common_data = get_user_option( static::get_option_name() );
|
||||
|
||||
if ( ! self::$common_data ) {
|
||||
self::$common_data = [];
|
||||
};
|
||||
}
|
||||
|
||||
$this->data = & self::$common_data;
|
||||
}
|
||||
|
||||
public function action_reset() {
|
||||
delete_user_option( get_current_user_id(), static::OPTION_CONNECT_COMMON_DATA_KEY );
|
||||
|
||||
parent::action_reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Connect extends Common_App {
|
||||
|
||||
public function get_title() {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
protected function get_slug() {
|
||||
return 'connect';
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function render_admin_widget() {}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect\Apps;
|
||||
|
||||
use Elementor\Api;
|
||||
use Elementor\User;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Library extends Common_App {
|
||||
|
||||
public function get_title() {
|
||||
return esc_html__( 'Library', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*/
|
||||
protected function get_slug() {
|
||||
return 'library';
|
||||
}
|
||||
|
||||
public function get_template_content( $id ) {
|
||||
if ( ! $this->is_connected() ) {
|
||||
return new \WP_Error( '401', esc_html__( 'Connecting to the Library failed. Please try reloading the page and try again', 'elementor' ) );
|
||||
}
|
||||
|
||||
$body_args = [
|
||||
'id' => $id,
|
||||
|
||||
// Which API version is used.
|
||||
'api_version' => ELEMENTOR_VERSION,
|
||||
// Which language to return.
|
||||
'site_lang' => get_bloginfo( 'language' ),
|
||||
];
|
||||
|
||||
/**
|
||||
* API: Template body args.
|
||||
*
|
||||
* Filters the body arguments send with the GET request when fetching the content.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $body_args Body arguments.
|
||||
*/
|
||||
$body_args = apply_filters( 'elementor/api/get_templates/body_args', $body_args );
|
||||
|
||||
$template_content = $this->request( 'get_template_content', $body_args, true );
|
||||
|
||||
if ( is_wp_error( $template_content ) && 401 === $template_content->get_error_code() ) {
|
||||
// Normalize 401 message
|
||||
return new \WP_Error( 401, esc_html__( 'Connecting to the Library failed. Please try reloading the page and try again', 'elementor' ) );
|
||||
}
|
||||
|
||||
return $template_content;
|
||||
}
|
||||
|
||||
public function localize_settings( $settings ) {
|
||||
$is_connected = $this->is_connected();
|
||||
|
||||
/** @var ConnectModule $connect */
|
||||
$connect = Plugin::$instance->common->get_component( 'connect' );
|
||||
|
||||
return array_replace_recursive( $settings, [
|
||||
'library_connect' => [
|
||||
'is_connected' => $is_connected,
|
||||
'subscription_plans' => $connect->get_subscription_plans( 'template-library' ),
|
||||
// TODO: Remove `base_access_level`.
|
||||
'base_access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'base_access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
'current_access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'current_access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
public function library_connect_popup_seen() {
|
||||
User::set_introduction_viewed( [
|
||||
'introductionKey' => 'library_connect',
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Elementor\Core\Common\Modules\Ajax\Module $ajax_manager
|
||||
*/
|
||||
public function register_ajax_actions( $ajax_manager ) {
|
||||
$ajax_manager->register_ajax_action( 'library_connect_popup_seen', [ $this, 'library_connect_popup_seen' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* After Connect
|
||||
*
|
||||
* After Connecting to the library, re-fetch the library data to get it up to date.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*/
|
||||
protected function after_connect() {
|
||||
Api::get_library_data( true );
|
||||
}
|
||||
|
||||
protected function get_app_info() {
|
||||
return [
|
||||
'user_common_data' => [
|
||||
'label' => 'User Common Data',
|
||||
'value' => get_user_option( $this->get_option_name(), get_current_user_id() ),
|
||||
],
|
||||
'connect_site_key' => [
|
||||
'label' => 'Site Key',
|
||||
'value' => get_option( self::OPTION_CONNECT_SITE_KEY ),
|
||||
],
|
||||
'remote_info_library' => [
|
||||
'label' => 'Remote Library Info',
|
||||
'value' => get_option( 'elementor_remote_info_library' ),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function get_popup_success_event_data() {
|
||||
return [
|
||||
'access_level' => ConnectModule::ACCESS_LEVEL_CORE,
|
||||
'access_tier' => ConnectModule::ACCESS_TIER_FREE,
|
||||
];
|
||||
}
|
||||
|
||||
protected function init() {
|
||||
add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
|
||||
add_filter( 'elementor/common/localize_settings', [ $this, 'localize_settings' ] );
|
||||
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect;
|
||||
|
||||
use Elementor\Core\Admin\Menu\Interfaces\Admin_Menu_Item_With_Page;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Base_App;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Settings;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Connect_Menu_Item implements Admin_Menu_Item_With_Page {
|
||||
|
||||
public function is_visible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_parent_slug() {
|
||||
return Settings::PAGE_ID;
|
||||
}
|
||||
|
||||
public function get_label() {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_page_title() {
|
||||
return esc_html__( 'Connect', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_capability() {
|
||||
return 'edit_posts';
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$apps = Plugin::$instance->common->get_component( 'connect' )->get_apps();
|
||||
?>
|
||||
<style>
|
||||
.elementor-connect-app-wrapper{
|
||||
margin-bottom: 50px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<div class="wrap">
|
||||
<?php
|
||||
|
||||
/** @var Base_App $app */
|
||||
foreach ( $apps as $app ) {
|
||||
echo '<div class="elementor-connect-app-wrapper">';
|
||||
$app->render_admin_widget();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
?>
|
||||
</div><!-- /.wrap -->
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Connect;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Base_App;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Common_App;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Connect;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Utils;
|
||||
use WP_User_Query;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Module extends BaseModule {
|
||||
const ACCESS_LEVEL_CORE = 0;
|
||||
const ACCESS_LEVEL_PRO = 1;
|
||||
const ACCESS_LEVEL_EXPERT = 20;
|
||||
|
||||
const ACCESS_TIER_FREE = 'free';
|
||||
const ACCESS_TIER_ESSENTIAL = 'essential';
|
||||
const ACCESS_TIER_ESSENTIAL_OCT_2023 = 'essential-oct2023';
|
||||
const ACCESS_TIER_ADVANCED = 'advanced';
|
||||
const ACCESS_TIER_EXPERT = 'expert';
|
||||
const ACCESS_TIER_AGENCY = 'agency';
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'connect';
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $registered_apps = [];
|
||||
|
||||
/**
|
||||
* Apps Instances.
|
||||
*
|
||||
* Holds the list of all the apps instances.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @var Base_App[]
|
||||
*/
|
||||
protected $apps = [];
|
||||
|
||||
/**
|
||||
* Registered apps categories.
|
||||
*
|
||||
* Holds the list of all the registered apps categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $categories = [];
|
||||
|
||||
protected $admin_page;
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->registered_apps = [
|
||||
'connect' => Connect::get_class_name(),
|
||||
'library' => Library::get_class_name(),
|
||||
];
|
||||
|
||||
// When using REST API the parent module is construct after the action 'elementor/init'
|
||||
// so this part of code make sure to register the module "apps".
|
||||
if ( did_action( 'elementor/init' ) ) {
|
||||
$this->init();
|
||||
} else {
|
||||
// Note: The priority 11 is for allowing plugins to add their register callback on elementor init.
|
||||
add_action( 'elementor/init', [ $this, 'init' ], 11 );
|
||||
}
|
||||
|
||||
add_filter( 'elementor/tracker/send_tracking_data_params', function ( $params ) {
|
||||
return $this->add_tracking_data( $params );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register default apps.
|
||||
*
|
||||
* Registers the default apps.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function init() {
|
||||
if ( is_admin() ) {
|
||||
$this->admin_page = new Admin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Elementor apps.
|
||||
*
|
||||
* Fires after Elementor registers the default apps.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @param self $this The apps manager instance.
|
||||
*/
|
||||
do_action( 'elementor/connect/apps/register', $this );
|
||||
|
||||
foreach ( $this->registered_apps as $slug => $class ) {
|
||||
$this->apps[ $slug ] = new $class();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.0
|
||||
*/
|
||||
public function localize_settings() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' );
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register app.
|
||||
*
|
||||
* Registers an app.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $slug App slug.
|
||||
* @param string $class App full class name.
|
||||
*
|
||||
* @return self The updated apps manager instance.
|
||||
*/
|
||||
public function register_app( $slug, $class ) {
|
||||
$this->registered_apps[ $slug ] = $class;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get app instance.
|
||||
*
|
||||
* Retrieve the app instance.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param $slug
|
||||
*
|
||||
* @return Base_App|null
|
||||
*/
|
||||
public function get_app( $slug ) {
|
||||
if ( isset( $this->apps[ $slug ] ) ) {
|
||||
return $this->apps[ $slug ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
* @return Base_App[]
|
||||
*/
|
||||
public function get_apps() {
|
||||
return $this->apps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function register_category( $slug, $args ) {
|
||||
$this->categories[ $slug ] = $args;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_categories() {
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $context Where this subscription plan should be shown.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscription_plans( $context = '' ) {
|
||||
$base_url = Utils::has_pro() ? 'https://my.elementor.com/upgrade-subscription' : 'https://elementor.com/pro';
|
||||
$promotion_url = $base_url . '/?utm_source=' . $context . '&utm_medium=wp-dash&utm_campaign=gopro';
|
||||
|
||||
return [
|
||||
static::ACCESS_TIER_FREE => [
|
||||
'label' => null,
|
||||
'promotion_url' => null,
|
||||
'color' => null,
|
||||
],
|
||||
static::ACCESS_TIER_ESSENTIAL => [
|
||||
'label' => 'Pro',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_ESSENTIAL_OCT_2023 => [
|
||||
'label' => 'Advanced', // Should be the same label as "Advanced".
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_ADVANCED => [
|
||||
'label' => 'Advanced',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_EXPERT => [
|
||||
'label' => 'Expert',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
static::ACCESS_TIER_AGENCY => [
|
||||
'label' => 'Agency',
|
||||
'promotion_url' => $promotion_url,
|
||||
'color' => '#92003B',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function add_tracking_data( $params ) {
|
||||
$users = [];
|
||||
|
||||
$users_query = new WP_User_Query( [
|
||||
'count_total' => false, // Disable SQL_CALC_FOUND_ROWS.
|
||||
'meta_query' => [
|
||||
'key' => Common_App::OPTION_CONNECT_COMMON_DATA_KEY,
|
||||
'compare' => 'EXISTS',
|
||||
],
|
||||
] );
|
||||
|
||||
foreach ( $users_query->get_results() as $user ) {
|
||||
$connect_common_data = get_user_option( Common_App::OPTION_CONNECT_COMMON_DATA_KEY, $user->ID );
|
||||
|
||||
if ( $connect_common_data ) {
|
||||
$users [] = [
|
||||
'id' => $user->ID,
|
||||
'email' => $connect_common_data['user']->email,
|
||||
'roles' => implode( ', ', $user->roles ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$params['usages'][ $this->get_name() ] = [
|
||||
'site_key' => get_option( Base_App::OPTION_CONNECT_SITE_KEY ),
|
||||
'count' => count( $users ),
|
||||
'users' => $users,
|
||||
];
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker\Data;
|
||||
|
||||
use Elementor\Core\Common\Modules\EventTracker\DB as Events_DB_Manager;
|
||||
use Elementor\Plugin;
|
||||
use WP_REST_Server;
|
||||
use Elementor\Data\V2\Base\Controller as Controller_Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Controller extends Controller_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'send-event';
|
||||
}
|
||||
|
||||
public function register_endpoints() {
|
||||
$this->index_endpoint->register_items_route( \WP_REST_Server::CREATABLE, [
|
||||
'event_data' => [
|
||||
'description' => 'All the recorded event data in JSON format',
|
||||
'type' => 'object',
|
||||
'required' => true,
|
||||
],
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Permissions Callback
|
||||
*
|
||||
* This endpoint should only accept POST requests, and currently we only track site administrator actions.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param \WP_REST_Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function get_permission_callback( $request ) {
|
||||
if ( WP_REST_Server::CREATABLE !== $request->get_method() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Items
|
||||
*
|
||||
* Receives a request for adding an event data entry into the database. If the request contains event data, this
|
||||
* method initiates creation of a database entry with the event data in the Events DB table.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param \WP_REST_Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function create_items( $request ) {
|
||||
$request_body = $request->get_json_params();
|
||||
|
||||
if ( empty( $request_body['event_data'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var Events_DB_Manager $event_tracker_db_manager */
|
||||
$event_tracker_db_manager = Plugin::$instance->common
|
||||
->get_component( 'event-tracker' )
|
||||
->get_component( 'events-db' );
|
||||
|
||||
$event_tracker_db_manager->create_entry( $request_body['event_data'] );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Common_App;
|
||||
use Elementor\Core\Common\Modules\Connect\Apps\Library;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class DB extends Base_Object {
|
||||
|
||||
/**
|
||||
* @var \wpdb
|
||||
*/
|
||||
private $wpdb;
|
||||
|
||||
const TABLE_NAME = 'e_events';
|
||||
const DB_VERSION_OPTION_KEY = 'elementor_events_db_version';
|
||||
const CURRENT_DB_VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* Get Table Name
|
||||
*
|
||||
* Returns the Events database table's name with the `wpdb` prefix.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_table_name() {
|
||||
return $this->wpdb->prefix . self::TABLE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare Database for Entry
|
||||
*
|
||||
* The events database should have a limit of up to 1000 event entries stored daily.
|
||||
* Before adding a new entry to the database, we make sure that the limit of 1000 events is not reached.
|
||||
* If there are 1000 or more entries in the DB, we delete the earliest-inserted entry before inserting a new one.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public function prepare_db_for_entry() {
|
||||
$events = $this->get_event_ids_from_db();
|
||||
|
||||
if ( 1000 <= count( $events ) ) {
|
||||
$event_ids = [];
|
||||
|
||||
foreach ( $events as $event ) {
|
||||
$event_ids[] = $event->id;
|
||||
}
|
||||
|
||||
// Sort the array by entry ID
|
||||
array_multisort( $event_ids, SORT_ASC, $events );
|
||||
|
||||
// Delete the smallest ID (which is the earliest DB entry)
|
||||
$this->wpdb->delete( $this->get_table_name(), [ 'ID' => $events[0]->id ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Entry
|
||||
*
|
||||
* Adds an event entry to the database.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public function create_entry( $event_data ) {
|
||||
$this->prepare_db_for_entry();
|
||||
|
||||
$connect = Plugin::$instance->common->get_component( 'connect' );
|
||||
/** @var Library $library */
|
||||
$library = $connect->get_apps()['library'];
|
||||
|
||||
if ( ! isset( $event_data['details'] ) ) {
|
||||
$event_data['details'] = [];
|
||||
}
|
||||
|
||||
if ( $library->is_connected() ) {
|
||||
$user_connect_data = get_user_option( Common_App::OPTION_CONNECT_COMMON_DATA_KEY );
|
||||
|
||||
// Add the user's client ID to the event.
|
||||
$event_data['details']['client_id'] = $user_connect_data['client_id'];
|
||||
}
|
||||
|
||||
$event_data['details'] = json_encode( $event_data['details'] );
|
||||
|
||||
$entry = [
|
||||
'event_data' => wp_json_encode( $event_data ),
|
||||
'created_at' => $event_data['ts'],
|
||||
];
|
||||
|
||||
$this->wpdb->insert( $this->get_table_name(), $entry );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Event IDs From DB
|
||||
*
|
||||
* Fetches the IDs of all events saved in the database.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return array|object|null
|
||||
*/
|
||||
public function get_event_ids_from_db() {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
return $this->wpdb->get_results( "SELECT id FROM {$this->get_table_name()}" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Table
|
||||
*
|
||||
* Empties the contents of the Events DB table.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public static function reset_table() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . self::TABLE_NAME;
|
||||
|
||||
// Delete all content of the table.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$wpdb->query( "TRUNCATE TABLE {$table_name}" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Table
|
||||
*
|
||||
* Creates the `wp_e_events` database table.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param string $query to that looks for the Events table in the DB. Used for checking if table was created.
|
||||
*/
|
||||
private function create_table( $query ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
|
||||
$table_name = $this->get_table_name();
|
||||
$charset_collate = $this->wpdb->get_charset_collate();
|
||||
|
||||
$e_events_table = "CREATE TABLE `{$table_name}` (
|
||||
id bigint(20) unsigned auto_increment primary key,
|
||||
event_data text null,
|
||||
created_at datetime not null
|
||||
) {$charset_collate};";
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->wpdb->query( $e_events_table );
|
||||
|
||||
// Check if table was created successfully.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
if ( $this->wpdb->get_var( $query ) === $table_name ) {
|
||||
update_option( self::DB_VERSION_OPTION_KEY, self::CURRENT_DB_VERSION, false );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Indexes
|
||||
*
|
||||
* Adds an index to the events table for the creation date column.
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
private function add_indexes() {
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$this->wpdb->query( 'ALTER TABLE ' . $this->get_table_name() . '
|
||||
ADD INDEX `created_at_index` (`created_at`)
|
||||
' );
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
global $wpdb;
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
// Check if table exists. If not, create it.
|
||||
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $this->get_table_name() ) );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
if ( $wpdb->get_var( $query ) !== $this->get_table_name() ) {
|
||||
$this->create_table( $query );
|
||||
$this->add_indexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\EventTracker\Data\Controller;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Tracker;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Event Tracker Module Class
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
public function get_name() {
|
||||
return 'event-tracker';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* @since 3.6.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
return [
|
||||
'isUserDataShared' => Tracker::is_allow_track(),
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
// Initialize Events Database Table
|
||||
$this->add_component( 'events-db', new DB() );
|
||||
|
||||
// Handle User Data Deletion/Export requests.
|
||||
new Personal_Data();
|
||||
|
||||
Plugin::$instance->data_manager_v2->register_controller( new Controller() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\EventTracker;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Core\Common\Modules\EventTracker\DB as Events_DB_Manager;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Personal_Data extends Base_Object {
|
||||
|
||||
const WP_KEY = 'elementor-event-tracker';
|
||||
|
||||
/**
|
||||
* Get Title
|
||||
*
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_title() {
|
||||
return esc_html__( 'Elementor Event Tracker', 'elementor' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase all the submissions related to specific email.
|
||||
*
|
||||
* Since event data is saved globally per site and not per user, we remove all saved events from the DB upon a
|
||||
* user's data deletion request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function erase_data() {
|
||||
// Get number of events saved in the DB.
|
||||
/** @var Events_DB_Manager $event_tracker_db_manager */
|
||||
$event_tracker_db_manager = Plugin::$instance->common
|
||||
->get_component( 'event-tracker' )
|
||||
->get_component( 'events-db' );
|
||||
|
||||
$events = $event_tracker_db_manager->get_event_ids_from_db();
|
||||
$events_count = count( $events );
|
||||
|
||||
DB::reset_table();
|
||||
|
||||
// Validate table deleted
|
||||
$updated_events = $event_tracker_db_manager->get_event_ids_from_db();
|
||||
$updated_events_count = count( $updated_events );
|
||||
|
||||
return [
|
||||
'items_removed' => $events_count - $updated_events_count,
|
||||
'items_retained' => 0,
|
||||
'messages' => [],
|
||||
'done' => 0 === $updated_events_count,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add eraser to the list of erasers.
|
||||
*
|
||||
* @param $erasers
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
private function add_eraser( $erasers ) {
|
||||
return $erasers + [
|
||||
self::WP_KEY => [
|
||||
'eraser_friendly_name' => $this->get_title(),
|
||||
'callback' => function () {
|
||||
return $this->erase_data();
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Personal_Data constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'wp_privacy_personal_data_erasers', function ( $exporters ) {
|
||||
return $this->add_eraser( $exporters );
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder;
|
||||
|
||||
use Elementor\Core\Base\Base_Object;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Category
|
||||
*
|
||||
* Base class for Elementor Finder categories.
|
||||
*/
|
||||
abstract class Base_Category extends Base_Object {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @abstract
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_title();
|
||||
|
||||
/**
|
||||
* Get a unique category ID.
|
||||
*
|
||||
* TODO: Make abstract.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @deprecated 3.5.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
|
||||
get_class( $this ) . '::' . __FUNCTION__,
|
||||
'3.5.0',
|
||||
'This method will be replaced with an abstract method.'
|
||||
);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @abstract
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function get_category_items( array $options = [] );
|
||||
|
||||
/**
|
||||
* Is dynamic.
|
||||
*
|
||||
* Determine if the category is dynamic.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dynamic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
$settings = [
|
||||
'title' => $this->get_title(),
|
||||
'dynamic' => $this->is_dynamic(),
|
||||
];
|
||||
|
||||
if ( ! $settings['dynamic'] ) {
|
||||
$settings['items'] = $this->get_category_items();
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder;
|
||||
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Categories_Manager {
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*
|
||||
* @var Base_Category[]
|
||||
*/
|
||||
private $categories;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $categories_list = [
|
||||
'edit',
|
||||
'general',
|
||||
'create',
|
||||
'site',
|
||||
'settings',
|
||||
'tools',
|
||||
];
|
||||
|
||||
/**
|
||||
* Add category.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @deprecated 3.5.0 Use `register()` method instead.
|
||||
* @access public
|
||||
*
|
||||
* @param string $category_name
|
||||
* @param Base_Category $category
|
||||
*
|
||||
* @deprecated 3.5.0 Use `register()` method instead.
|
||||
*/
|
||||
public function add_category( $category_name, Base_Category $category ) {
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
|
||||
__METHOD__,
|
||||
'3.5.0',
|
||||
'register()'
|
||||
);
|
||||
|
||||
$this->register( $category, $category_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register finder category.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @access public
|
||||
*
|
||||
* @param Base_Category $finder_category_instance An Instance of a category.
|
||||
* @param string $finder_category_name A Category name. Deprecated parameter.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register( Base_Category $finder_category_instance, $finder_category_name = null ) {
|
||||
// TODO: For BC. Remove in the future.
|
||||
if ( $finder_category_name ) {
|
||||
Plugin::instance()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_argument(
|
||||
'$finder_category_name', '3.5.0'
|
||||
);
|
||||
} else {
|
||||
$finder_category_name = $finder_category_instance->get_id();
|
||||
}
|
||||
|
||||
$this->categories[ $finder_category_name ] = $finder_category_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a finder category.
|
||||
*
|
||||
* @param string $finder_category_name - Category to unregister.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.6.0
|
||||
* @access public
|
||||
*/
|
||||
public function unregister( $finder_category_name ) {
|
||||
unset( $this->categories[ $finder_category_name ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get categories.
|
||||
*
|
||||
* Retrieve the registered categories, or a specific category if the category name
|
||||
* is provided as a parameter.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param string $category Category name.
|
||||
*
|
||||
* @return Base_Category|Base_Category[]|null
|
||||
*/
|
||||
public function get_categories( $category = '' ) {
|
||||
if ( ! $this->categories ) {
|
||||
$this->init_categories();
|
||||
}
|
||||
|
||||
if ( $category ) {
|
||||
if ( isset( $this->categories[ $category ] ) ) {
|
||||
return $this->categories[ $category ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init categories.
|
||||
*
|
||||
* Used to initialize the native finder categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access private
|
||||
*/
|
||||
private function init_categories() {
|
||||
foreach ( $this->categories_list as $category_name ) {
|
||||
$class_name = __NAMESPACE__ . '\Categories\\' . $category_name;
|
||||
|
||||
$this->register( new $class_name() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor Finder categories init.
|
||||
*
|
||||
* Fires after Elementor Finder initialize it's native categories.
|
||||
*
|
||||
* This hook should be used to add your own Finder categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @deprecated 3.5.0 Use `elementor/finder/register` hook instead.
|
||||
*
|
||||
* @param Categories_Manager $this.
|
||||
*/
|
||||
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action(
|
||||
'elementor/finder/categories/init',
|
||||
[ $this ],
|
||||
'3.5.0',
|
||||
'elementor/finder/register'
|
||||
);
|
||||
|
||||
/**
|
||||
* Elementor Finder categories registration.
|
||||
*
|
||||
* Fires after Elementor Finder initialize it's native categories.
|
||||
*
|
||||
* This hook should be used to register your own Finder categories.
|
||||
*
|
||||
* @since 3.5.0
|
||||
*
|
||||
* @param Categories_Manager $this Finder Categories manager.
|
||||
*/
|
||||
do_action( 'elementor/finder/register', $this );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Category
|
||||
*
|
||||
* Provides items related to creation of new posts/pages/templates etc.
|
||||
*/
|
||||
class Create extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Create', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'create';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
$result = [];
|
||||
|
||||
$registered_document_types = Plugin::$instance->documents->get_document_types();
|
||||
|
||||
// TODO: Remove - Support 'post' backwards compatibility - See `Documents_Manager::register_default_types()`.
|
||||
unset( $registered_document_types['post'] );
|
||||
|
||||
$elementor_supported_post_types = array_flip( get_post_types_by_support( 'elementor' ) );
|
||||
|
||||
foreach ( $registered_document_types as $document_name => $document_class ) {
|
||||
$document_properties = $document_class::get_properties();
|
||||
|
||||
if ( empty( $document_properties['show_in_finder'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $document_properties['cpt'] ) ) {
|
||||
foreach ( $document_properties['cpt'] as $cpt ) {
|
||||
unset( $elementor_supported_post_types[ $cpt ] );
|
||||
}
|
||||
}
|
||||
|
||||
$result[ $document_name ] = $this->create_item_url_by_document_class( $document_class );
|
||||
}
|
||||
|
||||
foreach ( $elementor_supported_post_types as $post_type => $val ) {
|
||||
$result[ $post_type ] = $this->create_item_url_by_post_type( $post_type );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function create_item_url_by_post_type( $post_type ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
|
||||
// If there is an old post type from inactive plugins.
|
||||
if ( ! $post_type_object ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->get_create_new_template(
|
||||
sprintf( __( 'Add New %s', 'elementor' ), $post_type_object->labels->singular_name ),
|
||||
Plugin::$instance->documents->get_create_new_post_url( $post_type )
|
||||
);
|
||||
}
|
||||
|
||||
private function create_item_url_by_document_class( $document_class ) {
|
||||
$result = $this->get_create_new_template(
|
||||
$document_class::get_add_new_title(),
|
||||
$document_class::get_create_url()
|
||||
);
|
||||
|
||||
$lock_behavior = $document_class::get_lock_behavior_v2();
|
||||
$is_locked = ! empty( $lock_behavior ) && $lock_behavior->is_locked();
|
||||
|
||||
if ( $is_locked ) {
|
||||
$result['lock'] = $lock_behavior->get_config();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function get_create_new_template( $add_new_title, $url ) {
|
||||
return [
|
||||
'title' => $add_new_title,
|
||||
'icon' => 'plus-circle-o',
|
||||
'url' => $url,
|
||||
'keywords' => [ $add_new_title, 'post', 'page', 'template', 'new', 'create' ],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Base\Document;
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Category
|
||||
*
|
||||
* Provides items related to editing of posts/pages/templates etc.
|
||||
*/
|
||||
class Edit extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Edit', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'edit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is dynamic.
|
||||
*
|
||||
* Determine if the category is dynamic.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dynamic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
$post_types = get_post_types( [
|
||||
'exclude_from_search' => false,
|
||||
] );
|
||||
|
||||
$post_types[] = Source_Local::CPT;
|
||||
|
||||
$document_types = Plugin::$instance->documents->get_document_types( [
|
||||
'is_editable' => true,
|
||||
'show_in_finder' => true,
|
||||
] );
|
||||
|
||||
$recently_edited_query_args = [
|
||||
'no_found_rows' => true,
|
||||
'post_type' => $post_types,
|
||||
'post_status' => [ 'publish', 'draft', 'private', 'pending', 'future' ],
|
||||
'posts_per_page' => '10',
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => '_elementor_edit_mode',
|
||||
'value' => 'builder',
|
||||
],
|
||||
[
|
||||
'relation' => 'or',
|
||||
[
|
||||
'key' => Document::TYPE_META_KEY,
|
||||
'compare' => 'NOT EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => Document::TYPE_META_KEY,
|
||||
'value' => array_keys( $document_types ),
|
||||
],
|
||||
],
|
||||
],
|
||||
'orderby' => 'modified',
|
||||
's' => $options['filter'],
|
||||
];
|
||||
|
||||
$recently_edited_query = new \WP_Query( $recently_edited_query_args );
|
||||
|
||||
$items = [];
|
||||
|
||||
/** @var \WP_Post $post */
|
||||
foreach ( $recently_edited_query->posts as $post ) {
|
||||
$document = Plugin::$instance->documents->get( $post->ID );
|
||||
|
||||
if ( ! $document ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$is_template = Source_Local::CPT === $post->post_type;
|
||||
|
||||
$description = $document->get_title();
|
||||
|
||||
$icon = 'document-file';
|
||||
|
||||
if ( $is_template ) {
|
||||
$description = esc_html__( 'Template', 'elementor' ) . ' / ' . $description;
|
||||
|
||||
$icon = 'post-title';
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'icon' => $icon,
|
||||
'title' => esc_html( $post->post_title ),
|
||||
'description' => $description,
|
||||
'url' => $document->get_edit_url(),
|
||||
'actions' => [
|
||||
[
|
||||
'name' => 'view',
|
||||
'url' => $document->get_permalink(),
|
||||
'icon' => 'preview-medium',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Core\RoleManager\Role_Manager;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\TemplateLibrary\Source_Local;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* General Category
|
||||
*
|
||||
* Provides general items related to Elementor Admin.
|
||||
*/
|
||||
class General extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'General', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'general';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
return [
|
||||
'saved-templates' => [
|
||||
'title' => esc_html__( 'Saved Templates', 'elementor' ),
|
||||
'icon' => 'library-save',
|
||||
'url' => Source_Local::get_admin_url(),
|
||||
'keywords' => [ 'template', 'section', 'page', 'library' ],
|
||||
],
|
||||
'system-info' => [
|
||||
'title' => esc_html__( 'System Info', 'elementor' ),
|
||||
'icon' => 'info-circle-o',
|
||||
'url' => admin_url( 'admin.php?page=elementor-system-info' ),
|
||||
'keywords' => [ 'system', 'info', 'environment', 'elementor' ],
|
||||
],
|
||||
'role-manager' => [
|
||||
'title' => esc_html__( 'Role Manager', 'elementor' ),
|
||||
'icon' => 'person',
|
||||
'url' => Role_Manager::get_url(),
|
||||
'keywords' => [ 'role', 'manager', 'user', 'elementor' ],
|
||||
],
|
||||
'knowledge-base' => [
|
||||
'title' => esc_html__( 'Knowledge Base', 'elementor' ),
|
||||
'url' => admin_url( 'admin.php?page=go_knowledge_base_site' ),
|
||||
'keywords' => [ 'help', 'knowledge', 'docs', 'elementor' ],
|
||||
],
|
||||
'theme-builder' => [
|
||||
'title' => esc_html__( 'Theme Builder', 'elementor' ),
|
||||
'icon' => 'library-save',
|
||||
'url' => Plugin::$instance->app->get_settings( 'menu_url' ),
|
||||
'keywords' => [ 'template', 'header', 'footer', 'single', 'archive', 'search', '404', 'library' ],
|
||||
],
|
||||
'kit-library' => [
|
||||
'title' => esc_html__( 'Kit Library', 'elementor' ),
|
||||
'icon' => 'kit-parts',
|
||||
'url' => Plugin::$instance->app->get_base_url() . '#/kit-library',
|
||||
'keywords' => [ 'kit library', 'kit', 'library', 'site parts', 'parts', 'assets', 'templates' ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?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 = [] ) {
|
||||
$settings_url = ElementorSettings::get_url();
|
||||
|
||||
return [
|
||||
'general-settings' => [
|
||||
'title' => esc_html__( 'General Settings', 'elementor' ),
|
||||
'url' => $settings_url,
|
||||
'keywords' => [ 'general', 'settings', 'elementor' ],
|
||||
],
|
||||
'advanced' => [
|
||||
'title' => esc_html__( 'Advanced', 'elementor' ),
|
||||
'url' => $settings_url . '#tab-advanced',
|
||||
'keywords' => [ 'advanced', 'settings', 'elementor' ],
|
||||
],
|
||||
'experiments' => [
|
||||
'title' => esc_html__( 'Experiments', 'elementor' ),
|
||||
'url' => $settings_url . '#tab-experiments',
|
||||
'keywords' => [ 'settings', 'elementor', 'experiments' ],
|
||||
],
|
||||
'features' => [
|
||||
'title' => esc_html__( 'Features', 'elementor' ),
|
||||
'url' => $settings_url . '#tab-experiments',
|
||||
'keywords' => [ 'settings', 'elementor', 'features' ],
|
||||
],
|
||||
'element-manager' => [
|
||||
'title' => esc_html__( 'Element Manager', 'elementor' ),
|
||||
'url' => admin_url( 'admin.php?page=' . ElementManagerModule::PAGE_ID ),
|
||||
'keywords' => [ 'settings', 'elements', 'widgets', 'manager' ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Site Category
|
||||
*
|
||||
* Provides general site items.
|
||||
*/
|
||||
class Site extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Site', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'site';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
return [
|
||||
'homepage' => [
|
||||
'title' => esc_html__( 'Homepage', 'elementor' ),
|
||||
'url' => home_url(),
|
||||
'icon' => 'home-heart',
|
||||
'keywords' => [ 'home', 'page' ],
|
||||
],
|
||||
'wordpress-dashboard' => [
|
||||
'title' => esc_html__( 'Dashboard', 'elementor' ),
|
||||
'icon' => 'dashboard',
|
||||
'url' => admin_url(),
|
||||
'keywords' => [ 'dashboard', 'wordpress' ],
|
||||
],
|
||||
'wordpress-menus' => [
|
||||
'title' => esc_html__( 'Menus', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'nav-menus.php' ),
|
||||
'keywords' => [ 'menu', 'wordpress' ],
|
||||
],
|
||||
'wordpress-themes' => [
|
||||
'title' => esc_html__( 'Themes', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'themes.php' ),
|
||||
'keywords' => [ 'themes', 'wordpress' ],
|
||||
],
|
||||
'wordpress-customizer' => [
|
||||
'title' => esc_html__( 'Customizer', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'customize.php' ),
|
||||
'keywords' => [ 'customizer', 'wordpress' ],
|
||||
],
|
||||
'wordpress-plugins' => [
|
||||
'title' => esc_html__( 'Plugins', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'plugins.php' ),
|
||||
'keywords' => [ 'plugins', 'wordpress' ],
|
||||
],
|
||||
'wordpress-users' => [
|
||||
'title' => esc_html__( 'Users', 'elementor' ),
|
||||
'icon' => 'wordpress',
|
||||
'url' => admin_url( 'users.php' ),
|
||||
'keywords' => [ 'users', 'profile', 'wordpress' ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder\Categories;
|
||||
|
||||
use Elementor\Core\Common\Modules\Finder\Base_Category;
|
||||
use Elementor\Tools as ElementorTools;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools Category
|
||||
*
|
||||
* Provides items related to Elementor's tools.
|
||||
*/
|
||||
class Tools extends Base_Category {
|
||||
|
||||
/**
|
||||
* Get title.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
return esc_html__( 'Tools', 'elementor' );
|
||||
}
|
||||
|
||||
public function get_id() {
|
||||
return 'tools';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_category_items( array $options = [] ) {
|
||||
$tools_url = ElementorTools::get_url();
|
||||
|
||||
$items = [
|
||||
'tools' => [
|
||||
'title' => esc_html__( 'Tools', 'elementor' ),
|
||||
'icon' => 'tools',
|
||||
'url' => $tools_url,
|
||||
'keywords' => [ 'tools', 'regenerate css', 'safe mode', 'debug bar', 'sync library', 'elementor' ],
|
||||
],
|
||||
'replace-url' => [
|
||||
'title' => esc_html__( 'Replace URL', 'elementor' ),
|
||||
'icon' => 'tools',
|
||||
'url' => $tools_url . '#tab-replace_url',
|
||||
'keywords' => [ 'tools', 'replace url', 'domain', 'elementor' ],
|
||||
],
|
||||
'maintenance-mode' => [
|
||||
'title' => esc_html__( 'Maintenance Mode', 'elementor' ),
|
||||
'icon' => 'tools',
|
||||
'url' => $tools_url . '#tab-maintenance_mode',
|
||||
'keywords' => [ 'tools', 'maintenance', 'coming soon', 'elementor' ],
|
||||
],
|
||||
'import-export' => [
|
||||
'title' => esc_html__( 'Import Export', 'elementor' ),
|
||||
'icon' => 'import-export',
|
||||
'url' => $tools_url . '#tab-import-export-kit',
|
||||
'keywords' => [ 'tools', 'import export', 'import', 'export', 'kit' ],
|
||||
],
|
||||
];
|
||||
|
||||
if ( ElementorTools::can_user_rollback_versions() ) {
|
||||
$items['version-control'] = [
|
||||
'title' => esc_html__( 'Version Control', 'elementor' ),
|
||||
'icon' => 'time-line',
|
||||
'url' => $tools_url . '#tab-versions',
|
||||
'keywords' => [ 'tools', 'version', 'control', 'rollback', 'beta', 'elementor' ],
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Common\Modules\Finder;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
|
||||
use Elementor\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Finder Module
|
||||
*
|
||||
* Responsible for initializing Elementor Finder functionality
|
||||
*/
|
||||
class Module extends BaseModule {
|
||||
|
||||
/**
|
||||
* Categories manager.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @var Categories_Manager
|
||||
*/
|
||||
private $categories_manager;
|
||||
|
||||
/**
|
||||
* Module constructor.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->categories_manager = new Categories_Manager();
|
||||
|
||||
$this->add_template();
|
||||
|
||||
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return 'finder';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add template.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*/
|
||||
public function add_template() {
|
||||
Plugin::$instance->common->add_template( __DIR__ . '/template.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register ajax actions.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param Ajax $ajax
|
||||
*/
|
||||
public function register_ajax_actions( Ajax $ajax ) {
|
||||
$ajax->register_ajax_action( 'finder_get_category_items', [ $this, 'ajax_get_category_items' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax get category items.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access public
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function ajax_get_category_items( array $data ) {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
throw new \Exception( 'Access denied.' );
|
||||
}
|
||||
|
||||
$category = $this->categories_manager->get_categories( $data['category'] );
|
||||
|
||||
return $category->get_category_items( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get init settings.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_init_settings() {
|
||||
$categories = $this->categories_manager->get_categories();
|
||||
|
||||
$categories_data = [];
|
||||
|
||||
foreach ( $categories as $category_name => $category ) {
|
||||
$categories_data[ $category_name ] = array_merge( $category->get_settings(), [ 'name' => $category_name ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Finder categories.
|
||||
*
|
||||
* Filters the list of finder categories. This hook is used to manage Finder
|
||||
* categories - to add new categories, remove and edit existing categories.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @param array $categories_data A list of finder categories.
|
||||
*/
|
||||
$categories_data = apply_filters( 'elementor/finder/categories', $categories_data );
|
||||
|
||||
return [
|
||||
'data' => $categories_data,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Modules\Finder;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
?>
|
||||
<script type="text/template" id="tmpl-elementor-finder">
|
||||
<div id="elementor-finder__search">
|
||||
<i class="eicon-search" aria-hidden="true"></i>
|
||||
<input id="elementor-finder__search__input" placeholder="<?php echo esc_attr__( 'Type to find anything in Elementor', 'elementor' ); ?>" autocomplete="off">
|
||||
</div>
|
||||
<div id="elementor-finder__content"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="tmpl-elementor-finder-results-container">
|
||||
<div id="elementor-finder__no-results"><?php echo esc_html__( 'No Results Found', 'elementor' ); ?></div>
|
||||
<div id="elementor-finder__results"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="tmpl-elementor-finder__results__category">
|
||||
<div class="elementor-finder__results__category__title">{{{ title }}}</div>
|
||||
<div class="elementor-finder__results__category__items"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="tmpl-elementor-finder__results__item">
|
||||
<a href="{{ url }}" class="elementor-finder__results__item__link">
|
||||
<div class="elementor-finder__results__item__icon">
|
||||
<i class="eicon-{{{ icon }}}" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="elementor-finder__results__item__title">{{{ title }}}</div>
|
||||
<# if ( description ) { #>
|
||||
<div class="elementor-finder__results__item__description">- {{{ description }}}</div>
|
||||
<# } #>
|
||||
|
||||
<# if ( lock ) { #>
|
||||
<div class="elementor-finder__results__item__badge"><i class="{{{ lock.badge.icon }}}"></i>{{ lock.badge.text }}</div>
|
||||
<# } #>
|
||||
</a>
|
||||
<# if ( actions.length ) { #>
|
||||
<div class="elementor-finder__results__item__actions">
|
||||
<# jQuery.each( actions, function() { #>
|
||||
<a class="elementor-finder__results__item__action elementor-finder__results__item__action--{{ this.name }}" href="{{ this.url }}" target="_blank">
|
||||
<i class="eicon-{{{ this.icon }}}"></i>
|
||||
</a>
|
||||
<# } ); #>
|
||||
</div>
|
||||
<# } #>
|
||||
</script>
|
||||
Reference in New Issue
Block a user