first commit

This commit is contained in:
DESKTOP-GBA0BK8\Admin
2023-04-08 12:19:53 -04:00
commit 7c8c8b1c76
4586 changed files with 2050693 additions and 0 deletions
@@ -0,0 +1,252 @@
<?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;
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() );
}
/**
* 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',
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.8.1',
true
);
wp_enqueue_script(
'elementor-common',
$this->get_js_assets_url( 'common' ),
[
'jquery',
'jquery-ui-draggable',
'backbone-marionette',
'backbone-radio',
'elementor-common-modules',
'elementor-dialog',
'wp-api-request',
],
ELEMENTOR_VERSION,
true
);
$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/' ),
[],
'5.9.1'
);
wp_enqueue_style(
'elementor-common',
$this->get_css_assets_url( 'common', null, 'default', true ),
[
'elementor-icons',
],
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;
}
}
/**
* 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() {
return [
'version' => ELEMENTOR_VERSION,
'isRTL' => is_rtl(),
'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
'isElementorDebug' => ( defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG ),
'activeModules' => array_keys( $this->get_components() ),
'urls' => [
'assets' => ELEMENTOR_ASSETS_URL,
'rest' => get_rest_url(),
],
];
}
/**
* 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 );
}
}
}
@@ -0,0 +1,320 @@
<?php
namespace Elementor\Core\Common\Modules\Ajax;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Core\Utils\Exceptions;
use Elementor\Plugin;
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, __( '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 );
$this->requests = json_decode( stripslashes( $_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, __( 'Action not found.', 'elementor' ), Exceptions::BAD_REQUEST );
continue;
}
if ( $editor_post_id ) {
$action_data['data']['editor_post_id'] = $editor_post_id;
}
try {
$results = call_user_func( $this->ajax_actions[ $action_data['action'] ]['callback'], $action_data['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 ! empty( $_REQUEST['_nonce'] ) && wp_verify_nonce( $_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;
} else {
echo $json;
}
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,109 @@
<?php
namespace Elementor\Core\Common\Modules\Connect;
use Elementor\Plugin;
use Elementor\Settings;
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() {
$submenu_page = add_submenu_page(
Settings::PAGE_ID,
__( 'Connect', 'elementor' ),
__( 'Connect', 'elementor' ),
'edit_posts',
self::PAGE_ID,
[ $this, 'render_page' ]
);
add_action( 'load-' . $submenu_page, [ $this, 'on_load_page' ] );
}
/**
* @since 2.3.0
* @access public
*/
public function hide_menu_item() {
remove_submenu_page( Settings::PAGE_ID, self::PAGE_ID );
}
/**
* @since 2.3.0
* @access public
*/
public function on_load_page() {
if ( isset( $_GET['action'], $_GET['app'] ) ) {
$manager = Plugin::$instance->common->get_component( 'connect' );
$app_slug = $_GET['app'];
$app = $manager->get_app( $app_slug );
$nonce_action = $_GET['app'] . $_GET['action'];
if ( ! $app ) {
wp_die( 'Unknown app: ' . esc_attr( $app_slug ) );
}
if ( empty( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], $nonce_action ) ) {
wp_die( 'Invalid Nonce', 'Invalid Nonce', [
'back_link' => true,
] );
}
$method = 'action_' . $_GET['action'];
if ( method_exists( $app, $method ) ) {
call_user_func( [ $app, $method ] );
}
}
}
/**
* @since 2.3.0
* @access public
*/
public function render_page() {
$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 \Elementor\Core\Common\Modules\Connect\Apps\Base_App $app */
foreach ( $apps as $app ) {
echo '<div class="elementor-connect-app-wrapper">';
$app->render_admin_widget();
echo '</div>';
}
?>
</div><!-- /.wrap -->
<?php
}
/**
* @since 2.3.0
* @access public
*/
public function __construct() {
self::$url = admin_url( 'admin.php?page=' . self::PAGE_ID );
add_action( 'admin_menu', [ $this, 'register_admin_menu' ], 206 );
add_action( 'admin_head', [ $this, 'hide_menu_item' ] );
}
}
@@ -0,0 +1,629 @@
<?php
namespace Elementor\Core\Common\Modules\Connect\Apps;
use Elementor\Core\Common\Modules\Connect\Admin;
use Elementor\Tracker;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
abstract class Base_App {
const OPTION_NAME_PREFIX = 'elementor_connect_';
const SITE_URL = 'https://my.elementor.com/connect/v1';
const API_URL = 'https://my.elementor.com/api/connect/v1';
protected $data = [];
protected $auth_mode = '';
/**
* @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() {
echo '<h2>' . $this->get_title() . '</h2>';
if ( $this->is_connected() ) {
$remote_user = $this->get( 'user' );
$title = sprintf( __( 'Connected as %s', 'elementor' ), '<strong>' . $remote_user->email . '</strong>' );
$label = __( 'Disconnect', 'elementor' );
$url = $this->get_admin_url( 'disconnect' );
$attr = '';
echo sprintf( '%s <a %s href="%s">%s</a>', $title, $attr, 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>', $this->get_admin_url( 'reset' ), __( '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 ) ) {
wp_die( $response, $response->get_error_message() );
}
// 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( __( '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() {
delete_user_option( get_current_user_id(), 'elementor_connect_common_data' );
if ( current_user_can( 'manage_options' ) ) {
delete_option( 'elementor_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();
}
if ( empty( $_REQUEST['state'] ) || $_REQUEST['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' => $_REQUEST['code'],
'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();
}
if ( ! empty( $response->data_share_opted_in ) && current_user_can( 'manage_options' ) ) {
Tracker::set_opt_in( true );
}
$this->delete( 'state' );
$this->set( (array) $response );
$this->after_connect();
// Add the notice *after* the method `after_connect`, so an app can redirect without the notice.
$this->add_notice( __( '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( __( '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;
// Encode base url, the encode is limited to 64 chars.
$admin_url = \Requests_IDNAEncoder::encode( 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' ), [] );
}
/**
* @since 2.3.0
* @access protected
*/
protected function request( $action, $request_body = [], $as_array = false ) {
$request_body = [
'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() ),
] + $request_body;
$headers = [];
if ( $this->is_connected() ) {
$headers['X-Elementor-Signature'] = hash_hmac( 'sha256', wp_json_encode( $request_body, JSON_NUMERIC_CHECK ), $this->get( 'access_token_secret' ) );
}
$response = wp_remote_post( $this->get_api_url() . '/' . $action, [
'body' => $request_body,
'headers' => $headers,
'timeout' => 25,
] );
if ( is_wp_error( $response ) ) {
wp_die( $response, [
'back_link' => true,
] );
}
$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, $as_array );
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 = isset( $body->code ) ? $body->code : $response_code;
if ( 401 === $code ) {
$this->delete();
$this->action_authorize();
}
return new \WP_Error( $code, $message );
}
return $body;
}
/**
* @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();
$url = add_query_arg( [
'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' ),
], $this->get_remote_site_url() );
return $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() {
if ( $this->get( 'client_id' ) ) {
return;
}
$response = $this->request( 'get_client_id' );
if ( is_wp_error( $response ) ) {
wp_die( $response, $response->get_error_message() );
}
$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 ) );
}
/**
* @since 2.3.0
* @access protected
*/
protected function print_popup_close_script( $url ) {
?>
<script>
if ( opener && opener !== window ) {
opener.jQuery( 'body' ).trigger( 'elementor/connect/success/<?php echo esc_attr( $_REQUEST['callback_id'] ); ?>' );
window.close();
opener.focus();
} else {
location = '<?php echo $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
*/
protected function get_site_key() {
$site_key = get_option( 'elementor_connect_site_key' );
if ( ! $site_key ) {
$site_key = md5( uniqid( wp_generate_password() ) );
update_option( 'elementor_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( $_REQUEST['token'] );
return;
default:
wp_redirect( $this->get_remote_authorize_url() );
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( $_REQUEST['callback_id'] ),
], $redirect_uri );
break;
}
return $redirect_uri;
}
protected function print_notices( $notices ) {
switch ( $this->auth_mode ) {
case 'cli':
foreach ( $notices as $notice ) {
printf( '[%s] %s', $notice['type'], $notice['content'] );
}
break;
default:
echo '<div id="message" class="updated notice is-dismissible"><p>';
foreach ( $notices as $notice ) {
echo wp_kses_post( sprintf( '<div class="%s"><p>%s</p></div>', $notice['type'], wpautop( $notice['content'] ) ) );
}
echo '</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' . __( 'Dismiss', 'elementor' ) . '</span></button></div>';
}
}
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';
}
printf( '%s: <strong style="color:%s">%s</strong><br>', $item['label'], $color, $status );
}
}
/**
* @since 2.3.0
* @access public
*/
public function __construct() {
add_action( 'admin_notices', [ $this, 'admin_notice' ] );
if ( isset( $_REQUEST['mode'] ) ) { // phpcs:ignore -- nonce validation is not require here.
$allowed_auth_modes = [
'popup',
];
if ( defined( 'WP_CLI' ) && WP_CLI ) {
$allowed_auth_modes[] = 'cli';
}
$mode = $_REQUEST['mode']; // phpcs:ignore -- nonce validation is not require here.
if ( in_array( $mode, $allowed_auth_modes, true ) ) {
$this->auth_mode = $mode;
}
}
/**
* 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,35 @@
<?php
namespace Elementor\Core\Common\Modules\Connect\Apps;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
abstract class Common_App extends Base_User_App {
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;
}
}
@@ -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 __( '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,103 @@
<?php
namespace Elementor\Core\Common\Modules\Connect\Apps;
use Elementor\User;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Library extends Common_App {
public function get_title() {
return __( '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', __( '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 );
return $template_content;
}
public function localize_settings( $settings ) {
$is_connected = $this->is_connected();
return array_replace_recursive( $settings, [
'i18n' => [
// Route: library/connect
'library/connect:title' => __( 'Connect to Template Library', 'elementor' ),
'library/connect:message' => __( 'Access this template and our entire library by creating a free personal account', 'elementor' ),
'library/connect:button' => __( 'Get Started', 'elementor' ),
],
'library_connect' => [
'is_connected' => $is_connected,
],
] );
}
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' ] );
}
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( 'elementor_connect_site_key' ),
],
'remote_info_library' => [
'label' => 'Remote Library Info',
'value' => get_option( 'elementor_remote_info_library' ),
],
];
}
protected function init() {
add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
}
}
@@ -0,0 +1,173 @@
<?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\Connect;
use Elementor\Core\Common\Modules\Connect\Apps\Library;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Module extends BaseModule {
/**
* @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(),
];
// Note: The priority 11 is for allowing plugins to add their register callback on elementor init.
add_action( 'elementor/init', [ $this, 'init' ], 11 );
}
/**
* 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();
}
add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
}
public function localize_settings( $settings ) {
return array_replace_recursive( $settings, [
'i18n' => [
'connect_error' => __( 'Unable to connect', 'elementor' ),
'connected_successfully' => __( 'Connected successfully', 'elementor' ),
],
] );
}
/**
* 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;
}
}
@@ -0,0 +1,76 @@
<?php
namespace Elementor\Core\Common\Modules\Finder;
use Elementor\Core\Base\Base_Object;
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 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,94 @@
<?php
namespace Elementor\Core\Common\Modules\Finder;
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
* @access public
* @param string $category_name
* @param Base_Category $category
*/
public function add_category( $category_name, Base_Category $category ) {
$this->categories[ $category_name ] = $category;
}
/**
* Get categories.
*
* @since 2.3.0
* @access public
* @param string $category
*
* @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 finder default 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->add_category( $category_name, 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
*
* @param Categories_Manager $this.
*/
do_action( 'elementor/finder/categories/init', $this );
}
}
@@ -0,0 +1,71 @@
<?php
namespace Elementor\Core\Common\Modules\Finder\Categories;
use Elementor\Core\Common\Modules\Finder\Base_Category;
use Elementor\TemplateLibrary\Source_Local;
use Elementor\Utils;
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 __( 'Create', 'elementor' );
}
/**
* Get category items.
*
* @since 2.3.0
* @access public
*
* @param array $options
*
* @return array
*/
public function get_category_items( array $options = [] ) {
$elementor_supported_post_types = get_post_types_by_support( 'elementor' );
$items = [];
foreach ( $elementor_supported_post_types as $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 ) {
continue;
}
if ( Source_Local::CPT === $post_type ) {
$url = admin_url( Source_Local::ADMIN_MENU_SLUG . '#add_new' );
} else {
$url = Utils::get_create_new_post_url( $post_type );
}
$items[ $post_type ] = [
/* translators: %s the title of the post type */
'title' => sprintf( __( 'Add New %s', 'elementor' ), $post_type_object->labels->singular_name ),
'icon' => 'plus-circle-o',
'url' => $url,
'keywords' => [ 'post', 'page', 'template', 'new', 'create' ],
];
}
return $items;
}
}
@@ -0,0 +1,138 @@
<?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 __( 'Edit', 'elementor' );
}
/**
* 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,
] );
// TODO: Remove on 2.4.0.
unset( $document_types['widget'] );
$recently_edited_query_args = [
'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 = __( '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,68 @@
<?php
namespace Elementor\Core\Common\Modules\Finder\Categories;
use Elementor\Core\Common\Modules\Finder\Base_Category;
use Elementor\Core\RoleManager\Role_Manager;
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 __( 'General', 'elementor' );
}
/**
* 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' => _x( 'Saved Templates', 'Template Library', 'elementor' ),
'icon' => 'library-save',
'url' => Source_Local::get_admin_url(),
'keywords' => [ 'template', 'section', 'page', 'library' ],
],
'system-info' => [
'title' => __( 'System Info', 'elementor' ),
'icon' => 'info-circle-o',
'url' => admin_url( 'admin.php?page=elementor-system-info' ),
'keywords' => [ 'system', 'info', 'environment', 'elementor' ],
],
'role-manager' => [
'title' => __( 'Role Manager', 'elementor' ),
'icon' => 'person',
'url' => Role_Manager::get_url(),
'keywords' => [ 'role', 'manager', 'user', 'elementor' ],
],
'knowledge-base' => [
'title' => __( 'Knowledge Base', 'elementor' ),
'url' => admin_url( 'admin.php?page=go_knowledge_base_site' ),
'keywords' => [ 'help', 'knowledge', 'docs', 'elementor' ],
],
];
}
}
@@ -0,0 +1,57 @@
<?php
namespace Elementor\Core\Common\Modules\Finder\Categories;
use Elementor\Core\Common\Modules\Finder\Base_Category;
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 __( 'Settings', 'elementor' );
}
/**
* 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' => __( 'General Settings', 'elementor' ),
'url' => $settings_url,
'keywords' => [ 'general', 'settings', 'elementor' ],
],
'advanced' => [
'title' => __( 'Advanced', 'elementor' ),
'url' => $settings_url . '#tab-advanced',
'keywords' => [ 'advanced', 'settings', 'elementor' ],
],
];
}
}
@@ -0,0 +1,85 @@
<?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 __( 'Site', 'elementor' );
}
/**
* Get category items.
*
* @since 2.3.0
* @access public
*
* @param array $options
*
* @return array
*/
public function get_category_items( array $options = [] ) {
return [
'homepage' => [
'title' => __( 'Homepage', 'elementor' ),
'url' => home_url(),
'icon' => 'home-heart',
'keywords' => [ 'home', 'page' ],
],
'wordpress-dashboard' => [
'title' => __( 'Dashboard', 'elementor' ),
'icon' => 'dashboard',
'url' => admin_url(),
'keywords' => [ 'dashboard', 'wordpress' ],
],
'wordpress-menus' => [
'title' => __( 'Menus', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'nav-menus.php' ),
'keywords' => [ 'menu', 'wordpress' ],
],
'wordpress-themes' => [
'title' => __( 'Themes', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'themes.php' ),
'keywords' => [ 'themes', 'wordpress' ],
],
'wordpress-customizer' => [
'title' => __( 'Customizer', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'customize.php' ),
'keywords' => [ 'customizer', 'wordpress' ],
],
'wordpress-plugins' => [
'title' => __( 'Plugins', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'plugins.php' ),
'keywords' => [ 'plugins', 'wordpress' ],
],
'wordpress-users' => [
'title' => __( 'Users', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'users.php' ),
'keywords' => [ 'users', 'profile', 'wordpress' ],
],
];
}
}
@@ -0,0 +1,71 @@
<?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 __( 'Tools', 'elementor' );
}
/**
* 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();
return [
'tools' => [
'title' => __( 'Tools', 'elementor' ),
'icon' => 'tools',
'url' => $tools_url,
'keywords' => [ 'tools', 'regenerate css', 'safe mode', 'debug bar', 'sync library', 'elementor' ],
],
'replace-url' => [
'title' => __( 'Replace URL', 'elementor' ),
'icon' => 'tools',
'url' => $tools_url . '#tab-replace_url',
'keywords' => [ 'tools', 'replace url', 'domain', 'elementor' ],
],
'version-control' => [
'title' => __( 'Version Control', 'elementor' ),
'icon' => 'time-line',
'url' => $tools_url . '#tab-versions',
'keywords' => [ 'tools', 'version', 'control', 'rollback', 'beta', 'elementor' ],
],
'maintenance-mode' => [
'title' => __( 'Maintenance Mode', 'elementor' ),
'icon' => 'tools',
'url' => $tools_url . '#tab-maintenance_mode',
'keywords' => [ 'tools', 'maintenance', 'coming soon', 'elementor' ],
],
];
}
}
@@ -0,0 +1,119 @@
<?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 ) {
$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 ] );
}
$categories_data = apply_filters( 'elementor/finder/categories', $categories_data );
return [
'data' => $categories_data,
'i18n' => [
'finder' => __( 'Finder', 'elementor' ),
],
];
}
}
@@ -0,0 +1,47 @@
<?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"></i>
<input id="elementor-finder__search__input" placeholder="<?php echo __( 'Type to find anything in Elementor', 'elementor' ); ?>">
</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 __( '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 }}}"></i>
</div>
<div class="elementor-finder__results__item__title">{{{ title }}}</div>
<# if ( description ) { #>
<div class="elementor-finder__results__item__description">- {{{ description }}}</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>