dvadf
File manager - Edit - /home/theblueo/tv/wp-includes/pomo/lib/base.tar
Back
module.php 0000666 00000013767 15214223211 0006555 0 ustar 00 <?php namespace Elementor\Core\Base; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor module. * * An abstract class that provides the needed properties and methods to * manage and handle modules in inheriting classes. * * @since 1.7.0 * @abstract */ abstract class Module extends Base_Object { /** * Module class reflection. * * Holds the information about a class. * * @since 1.7.0 * @access private * * @var \ReflectionClass */ private $reflection; /** * Module components. * * Holds the module components. * * @since 1.7.0 * @access private * * @var array */ private $components = []; /** * Module instance. * * Holds the module instance. * * @since 1.7.0 * @access protected * * @var Module */ protected static $_instances = []; /** * Get module name. * * Retrieve the module name. * * @since 1.7.0 * @access public * @abstract * * @return string Module name. */ abstract public function get_name(); /** * Instance. * * Ensures only one instance of the module class is loaded or can be loaded. * * @since 1.7.0 * @access public * @static * * @return Module An instance of the class. */ public static function instance() { $class_name = static::class_name(); if ( empty( static::$_instances[ $class_name ] ) ) { static::$_instances[ $class_name ] = new static(); } return static::$_instances[ $class_name ]; } /** * @since 2.0.0 * @access public * @static */ public static function is_active() { return true; } /** * Class name. * * Retrieve the name of the class. * * @since 1.7.0 * @access public * @static */ public static function class_name() { return get_called_class(); } /** * Clone. * * Disable class cloning and throw an error on object clone. * * The whole idea of the singleton design pattern is that there is a single * object. Therefore, we don't want the object to be cloned. * * @since 1.7.0 * @access public */ public function __clone() { // Cloning instances of the class is forbidden _doing_it_wrong( __FUNCTION__, esc_html__( 'Something went wrong.', 'elementor' ), '1.0.0' ); } /** * Wakeup. * * Disable unserializing of the class. * * @since 1.7.0 * @access public */ public function __wakeup() { // Unserializing instances of the class is forbidden _doing_it_wrong( __FUNCTION__, esc_html__( 'Something went wrong.', 'elementor' ), '1.0.0' ); } /** * @since 2.0.0 * @access public */ public function get_reflection() { if ( null === $this->reflection ) { $this->reflection = new \ReflectionClass( $this ); } return $this->reflection; } /** * Add module component. * * Add new component to the current module. * * @since 1.7.0 * @access public * * @param string $id Component ID. * @param mixed $instance An instance of the component. */ public function add_component( $id, $instance ) { $this->components[ $id ] = $instance; } /** * @since 2.3.0 * @access public * @return Module[] */ public function get_components() { return $this->components; } /** * Get module component. * * Retrieve the module component. * * @since 1.7.0 * @access public * * @param string $id Component ID. * * @return mixed An instance of the component, or `false` if the component * doesn't exist. */ public function get_component( $id ) { if ( isset( $this->components[ $id ] ) ) { return $this->components[ $id ]; } return false; } /** * Get assets url. * * @since 2.3.0 * @access protected * * @param string $file_name * @param string $file_extension * @param string $relative_url Optional. Default is null. * @param string $add_min_suffix Optional. Default is 'default'. * * @return string */ final protected function get_assets_url( $file_name, $file_extension, $relative_url = null, $add_min_suffix = 'default' ) { static $is_test_mode = null; if ( null === $is_test_mode ) { $is_test_mode = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_TESTS; } if ( ! $relative_url ) { $relative_url = $this->get_assets_relative_url() . $file_extension . '/'; } $url = $this->get_assets_base_url() . $relative_url . $file_name; if ( 'default' === $add_min_suffix ) { $add_min_suffix = ! $is_test_mode; } if ( $add_min_suffix ) { $url .= '.min'; } return $url . '.' . $file_extension; } /** * Get js assets url * * @since 2.3.0 * @access protected * * @param string $file_name * @param string $relative_url Optional. Default is null. * @param string $add_min_suffix Optional. Default is 'default'. * * @return string */ final protected function get_js_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default' ) { return $this->get_assets_url( $file_name, 'js', $relative_url, $add_min_suffix ); } /** * Get css assets url * * @since 2.3.0 * @access protected * * @param string $file_name * @param string $relative_url Optional. Default is null. * @param string $add_min_suffix Optional. Default is 'default'. * @param bool $add_direction_suffix Optional. Default is `false` * * @return string */ final protected function get_css_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default', $add_direction_suffix = false ) { static $direction_suffix = null; if ( ! $direction_suffix ) { $direction_suffix = is_rtl() ? '-rtl' : ''; } if ( $add_direction_suffix ) { $file_name .= $direction_suffix; } return $this->get_assets_url( $file_name, 'css', $relative_url, $add_min_suffix ); } /** * Get assets base url * * @since 2.6.0 * @access protected * * @return string */ protected function get_assets_base_url() { return ELEMENTOR_URL; } /** * Get assets relative url * * @since 2.3.0 * @access protected * * @return string */ protected function get_assets_relative_url() { return 'assets/'; } } background-task.php 0000666 00000021674 15214223211 0010343 0 ustar 00 <?php namespace Elementor\Core\Base; use Elementor\Plugin; /** * Based on https://github.com/woocommerce/woocommerce/blob/master/includes/abstracts/class-wc-background-process.php * & https://github.com/woocommerce/woocommerce/blob/master/includes/class-wc-background-updater.php */ defined( 'ABSPATH' ) || exit; include_once ELEMENTOR_PATH . '/includes/libraries/wp-background-process/wp-async-request.php'; include_once ELEMENTOR_PATH . '/includes/libraries/wp-background-process/wp-background-process.php'; /** * WC_Background_Process class. */ abstract class Background_Task extends \WP_Background_Process { protected $current_item; /** * Dispatch updater. * * Updater will still run via cron job if this fails for any reason. */ public function dispatch() { $dispatched = parent::dispatch(); if ( is_wp_error( $dispatched ) ) { wp_die( $dispatched ); } } public function query_col( $sql ) { global $wpdb; // Add Calc. $item = $this->get_current_item(); if ( empty( $item['total'] ) ) { $sql = preg_replace( '/^SELECT/', 'SELECT SQL_CALC_FOUND_ROWS', $sql ); } // Add offset & limit. $sql = preg_replace( '/;$/', '', $sql ); $sql .= ' LIMIT %d, %d;'; $results = $wpdb->get_col( $wpdb->prepare( $sql, $this->get_current_offset(), $this->get_limit() ) ); // WPCS: unprepared SQL OK. if ( ! empty( $results ) ) { $this->set_total(); } return $results; } public function should_run_again( $updated_rows ) { return count( $updated_rows ) === $this->get_limit(); } public function get_current_offset() { $limit = $this->get_limit(); return ( $this->current_item['iterate_num'] - 1 ) * $limit; } public function get_limit() { return $this->manager->get_query_limit(); } public function set_total() { global $wpdb; if ( empty( $this->current_item['total'] ) ) { $total_rows = $wpdb->get_var( 'SELECT FOUND_ROWS();' ); $total_iterates = ceil( $total_rows / $this->get_limit() ); $this->current_item['total'] = $total_iterates; } } /** * Complete * * Override if applicable, but ensure that the below actions are * performed, or, call parent::complete(). */ protected function complete() { $this->manager->on_runner_complete( true ); parent::complete(); } public function continue_run() { // Used to fire an action added in WP_Background_Process::_construct() that calls WP_Background_Process::handle_cron_healthcheck(). // This method will make sure the database updates are executed even if cron is disabled. Nothing will happen if the updates are already running. do_action( $this->cron_hook_identifier ); } /** * @return mixed */ public function get_current_item() { return $this->current_item; } /** * Get batch. * * @return \stdClass Return the first batch from the queue. */ protected function get_batch() { $batch = parent::get_batch(); $batch->data = array_filter( (array) $batch->data ); return $batch; } /** * Handle cron healthcheck * * Restart the background process if not already running * and data exists in the queue. */ public function handle_cron_healthcheck() { if ( $this->is_process_running() ) { // Background process already running. return; } if ( $this->is_queue_empty() ) { // No data to process. $this->clear_scheduled_event(); return; } $this->handle(); } /** * Schedule fallback event. */ protected function schedule_event() { if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) { wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier ); } } /** * Is the updater running? * * @return boolean */ public function is_running() { return false === $this->is_queue_empty(); } /** * See if the batch limit has been exceeded. * * @return bool */ protected function batch_limit_exceeded() { return $this->time_exceeded() || $this->memory_exceeded(); } /** * Handle. * * Pass each queue item to the task handler, while remaining * within server memory and time limit constraints. */ protected function handle() { $this->manager->on_runner_start(); $this->lock_process(); do { $batch = $this->get_batch(); foreach ( $batch->data as $key => $value ) { $task = $this->task( $value ); if ( false !== $task ) { $batch->data[ $key ] = $task; } else { unset( $batch->data[ $key ] ); } if ( $this->batch_limit_exceeded() ) { // Batch limits reached. break; } } // Update or delete current batch. if ( ! empty( $batch->data ) ) { $this->update( $batch->key, $batch->data ); } else { $this->delete( $batch->key ); } } while ( ! $this->batch_limit_exceeded() && ! $this->is_queue_empty() ); $this->unlock_process(); // Start next batch or complete process. if ( ! $this->is_queue_empty() ) { $this->dispatch(); } else { $this->complete(); } } /** * Use the protected `is_process_running` method as a public method. * @return bool */ public function is_process_locked() { return $this->is_process_running(); } public function handle_immediately( $callbacks ) { $this->manager->on_runner_start(); $this->lock_process(); foreach ( $callbacks as $callback ) { $item = [ 'callback' => $callback, ]; do { $item = $this->task( $item ); } while ( $item ); } $this->unlock_process(); } /** * Task * * Override this method to perform any actions required on each * queue item. Return the modified item for further processing * in the next pass through. Or, return false to remove the * item from the queue. * * @param array $item * * @return array|bool */ protected function task( $item ) { $result = false; if ( ! isset( $item['iterate_num'] ) ) { $item['iterate_num'] = 1; } $logger = Plugin::$instance->logger->get_logger(); $callback = $this->format_callback_log( $item ); if ( is_callable( $item['callback'] ) ) { $progress = ''; if ( 1 < $item['iterate_num'] ) { if ( empty( $item['total'] ) ) { $progress = sprintf( '(x%s)', $item['iterate_num'] ); } else { $percent = ceil( $item['iterate_num'] / ( $item['total'] / 100 ) ); $progress = sprintf( '(%s of %s, %s%%)', $item['iterate_num'], $item['total'], $percent ); } } $logger->info( sprintf( '%s Start %s', $callback, $progress ) ); $this->current_item = $item; $result = (bool) call_user_func( $item['callback'], $this ); // get back the updated item. $item = $this->current_item; $this->current_item = null; if ( $result ) { if ( empty( $item['total'] ) ) { $logger->info( sprintf( '%s callback needs to run again', $callback ) ); } elseif ( 1 === $item['iterate_num'] ) { $logger->info( sprintf( '%s callback needs to run more %d times', $callback, $item['total'] - $item['iterate_num'] ) ); } $item['iterate_num']++; } else { $logger->info( sprintf( '%s Finished', $callback ) ); } } else { $logger->notice( sprintf( 'Could not find %s callback', $callback ) ); } return $result ? $item : false; } /** * Schedule cron healthcheck. * * @param array $schedules Schedules. * @return array */ public function schedule_cron_healthcheck( $schedules ) { $interval = apply_filters( $this->identifier . '_cron_interval', 5 ); // Adds every 5 minutes to the existing schedules. $schedules[ $this->identifier . '_cron_interval' ] = array( 'interval' => MINUTE_IN_SECONDS * $interval, /* translators: %d: interval */ 'display' => sprintf( __( 'Every %d minutes', 'elementor' ), $interval ), ); return $schedules; } /** * See if the batch limit has been exceeded. * * @return bool */ public function is_memory_exceeded() { return $this->memory_exceeded(); } /** * Delete all batches. * * @return self */ public function delete_all_batches() { global $wpdb; $table = $wpdb->options; $column = 'option_name'; if ( is_multisite() ) { $table = $wpdb->sitemeta; $column = 'meta_key'; } $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%'; $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE {$column} LIKE %s", $key ) ); // @codingStandardsIgnoreLine. return $this; } /** * Kill process. * * Stop processing queue items, clear cronjob and delete all batches. */ public function kill_process() { if ( ! $this->is_queue_empty() ) { $this->delete_all_batches(); wp_clear_scheduled_hook( $this->cron_hook_identifier ); } } public function set_current_item( $item ) { $this->current_item = $item; } protected function format_callback_log( $item ) { return implode( '::', (array) $item['callback'] ); } /** * @var \Elementor\Core\Base\Background_Task_Manager */ protected $manager; public function __construct( $manager ) { $this->manager = $manager; // Uses unique prefix per blog so each blog has separate queue. $this->prefix = 'elementor_' . get_current_blog_id(); $this->action = $this->manager->get_action(); parent::__construct(); } } base-object.php 0000666 00000005540 15214223211 0007434 0 ustar 00 <?php namespace Elementor\Core\Base; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * Base Object * * Base class that provides basic settings handling functionality. * * @since 2.3.0 */ class Base_Object { /** * Settings. * * Holds the object settings. * * @access private * * @var array */ private $settings; /** * Get Settings. * * @since 2.3.0 * @access public * * @param string $setting Optional. The key of the requested setting. Default is null. * * @return mixed An array of all settings, or a single value if `$setting` was specified. */ final public function get_settings( $setting = null ) { $this->ensure_settings(); return self::get_items( $this->settings, $setting ); } /** * Set settings. * * @since 2.3.0 * @access public * * @param array|string $key If key is an array, the settings are overwritten by that array. Otherwise, the * settings of the key will be set to the given `$value` param. * * @param mixed $value Optional. Default is null. */ final public function set_settings( $key, $value = null ) { $this->ensure_settings(); if ( is_array( $key ) ) { $this->settings = $key; } else { $this->settings[ $key ] = $value; } } /** * Delete setting. * * Deletes the settings array or a specific key of the settings array if `$key` is specified. * @since 2.3.0 * @access public * * @param string $key Optional. Default is null. */ public function delete_setting( $key = null ) { if ( $key ) { unset( $this->settings[ $key ] ); } else { $this->settings = []; } } /** * Get items. * * Utility method that receives an array with a needle and returns all the * items that match the needle. If needle is not defined the entire haystack * will be returned. * * @since 2.3.0 * @access protected * @static * * @param array $haystack An array of items. * @param string $needle Optional. Needle. Default is null. * * @return mixed The whole haystack or the needle from the haystack when requested. */ final protected static function get_items( array $haystack, $needle = null ) { if ( $needle ) { return isset( $haystack[ $needle ] ) ? $haystack[ $needle ] : null; } return $haystack; } /** * Get init settings. * * Used to define the default/initial settings of the object. Inheriting classes may implement this method to define * their own default/initial settings. * * @since 2.3.0 * @access protected * * @return array */ protected function get_init_settings() { return []; } /** * Ensure settings. * * Ensures that the `$settings` member is initialized * * @since 2.3.0 * @access private */ private function ensure_settings() { if ( null === $this->settings ) { $this->settings = $this->get_init_settings(); } } } document.php 0000666 00000070403 15214223211 0007074 0 ustar 00 <?php namespace Elementor\Core\Base; use Elementor\Core\Files\CSS\Post as Post_CSS; use Elementor\Core\Utils\Exceptions; use Elementor\Plugin; use Elementor\DB; use Elementor\Controls_Manager; use Elementor\Controls_Stack; use Elementor\User; use Elementor\Core\Settings\Manager as SettingsManager; use Elementor\Utils; use Elementor\Widget_Base; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * Elementor document. * * An abstract class that provides the needed properties and methods to * manage and handle documents in inheriting classes. * * @since 2.0.0 * @abstract */ abstract class Document extends Controls_Stack { /** * Document type meta key. */ const TYPE_META_KEY = '_elementor_template_type'; const PAGE_META_KEY = '_elementor_page_settings'; private $main_id; private static $properties = []; /** * Document post data. * * Holds the document post data. * * @since 2.0.0 * @access protected * * @var \WP_Post WordPress post data. */ protected $post; /** * @since 2.1.0 * @access protected * @static */ protected static function get_editor_panel_categories() { return Plugin::$instance->elements_manager->get_categories(); } /** * Get properties. * * Retrieve the document properties. * * @since 2.0.0 * @access public * @static * * @return array Document properties. */ public static function get_properties() { return [ 'has_elements' => true, 'is_editable' => true, 'edit_capability' => '', 'show_in_finder' => true, 'show_on_admin_bar' => true, 'support_kit' => false, ]; } /** * @since 2.1.0 * @access public * @static */ public static function get_editor_panel_config() { return [ 'title' => static::get_title(), // JS Container title. 'widgets_settings' => [], 'elements_categories' => static::get_editor_panel_categories(), 'default_route' => 'panel/elements/categories', 'has_elements' => static::get_property( 'has_elements' ), 'support_kit' => static::get_property( 'support_kit' ), 'messages' => [ /* translators: %s: the document title. */ 'publish_notification' => sprintf( __( 'Hurray! Your %s is live.', 'elementor' ), static::get_title() ), ], ]; } /** * Get element title. * * Retrieve the element title. * * @since 2.0.0 * @access public * @static * * @return string Element title. */ public static function get_title() { return __( 'Document', 'elementor' ); } /** * Get property. * * Retrieve the document property. * * @since 2.0.0 * @access public * @static * * @param string $key The property key. * * @return mixed The property value. */ public static function get_property( $key ) { $id = static::get_class_full_name(); if ( ! isset( self::$properties[ $id ] ) ) { self::$properties[ $id ] = static::get_properties(); } return self::get_items( self::$properties[ $id ], $key ); } /** * @since 2.0.0 * @access public * @static */ public static function get_class_full_name() { return get_called_class(); } /** * @since 2.0.0 * @access public */ public function get_unique_name() { return $this->get_name() . '-' . $this->post->ID; } /** * @since 2.3.0 * @access public */ public function get_post_type_title() { $post_type_object = get_post_type_object( $this->post->post_type ); return $post_type_object->labels->singular_name; } /** * @since 2.0.12 * @deprecated 2.4.0 Use `Document::get_remote_library_config()` instead * @access public */ public function get_remote_library_type() { _deprecated_function( __METHOD__, '2.4.0', __CLASS__ . '::get_remote_library_config()' ); } /** * @since 2.0.0 * @access public */ public function get_main_id() { if ( ! $this->main_id ) { $post_id = $this->post->ID; $parent_post_id = wp_is_post_revision( $post_id ); if ( $parent_post_id ) { $post_id = $parent_post_id; } $this->main_id = $post_id; } return $this->main_id; } /** * @since 2.0.0 * @access public * * @param $data * * @throws \Exception If the widget was not found. * * @return string */ public function render_element( $data ) { // Start buffering ob_start(); /** @var Widget_Base $widget */ $widget = Plugin::$instance->elements_manager->create_element_instance( $data ); if ( ! $widget ) { throw new \Exception( 'Widget not found.' ); } $widget->render_content(); $render_html = ob_get_clean(); return $render_html; } /** * @since 2.0.0 * @access public */ public function get_main_post() { return get_post( $this->get_main_id() ); } /** * @since 2.0.6 * @deprecated 2.4.0 Use `Document::get_container_attributes()` instead * @access public */ public function get_container_classes() { _deprecated_function( __METHOD__, '2.4.0', __CLASS__ . '::get_container_attributes()' ); return ''; } public function get_container_attributes() { $id = $this->get_main_id(); $attributes = [ 'data-elementor-type' => $this->get_name(), 'data-elementor-id' => $id, 'class' => 'elementor elementor-' . $id, ]; $version_meta = $this->get_main_meta( '_elementor_version' ); if ( version_compare( $version_meta, '2.5.0', '<' ) ) { $attributes['class'] .= ' elementor-bc-flex-widget'; } if ( Plugin::$instance->preview->is_preview() ) { $attributes['data-elementor-title'] = static::get_title(); } else { $attributes['data-elementor-settings'] = wp_json_encode( $this->get_frontend_settings() ); } return $attributes; } /** * @since 2.0.0 * @access public */ public function get_wp_preview_url() { $main_post_id = $this->get_main_id(); $document = $this; // Ajax request from editor. if ( ! empty( $_POST['initial_document_id'] ) ) { $document = Plugin::$instance->documents->get( $_POST['initial_document_id'] ); } $url = get_preview_post_link( $document->get_main_id(), [ 'preview_id' => $main_post_id, 'preview_nonce' => wp_create_nonce( 'post_preview_' . $main_post_id ), ] ); /** * Document "WordPress preview" URL. * * Filters the WordPress preview URL. * * @since 2.0.0 * * @param string $url WordPress preview URL. * @param Document $this The document instance. */ $url = apply_filters( 'elementor/document/urls/wp_preview', $url, $this ); return $url; } /** * @since 2.0.0 * @access public */ public function get_exit_to_dashboard_url() { $url = get_edit_post_link( $this->get_main_id(), 'raw' ); /** * Document "exit to dashboard" URL. * * Filters the "Exit To Dashboard" URL. * * @since 2.0.0 * * @param string $url The exit URL * @param Document $this The document instance. */ $url = apply_filters( 'elementor/document/urls/exit_to_dashboard', $url, $this ); return $url; } /** * Get auto-saved post revision. * * Retrieve the auto-saved post revision that is newer than current post. * * @since 2.0.0 * @access public * * * @return bool|Document */ public function get_newer_autosave() { $autosave = $this->get_autosave(); // Detect if there exists an autosave newer than the post. if ( $autosave && mysql2date( 'U', $autosave->get_post()->post_modified_gmt, false ) > mysql2date( 'U', $this->post->post_modified_gmt, false ) ) { return $autosave; } return false; } /** * @since 2.0.0 * @access public */ public function is_autosave() { return wp_is_post_autosave( $this->post->ID ); } /** * @since 2.0.0 * @access public * * @param int $user_id * @param bool $create * * @return bool|Document */ public function get_autosave( $user_id = 0, $create = false ) { if ( ! $user_id ) { $user_id = get_current_user_id(); } $autosave_id = $this->get_autosave_id( $user_id ); if ( $autosave_id ) { $document = Plugin::$instance->documents->get( $autosave_id ); } elseif ( $create ) { $autosave_id = wp_create_post_autosave( [ 'post_ID' => $this->post->ID, 'post_type' => $this->post->post_type, 'post_title' => $this->post->post_title, 'post_excerpt' => $this->post->post_excerpt, // Hack to cause $autosave_is_different=true in `wp_create_post_autosave`. 'post_content' => '<!-- Created With Elementor -->', 'post_modified' => current_time( 'mysql' ), ] ); Plugin::$instance->db->copy_elementor_meta( $this->post->ID, $autosave_id ); $document = Plugin::$instance->documents->get( $autosave_id ); $document->save_template_type(); } else { $document = false; } return $document; } /** * Add/Remove edit link in dashboard. * * Add or remove an edit link to the post/page action links on the post/pages list table. * * Fired by `post_row_actions` and `page_row_actions` filters. * * @access public * * @param array $actions An array of row action links. * * @return array An updated array of row action links. */ public function filter_admin_row_actions( $actions ) { if ( $this->is_built_with_elementor() && $this->is_editable_by_current_user() ) { $actions['edit_with_elementor'] = sprintf( '<a href="%1$s">%2$s</a>', $this->get_edit_url(), __( 'Edit with Elementor', 'elementor' ) ); } return $actions; } /** * @since 2.0.0 * @access public */ public function is_editable_by_current_user() { $edit_capability = static::get_property( 'edit_capability' ); if ( $edit_capability && ! current_user_can( $edit_capability ) ) { return false; } return self::get_property( 'is_editable' ) && User::is_current_user_can_edit( $this->get_main_id() ); } /** * @since 2.9.0 * @access protected */ protected function get_initial_config() { // Get document data *after* the scripts hook - so plugins can run compatibility before get data, but *before* enqueue the editor script - so elements can enqueue their own scripts that depended in editor script. $locked_user = Plugin::$instance->editor->get_locked_user( $this->get_main_id() ); if ( $locked_user ) { $locked_user = $locked_user->display_name; } $post_type_object = get_post_type_object( $this->get_main_post()->post_type ); $settings = SettingsManager::get_settings_managers_config(); $config = [ 'id' => $this->get_main_id(), 'type' => $this->get_name(), 'version' => $this->get_main_meta( '_elementor_version' ), 'settings' => $settings['page'], 'remoteLibrary' => $this->get_remote_library_config(), 'last_edited' => $this->get_last_edited(), 'panel' => static::get_editor_panel_config(), 'container' => 'body', 'post_type_title' => $this->get_post_type_title(), 'user' => [ 'can_publish' => current_user_can( $post_type_object->cap->publish_posts ), // Deprecated config since 2.9.0. 'locked' => $locked_user, ], 'urls' => [ 'exit_to_dashboard' => $this->get_exit_to_dashboard_url(), 'preview' => $this->get_preview_url(), 'wp_preview' => $this->get_wp_preview_url(), 'permalink' => $this->get_permalink(), 'have_a_look' => $this->get_have_a_look_url(), ], ]; if ( static::get_property( 'has_elements' ) ) { $config['elements'] = $this->get_elements_raw_data( null, true ); $config['widgets'] = Plugin::$instance->widgets_manager->get_widget_types_config(); } $additional_config = apply_filters( 'elementor/document/config', [], $this->get_main_id() ); if ( ! empty( $additional_config ) ) { $config = array_replace_recursive( $config, $additional_config ); } return $config; } /** * @since 2.0.0 * @access protected */ protected function _register_controls() { $this->register_document_controls(); /** * Register document controls. * * Fires after Elementor registers the document controls. * * @since 2.0.0 * * @param Document $this The document instance. */ do_action( 'elementor/documents/register_controls', $this ); } /** * @since 2.0.0 * @access public * * @param $data * * @return bool */ public function save( $data ) { if ( ! $this->is_editable_by_current_user() ) { return false; } /** * Before document save. * * Fires when document save starts on Elementor. * * @since 2.5.12 * * @param \Elementor\Core\Base\Document $this The current document. * @param $data. */ do_action( 'elementor/document/before_save', $this, $data ); if ( ! current_user_can( 'unfiltered_html' ) ) { $data = wp_kses_post_deep( $data ); } if ( ! empty( $data['settings'] ) ) { if ( isset( $data['settings']['post_status'] ) && DB::STATUS_AUTOSAVE === $data['settings']['post_status'] ) { if ( ! defined( 'DOING_AUTOSAVE' ) ) { define( 'DOING_AUTOSAVE', true ); } } $this->save_settings( $data['settings'] ); // Refresh post after save settings. $this->post = get_post( $this->post->ID ); } // Don't check is_empty, because an empty array should be saved. if ( isset( $data['elements'] ) && is_array( $data['elements'] ) ) { $this->save_elements( $data['elements'] ); } $this->save_template_type(); $this->save_version(); // Remove Post CSS $post_css = Post_CSS::create( $this->post->ID ); $post_css->delete(); /** * After document save. * * Fires when document save is complete. * * @since 2.5.12 * * @param \Elementor\Core\Base\Document $this The current document. * @param $data. */ do_action( 'elementor/document/after_save', $this, $data ); return true; } /** * Is built with Elementor. * * Check whether the post was built with Elementor. * * @since 2.0.0 * @access public * * @return bool Whether the post was built with Elementor. */ public function is_built_with_elementor() { return ! ! get_post_meta( $this->post->ID, '_elementor_edit_mode', true ); } /** * @since 2.0.0 * @access public * @static * * @return mixed */ public function get_edit_url() { $url = add_query_arg( [ 'post' => $this->get_main_id(), 'action' => 'elementor', ], admin_url( 'post.php' ) ); /** * Document edit url. * * Filters the document edit url. * * @since 2.0.0 * * @param string $url The edit url. * @param Document $this The document instance. */ $url = apply_filters( 'elementor/document/urls/edit', $url, $this ); return $url; } /** * @since 2.0.0 * @access public */ public function get_preview_url() { /** * Use a static var - to avoid change the `ver` parameter on every call. */ static $url; if ( empty( $url ) ) { add_filter( 'pre_option_permalink_structure', '__return_empty_string' ); $url = set_url_scheme( add_query_arg( [ 'elementor-preview' => $this->get_main_id(), 'ver' => time(), ], $this->get_permalink() ) ); remove_filter( 'pre_option_permalink_structure', '__return_empty_string' ); /** * Document preview URL. * * Filters the document preview URL. * * @since 2.0.0 * * @param string $url The preview URL. * @param Document $this The document instance. */ $url = apply_filters( 'elementor/document/urls/preview', $url, $this ); } return $url; } /** * @since 2.0.0 * @access public * * @param string $key * * @return array */ public function get_json_meta( $key ) { $meta = get_post_meta( $this->post->ID, $key, true ); if ( is_string( $meta ) && ! empty( $meta ) ) { $meta = json_decode( $meta, true ); } if ( empty( $meta ) ) { $meta = []; } return $meta; } /** * @since 2.0.0 * @access public * * @param null $data * @param bool $with_html_content * * @return array */ public function get_elements_raw_data( $data = null, $with_html_content = false ) { if ( ! static::get_property( 'has_elements' ) ) { return []; } if ( is_null( $data ) ) { $data = $this->get_elements_data(); } // Change the current documents, so widgets can use `documents->get_current` and other post data Plugin::$instance->documents->switch_to_document( $this ); $editor_data = []; foreach ( $data as $element_data ) { $element = Plugin::$instance->elements_manager->create_element_instance( $element_data ); if ( ! $element ) { continue; } $editor_data[] = $element->get_raw_data( $with_html_content ); } // End foreach(). Plugin::$instance->documents->restore_document(); return $editor_data; } /** * @since 2.0.0 * @access public * * @param string $status * * @return array */ public function get_elements_data( $status = DB::STATUS_PUBLISH ) { $elements = $this->get_json_meta( '_elementor_data' ); if ( DB::STATUS_DRAFT === $status ) { $autosave = $this->get_newer_autosave(); if ( is_object( $autosave ) ) { $autosave_elements = Plugin::$instance->documents ->get( $autosave->get_post()->ID ) ->get_json_meta( '_elementor_data' ); } } if ( Plugin::$instance->editor->is_edit_mode() ) { if ( empty( $elements ) && empty( $autosave_elements ) ) { // Convert to Elementor. $elements = $this->convert_to_elementor(); if ( $this->is_autosave() ) { Plugin::$instance->db->copy_elementor_meta( $this->post->post_parent, $this->post->ID ); } } } if ( ! empty( $autosave_elements ) ) { $elements = $autosave_elements; } return $elements; } /** * @since 2.3.0 * @access public */ public function convert_to_elementor() { $this->save( [] ); if ( empty( $this->post->post_content ) ) { return []; } // Check if it's only a shortcode. preg_match_all( '/' . get_shortcode_regex() . '/', $this->post->post_content, $matches, PREG_SET_ORDER ); if ( ! empty( $matches ) ) { foreach ( $matches as $shortcode ) { if ( trim( $this->post->post_content ) === $shortcode[0] ) { $widget_type = Plugin::$instance->widgets_manager->get_widget_types( 'shortcode' ); $settings = [ 'shortcode' => $this->post->post_content, ]; break; } } } if ( empty( $widget_type ) ) { $widget_type = Plugin::$instance->widgets_manager->get_widget_types( 'text-editor' ); $settings = [ 'editor' => $this->post->post_content, ]; } // TODO: Better coding to start template for editor return [ [ 'id' => Utils::generate_random_string(), 'elType' => 'section', 'elements' => [ [ 'id' => Utils::generate_random_string(), 'elType' => 'column', 'elements' => [ [ 'id' => Utils::generate_random_string(), 'elType' => $widget_type::get_type(), 'widgetType' => $widget_type->get_name(), 'settings' => $settings, ], ], ], ], ], ]; } /** * @since 2.1.3 * @access public */ public function print_elements_with_wrapper( $elements_data = null ) { if ( ! $elements_data ) { $elements_data = $this->get_elements_data(); } ?> <div <?php echo Utils::render_html_attributes( $this->get_container_attributes() ); ?>> <div class="elementor-inner"> <div class="elementor-section-wrap"> <?php $this->print_elements( $elements_data ); ?> </div> </div> </div> <?php } /** * @since 2.0.0 * @access public */ public function get_css_wrapper_selector() { return ''; } /** * @since 2.0.0 * @access public */ public function get_panel_page_settings() { return [ /* translators: %s: Document title */ 'title' => sprintf( __( '%s Settings', 'elementor' ), static::get_title() ), ]; } /** * @since 2.0.0 * @access public */ public function get_post() { return $this->post; } /** * @since 2.0.0 * @access public */ public function get_permalink() { return get_permalink( $this->get_main_id() ); } /** * @since 2.0.8 * @access public */ public function get_content( $with_css = false ) { return Plugin::$instance->frontend->get_builder_content( $this->post->ID, $with_css ); } /** * @since 2.0.0 * @access public */ public function delete() { if ( 'revision' === $this->post->post_type ) { $deleted = wp_delete_post_revision( $this->post ); } else { $deleted = wp_delete_post( $this->post->ID ); } return $deleted && ! is_wp_error( $deleted ); } /** * Save editor elements. * * Save data from the editor to the database. * * @since 2.0.0 * @access protected * * @param array $elements */ protected function save_elements( $elements ) { $editor_data = $this->get_elements_raw_data( $elements ); // We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta` $json_value = wp_slash( wp_json_encode( $editor_data ) ); // Don't use `update_post_meta` that can't handle `revision` post type $is_meta_updated = update_metadata( 'post', $this->post->ID, '_elementor_data', $json_value ); /** * Before saving data. * * Fires before Elementor saves data to the database. * * @since 1.0.0 * * @param string $status Post status. * @param int|bool $is_meta_updated Meta ID if the key didn't exist, true on successful update, false on failure. */ do_action( 'elementor/db/before_save', $this->post->post_status, $is_meta_updated ); Plugin::$instance->db->save_plain_text( $this->post->ID ); /** * After saving data. * * Fires after Elementor saves data to the database. * * @since 1.0.0 * * @param int $post_id The ID of the post. * @param array $editor_data Sanitize posted data. */ do_action( 'elementor/editor/after_save', $this->post->ID, $editor_data ); } /** * @since 2.0.0 * @access public * * @param int $user_id Optional. User ID. Default value is `0`. * * @return bool|int */ public function get_autosave_id( $user_id = 0 ) { if ( ! $user_id ) { $user_id = get_current_user_id(); } $autosave = Utils::get_post_autosave( $this->post->ID, $user_id ); if ( $autosave ) { return $autosave->ID; } return false; } public function save_version() { if ( ! defined( 'IS_ELEMENTOR_UPGRADE' ) ) { // Save per revision. $this->update_meta( '_elementor_version', ELEMENTOR_VERSION ); /** * Document version save. * * Fires when document version is saved on Elementor. * Will not fire during Elementor Upgrade. * * @since 2.5.12 * * @param \Elementor\Core\Base\Document $this The current document. * */ do_action( 'elementor/document/save_version', $this ); } } /** * @since 2.0.0 * @access public * @deprecated 2.2.0 Use `Document::save_template_type()`. */ public function save_type() { _deprecated_function( __METHOD__, '2.2.0', __CLASS__ . '::save_template_type()' ); $this->save_template_type(); } /** * @since 2.3.0 * @access public */ public function save_template_type() { return $this->update_main_meta( self::TYPE_META_KEY, $this->get_name() ); } /** * @since 2.3.0 * @access public */ public function get_template_type() { return $this->get_main_meta( self::TYPE_META_KEY ); } /** * @since 2.0.0 * @access public * * @param string $key Meta data key. * * @return mixed */ public function get_main_meta( $key ) { return get_post_meta( $this->get_main_id(), $key, true ); } /** * @since 2.0.4 * @access public * * @param string $key Meta data key. * @param string $value Meta data value. * * @return bool|int */ public function update_main_meta( $key, $value ) { return update_post_meta( $this->get_main_id(), $key, $value ); } /** * @since 2.0.4 * @access public * * @param string $key Meta data key. * @param string $value Optional. Meta data value. Default is an empty string. * * @return bool */ public function delete_main_meta( $key, $value = '' ) { return delete_post_meta( $this->get_main_id(), $key, $value ); } /** * @since 2.0.0 * @access public * * @param string $key Meta data key. * * @return mixed */ public function get_meta( $key ) { return get_post_meta( $this->post->ID, $key, true ); } /** * @since 2.0.0 * @access public * * @param string $key Meta data key. * @param mixed $value Meta data value. * * @return bool|int */ public function update_meta( $key, $value ) { // Use `update_metadata` in order to work also with revisions. return update_metadata( 'post', $this->post->ID, $key, $value ); } /** * @since 2.0.3 * @access public * * @param string $key Meta data key. * @param string $value Meta data value. * * @return bool */ public function delete_meta( $key, $value = '' ) { // Use `delete_metadata` in order to work also with revisions. return delete_metadata( 'post', $this->post->ID, $key, $value ); } /** * @since 2.0.0 * @access public */ public function get_last_edited() { $post = $this->post; $autosave_post = $this->get_autosave(); if ( $autosave_post ) { $post = $autosave_post->get_post(); } $date = date_i18n( _x( 'M j, H:i', 'revision date format', 'elementor' ), strtotime( $post->post_modified ) ); $display_name = get_the_author_meta( 'display_name', $post->post_author ); if ( $autosave_post || 'revision' === $post->post_type ) { /* translators: 1: Saving date, 2: Author display name */ $last_edited = sprintf( __( 'Draft saved on %1$s by %2$s', 'elementor' ), '<time>' . $date . '</time>', $display_name ); } else { /* translators: 1: Editing date, 2: Author display name */ $last_edited = sprintf( __( 'Last edited on %1$s by %2$s', 'elementor' ), '<time>' . $date . '</time>', $display_name ); } return $last_edited; } /** * @since 2.0.0 * @access public * * @param array $data * * @throws \Exception If the post does not exist. */ public function __construct( array $data = [] ) { if ( $data ) { if ( empty( $data['post_id'] ) ) { $this->post = new \WP_Post( (object) [] ); } else { $this->post = get_post( $data['post_id'] ); if ( ! $this->post ) { throw new \Exception( sprintf( 'Post ID #%s does not exist.', $data['post_id'] ), Exceptions::NOT_FOUND ); } } // Each Control_Stack is based on a unique ID. $data['id'] = $data['post_id']; if ( ! isset( $data['settings'] ) ) { $data['settings'] = []; } $saved_settings = get_post_meta( $this->post->ID, '_elementor_page_settings', true ); if ( ! empty( $saved_settings ) && is_array( $saved_settings ) ) { $data['settings'] += $saved_settings; } } parent::__construct( $data ); } protected function get_remote_library_config() { $config = [ 'type' => 'block', 'default_route' => 'templates/blocks', 'category' => $this->get_name(), 'autoImportSettings' => false, ]; return $config; } /** * @since 2.0.4 * @access protected * * @param $settings */ protected function save_settings( $settings ) { $page_settings_manager = SettingsManager::get_settings_managers( 'page' ); $page_settings_manager->ajax_before_save_settings( $settings, $this->post->ID ); $page_settings_manager->save_settings( $settings, $this->post->ID ); } /** * @since 2.1.3 * @access protected */ protected function print_elements( $elements_data ) { foreach ( $elements_data as $element_data ) { $element = Plugin::$instance->elements_manager->create_element_instance( $element_data ); if ( ! $element ) { continue; } $element->print_element(); } } protected function register_document_controls() { $this->start_controls_section( 'document_settings', [ 'label' => __( 'General Settings', 'elementor' ), 'tab' => Controls_Manager::TAB_SETTINGS, ] ); $this->add_control( 'post_title', [ 'label' => __( 'Title', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => $this->post->post_title, 'label_block' => true, 'separator' => 'none', ] ); $post_type_object = get_post_type_object( $this->post->post_type ); $can_publish = $post_type_object && current_user_can( $post_type_object->cap->publish_posts ); $is_published = DB::STATUS_PUBLISH === $this->post->post_status || DB::STATUS_PRIVATE === $this->post->post_status; if ( $is_published || $can_publish || ! Plugin::$instance->editor->is_edit_mode() ) { $statuses = $this->get_post_statuses(); if ( 'future' === $this->get_main_post()->post_status ) { $statuses['future'] = __( 'Future', 'elementor' ); } $this->add_control( 'post_status', [ 'label' => __( 'Status', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => $this->get_main_post()->post_status, 'options' => $statuses, ] ); } $this->end_controls_section(); } protected function get_post_statuses() { return get_post_statuses(); } protected function get_have_a_look_url() { return $this->get_permalink(); } } background-task-manager.php 0000666 00000004744 15214223211 0011752 0 ustar 00 <?php namespace Elementor\Core\Base; use Elementor\Core\Base\Module as BaseModule; use Elementor\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } abstract class Background_Task_Manager extends BaseModule { /** * @var Background_Task */ protected $task_runner; abstract public function get_action(); abstract public function get_plugin_name(); abstract public function get_plugin_label(); abstract public function get_task_runner_class(); abstract public function get_query_limit(); abstract protected function start_run(); public function on_runner_start() { $logger = Plugin::$instance->logger->get_logger(); $logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Started' ); } public function on_runner_complete( $did_tasks = false ) { $logger = Plugin::$instance->logger->get_logger(); $logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Completed' ); } public function get_task_runner() { if ( empty( $this->task_runner ) ) { $class_name = $this->get_task_runner_class(); $this->task_runner = new $class_name( $this ); } return $this->task_runner; } // TODO: Replace with a db settings system. protected function add_flag( $flag ) { add_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag, 1 ); } protected function get_flag( $flag ) { return get_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag ); } protected function delete_flag( $flag ) { delete_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag ); } protected function get_start_action_url() { return wp_nonce_url( add_query_arg( $this->get_action(), 'run' ), $this->get_action() . 'run' ); } protected function get_continue_action_url() { return wp_nonce_url( add_query_arg( $this->get_action(), 'continue' ), $this->get_action() . 'continue' ); } private function continue_run() { $runner = $this->get_task_runner(); $runner->continue_run(); } public function __construct() { if ( empty( $_GET[ $this->get_action() ] ) ) { return; } Plugin::$instance->init_common(); if ( 'run' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'run' ) ) { $this->start_run(); } if ( 'continue' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'continue' ) ) { $this->continue_run(); } wp_safe_redirect( remove_query_arg( [ $this->get_action(), '_wpnonce' ] ) ); die; } } db-upgrades-manager.php 0000666 00000012304 15214223211 0011057 0 ustar 00 <?php namespace Elementor\Core\Base; use Elementor\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } abstract class DB_Upgrades_Manager extends Background_Task_Manager { protected $current_version = null; protected $query_limit = 100; abstract public function get_new_version(); abstract public function get_version_option_name(); abstract public function get_upgrades_class(); abstract public function get_updater_label(); public function get_task_runner_class() { return 'Elementor\Core\Upgrade\Updater'; } public function get_query_limit() { return $this->query_limit; } public function set_query_limit( $limit ) { $this->query_limit = $limit; } public function get_current_version() { if ( null === $this->current_version ) { $this->current_version = get_option( $this->get_version_option_name() ); } return $this->current_version; } public function should_upgrade() { $current_version = $this->get_current_version(); // It's a new install. if ( ! $current_version ) { $this->update_db_version(); return false; } return version_compare( $this->get_new_version(), $current_version, '>' ); } public function on_runner_start() { parent::on_runner_start(); define( 'IS_ELEMENTOR_UPGRADE', true ); } public function on_runner_complete( $did_tasks = false ) { $logger = Plugin::$instance->logger->get_logger(); $logger->info( 'Elementor data updater process has been completed.', [ 'meta' => [ 'plugin' => $this->get_plugin_label(), 'from' => $this->current_version, 'to' => $this->get_new_version(), ], ] ); Plugin::$instance->files_manager->clear_cache(); $this->update_db_version(); if ( $did_tasks ) { $this->add_flag( 'completed' ); } } public function admin_notice_start_upgrade() { $upgrade_link = $this->get_start_action_url(); $message = '<p>' . sprintf( __( '%s Your site database needs to be updated to the latest version.', 'elementor' ), $this->get_updater_label() ) . '</p>'; $message .= '<p>' . sprintf( '<a href="%s" class="button-primary">%s</a>', $upgrade_link, __( 'Update Now', 'elementor' ) ) . '</p>'; echo '<div class="notice notice-error">' . $message . '</div>'; } public function admin_notice_upgrade_is_running() { $upgrade_link = $this->get_continue_action_url(); $message = '<p>' . sprintf( __( '%s Database update process is running in the background.', 'elementor' ), $this->get_updater_label() ) . '</p>'; $message .= '<p>' . __( 'Taking a while?', 'elementor' ) . '<a href="' . $upgrade_link . '" class="button-primary">' . __( 'Click here to run it now', 'elementor' ) . '</a></p>'; echo '<div class="notice notice-warning">' . $message . '</div>'; } public function admin_notice_upgrade_is_completed() { $this->delete_flag( 'completed' ); $message = '<p>' . sprintf( __( '%s The database update process is now complete. Thank you for updating to the latest version!', 'elementor' ), $this->get_updater_label() ) . '</p>'; echo '<div class="notice notice-success">' . $message . '</div>'; } /** * @access protected */ protected function start_run() { $updater = $this->get_task_runner(); if ( $updater->is_running() ) { return; } $upgrade_callbacks = $this->get_upgrade_callbacks(); if ( empty( $upgrade_callbacks ) ) { $this->on_runner_complete(); return; } foreach ( $upgrade_callbacks as $callback ) { $updater->push_to_queue( [ 'callback' => $callback, ] ); } $updater->save()->dispatch(); Plugin::$instance->logger->get_logger()->info( 'Elementor data updater process has been queued.', [ 'meta' => [ 'plugin' => $this->get_plugin_label(), 'from' => $this->current_version, 'to' => $this->get_new_version(), ], ] ); } protected function update_db_version() { update_option( $this->get_version_option_name(), $this->get_new_version() ); } public function get_upgrade_callbacks() { $prefix = '_v_'; $upgrades_class = $this->get_upgrades_class(); $upgrades_reflection = new \ReflectionClass( $upgrades_class ); $callbacks = []; foreach ( $upgrades_reflection->getMethods() as $method ) { $method_name = $method->getName(); if ( false === strpos( $method_name, $prefix ) ) { continue; } if ( ! preg_match_all( "/$prefix(\d+_\d+_\d+)/", $method_name, $matches ) ) { continue; } $method_version = str_replace( '_', '.', $matches[1][0] ); if ( ! version_compare( $method_version, $this->current_version, '>' ) ) { continue; } $callbacks[] = [ $upgrades_class, $method_name ]; } return $callbacks; } public function __construct() { // If upgrade is completed - show the notice only for admins. // Note: in this case `should_upgrade` returns false, because it's already upgraded. if ( is_admin() && current_user_can( 'update_plugins' ) && $this->get_flag( 'completed' ) ) { add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_completed' ] ); } if ( ! $this->should_upgrade() ) { return; } $updater = $this->get_task_runner(); $this->start_run(); if ( $updater->is_running() && current_user_can( 'update_plugins' ) ) { add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_running' ] ); } parent::__construct(); } } app.php 0000666 00000002344 15214223211 0006035 0 ustar 00 <?php namespace Elementor\Core\Base; use Elementor\Utils; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * Base App * * Base app utility class that provides shared functionality of apps. * * @since 2.3.0 */ abstract class App extends Module { /** * Print config. * * Used to print the app and its components settings as a JavaScript object. * * @param string $handle Optional * * @since 2.3.0 * @since 2.6.0 added the `$handle` parameter * @access protected */ final protected function print_config( $handle = null ) { $name = $this->get_name(); $js_var = 'elementor' . str_replace( ' ', '', ucwords( str_replace( '-', ' ', $name ) ) ) . 'Config'; $config = $this->get_settings() + $this->get_components_config(); if ( ! $handle ) { $handle = 'elementor-' . $name; } Utils::print_js_config( $handle, $js_var, $config ); } /** * Get components config. * * Retrieves the app components settings. * * @since 2.3.0 * @access private * * @return array */ private function get_components_config() { $settings = []; foreach ( $this->get_components() as $id => $instance ) { $settings[ $id ] = $instance->get_settings(); } return $settings; } } skin-base.php 0000666 00000020202 15214232264 0007132 0 ustar 00 <?php namespace Elementor; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor skin base. * * An abstract class to register new skins for Elementor widgets. Skins allows * you to add new templates, set custom controls and more. * * To register new skins for your widget use the `add_skin()` method inside the * widget's `_register_skins()` method. * * @since 1.0.0 * @abstract */ abstract class Skin_Base { /** * Parent widget. * * Holds the parent widget of the skin. Default value is null, no parent widget. * * @access protected * * @var Widget_Base|null */ protected $parent = null; /** * Skin base constructor. * * Initializing the skin base class by setting parent widget and registering * controls actions. * * @since 1.0.0 * @access public * @param Widget_Base $parent */ public function __construct( Widget_Base $parent ) { $this->parent = $parent; $this->_register_controls_actions(); } /** * Get skin ID. * * Retrieve the skin ID. * * @since 1.0.0 * @access public * @abstract */ abstract public function get_id(); /** * Get skin title. * * Retrieve the skin title. * * @since 1.0.0 * @access public * @abstract */ abstract public function get_title(); /** * Render skin. * * Generates the final HTML on the frontend. * * @since 1.0.0 * @access public * @abstract */ abstract public function render(); /** * Render skin output in the editor. * * Written as a Backbone JavaScript template and used to generate the live preview. * * @since 1.0.0 * @deprecated 1.7.6 * @access public */ public function _content_template() { _deprecated_function( __METHOD__, '1.7.6' ); } /** * Register skin controls actions. * * Run on init and used to register new skins to be injected to the widget. * This method is used to register new actions that specify the location of * the skin in the widget. * * Example usage: * `add_action( 'elementor/element/{widget_id}/{section_id}/before_section_end', [ $this, 'register_controls' ] );` * * @since 1.0.0 * @access protected */ protected function _register_controls_actions() {} /** * Get skin control ID. * * Retrieve the skin control ID. Note that skin controls have special prefix * to distinguish them from regular controls, and from controls in other * skins. * * @since 1.0.0 * @access protected * * @param string $control_base_id Control base ID. * * @return string Control ID. */ protected function get_control_id( $control_base_id ) { $skin_id = str_replace( '-', '_', $this->get_id() ); return $skin_id . '_' . $control_base_id; } /** * Get skin settings. * * Retrieve all the skin settings or, when requested, a specific setting. * * @since 1.0.0 * @TODO: rename to get_setting() and create backward compatibility. * * @access public * * @param string $control_base_id Control base ID. * * @return Widget_Base Widget instance. */ public function get_instance_value( $control_base_id ) { $control_id = $this->get_control_id( $control_base_id ); return $this->parent->get_settings( $control_id ); } /** * Start skin controls section. * * Used to add a new section of controls to the skin. * * @since 1.3.0 * @access public * * @param string $id Section ID. * @param array $args Section arguments. */ public function start_controls_section( $id, $args ) { $args['condition']['_skin'] = $this->get_id(); $this->parent->start_controls_section( $this->get_control_id( $id ), $args ); } /** * End skin controls section. * * Used to close an existing open skin controls section. * * @since 1.3.0 * @access public */ public function end_controls_section() { $this->parent->end_controls_section(); } /** * Add new skin control. * * Register a single control to the allow the user to set/update skin data. * * @since 1.0.0 * @access public * * @param string $id Control ID. * @param array $args Control arguments. * * @return bool True if skin added, False otherwise. */ public function add_control( $id, $args ) { $args['condition']['_skin'] = $this->get_id(); return $this->parent->add_control( $this->get_control_id( $id ), $args ); } /** * Update skin control. * * Change the value of an existing skin control. * * @since 1.3.0 * @since 1.8.1 New `$options` parameter added. * * @access public * * @param string $id Control ID. * @param array $args Control arguments. Only the new fields you want to update. * @param array $options Optional. Some additional options. */ public function update_control( $id, $args, array $options = [] ) { $args['condition']['_skin'] = $this->get_id(); $this->parent->update_control( $this->get_control_id( $id ), $args, $options ); } /** * Remove skin control. * * Unregister an existing skin control. * * @since 1.3.0 * @access public * * @param string $id Control ID. */ public function remove_control( $id ) { $this->parent->remove_control( $this->get_control_id( $id ) ); } /** * Add new responsive skin control. * * Register a set of controls to allow editing based on user screen size. * * @since 1.0.5 * @access public * * @param string $id Responsive control ID. * @param array $args Responsive control arguments. */ public function add_responsive_control( $id, $args ) { $args['condition']['_skin'] = $this->get_id(); $this->parent->add_responsive_control( $this->get_control_id( $id ), $args ); } /** * Update responsive skin control. * * Change the value of an existing responsive skin control. * * @since 1.3.5 * @access public * * @param string $id Responsive control ID. * @param array $args Responsive control arguments. */ public function update_responsive_control( $id, $args ) { $this->parent->update_responsive_control( $this->get_control_id( $id ), $args ); } /** * Remove responsive skin control. * * Unregister an existing skin responsive control. * * @since 1.3.5 * @access public * * @param string $id Responsive control ID. */ public function remove_responsive_control( $id ) { $this->parent->remove_responsive_control( $this->get_control_id( $id ) ); } /** * Start skin controls tab. * * Used to add a new tab inside a group of tabs. * * @since 1.5.0 * @access public * * @param string $id Control ID. * @param array $args Control arguments. */ public function start_controls_tab( $id, $args ) { $args['condition']['_skin'] = $this->get_id(); $this->parent->start_controls_tab( $this->get_control_id( $id ), $args ); } /** * End skin controls tab. * * Used to close an existing open controls tab. * * @since 1.5.0 * @access public */ public function end_controls_tab() { $this->parent->end_controls_tab(); } /** * Start skin controls tabs. * * Used to add a new set of tabs inside a section. * * @since 1.5.0 * @access public * * @param string $id Control ID. */ public function start_controls_tabs( $id ) { $args['condition']['_skin'] = $this->get_id(); $this->parent->start_controls_tabs( $this->get_control_id( $id ) ); } /** * End skin controls tabs. * * Used to close an existing open controls tabs. * * @since 1.5.0 * @access public */ public function end_controls_tabs() { $this->parent->end_controls_tabs(); } /** * Add new group control. * * Register a set of related controls grouped together as a single unified * control. * * @since 1.0.0 * @access public * * @param string $group_name Group control name. * @param array $args Group control arguments. Default is an empty array. */ final public function add_group_control( $group_name, $args = [] ) { $args['name'] = $this->get_control_id( $args['name'] ); $args['condition']['_skin'] = $this->get_id(); $this->parent->add_group_control( $group_name, $args ); } /** * Set parent widget. * * Used to define the parent widget of the skin. * * @since 1.0.0 * @access public * * @param Widget_Base $parent Parent widget. */ public function set_parent( $parent ) { $this->parent = $parent; } } controls-stack.php 0000666 00000151162 15214232264 0010236 0 ustar 00 <?php namespace Elementor; use Elementor\Core\Base\Base_Object; use Elementor\Core\DynamicTags\Manager; use Elementor\Core\Schemes\Manager as Schemes_Manager; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor controls stack. * * An abstract class that provides the needed properties and methods to * manage and handle controls in the editor panel to inheriting classes. * * @since 1.4.0 * @abstract */ abstract class Controls_Stack extends Base_Object { /** * Responsive 'desktop' device name. */ const RESPONSIVE_DESKTOP = 'desktop'; /** * Responsive 'tablet' device name. */ const RESPONSIVE_TABLET = 'tablet'; /** * Responsive 'mobile' device name. */ const RESPONSIVE_MOBILE = 'mobile'; /** * Generic ID. * * Holds the unique ID. * * @access private * * @var string */ private $id; private $active_settings; private $parsed_active_settings; /** * Parsed Dynamic Settings. * * @access private * * @var null|array */ private $parsed_dynamic_settings; /** * Raw Data. * * Holds all the raw data including the element type, the child elements, * the user data. * * @access private * * @var null|array */ private $data; /** * The configuration. * * Holds the configuration used to generate the Elementor editor. It includes * the element name, icon, categories, etc. * * @access private * * @var null|array */ private $config; /** * Current section. * * Holds the current section while inserting a set of controls sections. * * @access private * * @var null|array */ private $current_section; /** * Current tab. * * Holds the current tab while inserting a set of controls tabs. * * @access private * * @var null|array */ private $current_tab; /** * Current popover. * * Holds the current popover while inserting a set of controls. * * @access private * * @var null|array */ private $current_popover; /** * Injection point. * * Holds the injection point in the stack where the control will be inserted. * * @access private * * @var null|array */ private $injection_point; /** * Data sanitized. * * @access private * * @var bool */ private $settings_sanitized = false; /** * Get element name. * * Retrieve the element name. * * @since 1.4.0 * @access public * @abstract * * @return string The name. */ abstract public function get_name(); /** * Get unique name. * * Some classes need to use unique names, this method allows you to create * them. By default it retrieves the regular name. * * @since 1.6.0 * @access public * * @return string Unique name. */ public function get_unique_name() { return $this->get_name(); } /** * Get element ID. * * Retrieve the element generic ID. * * @since 1.4.0 * @access public * * @return string The ID. */ public function get_id() { return $this->id; } /** * Get element ID. * * Retrieve the element generic ID as integer. * * @since 1.8.0 * @access public * * @return string The converted ID. */ public function get_id_int() { return hexdec( $this->id ); } /** * Get the type. * * Retrieve the type, e.g. 'stack', 'section', 'widget' etc. * * @since 1.4.0 * @access public * @static * * @return string The type. */ public static function get_type() { return 'stack'; } /** * @since 2.9.0 * @access public * * @return bool */ public function is_editable() { return true; } /** * Get items. * * Utility method that receives an array with a needle and returns all the * items that match the needle. If needle is not defined the entire haystack * will be returned. * * @since 1.4.0 * @deprecated 2.3.0 Use `Controls_Stack::get_items()` instead * @access protected * @static * * @param array $haystack An array of items. * @param string $needle Optional. Needle. Default is null. * * @return mixed The whole haystack or the needle from the haystack when requested. */ protected static function _get_items( array $haystack, $needle = null ) { _deprecated_function( __METHOD__, '2.3.0', __CLASS__ . '::get_items()' ); if ( $needle ) { return isset( $haystack[ $needle ] ) ? $haystack[ $needle ] : null; } return $haystack; } /** * Get current section. * * When inserting new controls, this method will retrieve the current section. * * @since 1.7.1 * @access public * * @return null|array Current section. */ public function get_current_section() { return $this->current_section; } /** * Get current tab. * * When inserting new controls, this method will retrieve the current tab. * * @since 1.7.1 * @access public * * @return null|array Current tab. */ public function get_current_tab() { return $this->current_tab; } /** * Get controls. * * Retrieve all the controls or, when requested, a specific control. * * @since 1.4.0 * @access public * * @param string $control_id The ID of the requested control. Optional field, * when set it will return a specific control. * Default is null. * * @return mixed Controls list. */ public function get_controls( $control_id = null ) { return self::get_items( $this->get_stack()['controls'], $control_id ); } /** * Get active controls. * * Retrieve an array of active controls that meet the condition field. * * If specific controls was given as a parameter, retrieve active controls * from that list, otherwise check for all the controls available. * * @since 1.4.0 * @since 2.0.9 Added the `controls` and the `settings` parameters. * @access public * * @param array $controls Optional. An array of controls. Default is null. * @param array $settings Optional. Controls settings. Default is null. * * @return array Active controls. */ public function get_active_controls( array $controls = null, array $settings = null ) { if ( ! $controls ) { $controls = $this->get_controls(); } if ( ! $settings ) { $settings = $this->get_controls_settings(); } $active_controls = array_reduce( array_keys( $controls ), function( $active_controls, $control_key ) use ( $controls, $settings ) { $control = $controls[ $control_key ]; if ( $this->is_control_visible( $control, $settings ) ) { $active_controls[ $control_key ] = $control; } return $active_controls; }, [] ); return $active_controls; } /** * Get controls settings. * * Retrieve the settings for all the controls that represent them. * * @since 1.5.0 * @access public * * @return array Controls settings. */ public function get_controls_settings() { return array_intersect_key( $this->get_settings(), $this->get_controls() ); } /** * Add new control to stack. * * Register a single control to allow the user to set/update data. * * This method should be used inside `_register_controls()`. * * @since 1.4.0 * @access public * * @param string $id Control ID. * @param array $args Control arguments. * @param array $options Optional. Control options. Default is an empty array. * * @return bool True if control added, False otherwise. */ public function add_control( $id, array $args, $options = [] ) { $default_options = [ 'overwrite' => false, 'position' => null, ]; $options = array_merge( $default_options, $options ); if ( $options['position'] ) { $this->start_injection( $options['position'] ); } if ( $this->injection_point ) { $options['index'] = $this->injection_point['index']++; } if ( empty( $args['type'] ) || ! in_array( $args['type'], [ Controls_Manager::SECTION, Controls_Manager::WP_WIDGET ], true ) ) { $target_section_args = $this->current_section; $target_tab = $this->current_tab; if ( $this->injection_point ) { $target_section_args = $this->injection_point['section']; if ( ! empty( $this->injection_point['tab'] ) ) { $target_tab = $this->injection_point['tab']; } } if ( null !== $target_section_args ) { if ( ! empty( $args['section'] ) || ! empty( $args['tab'] ) ) { _doing_it_wrong( sprintf( '%s::%s', get_called_class(), __FUNCTION__ ), sprintf( 'Cannot redeclare control with `tab` or `section` args inside section "%s".', $id ), '1.0.0' ); } $args = array_replace_recursive( $target_section_args, $args ); if ( null !== $target_tab ) { $args = array_replace_recursive( $target_tab, $args ); } } elseif ( empty( $args['section'] ) && ( ! $options['overwrite'] || is_wp_error( Plugin::$instance->controls_manager->get_control_from_stack( $this->get_unique_name(), $id ) ) ) ) { wp_die( sprintf( '%s::%s: Cannot add a control outside of a section (use `start_controls_section`).', get_called_class(), __FUNCTION__ ) ); } } if ( $options['position'] ) { $this->end_injection(); } unset( $options['position'] ); if ( $this->current_popover && ! $this->current_popover['initialized'] ) { $args['popover'] = [ 'start' => true, ]; $this->current_popover['initialized'] = true; } return Plugin::$instance->controls_manager->add_control_to_stack( $this, $id, $args, $options ); } /** * Remove control from stack. * * Unregister an existing control and remove it from the stack. * * @since 1.4.0 * @access public * * @param string $control_id Control ID. * * @return bool|\WP_Error */ public function remove_control( $control_id ) { return Plugin::$instance->controls_manager->remove_control_from_stack( $this->get_unique_name(), $control_id ); } /** * Update control in stack. * * Change the value of an existing control in the stack. When you add new * control you set the `$args` parameter, this method allows you to update * the arguments by passing new data. * * @since 1.4.0 * @since 1.8.1 New `$options` parameter added. * * @access public * * @param string $control_id Control ID. * @param array $args Control arguments. Only the new fields you want * to update. * @param array $options Optional. Some additional options. Default is * an empty array. * * @return bool */ public function update_control( $control_id, array $args, array $options = [] ) { $is_updated = Plugin::$instance->controls_manager->update_control_in_stack( $this, $control_id, $args, $options ); if ( ! $is_updated ) { return false; } $control = $this->get_controls( $control_id ); if ( Controls_Manager::SECTION === $control['type'] ) { $section_args = $this->get_section_args( $control_id ); $section_controls = $this->get_section_controls( $control_id ); foreach ( $section_controls as $section_control_id => $section_control ) { $this->update_control( $section_control_id, $section_args, $options ); } } return true; } /** * Get stack. * * Retrieve the stack of controls. * * @since 1.9.2 * @access public * * @return array Stack of controls. */ public function get_stack() { $stack = Plugin::$instance->controls_manager->get_element_stack( $this ); if ( null === $stack ) { $this->init_controls(); return Plugin::$instance->controls_manager->get_element_stack( $this ); } return $stack; } /** * Get position information. * * Retrieve the position while injecting data, based on the element type. * * @since 1.7.0 * @access public * * @param array $position { * The injection position. * * @type string $type Injection type, either `control` or `section`. * Default is `control`. * @type string $at Where to inject. If `$type` is `control` accepts * `before` and `after`. If `$type` is `section` * accepts `start` and `end`. Default values based on * the `type`. * @type string $of Control/Section ID. * @type array $fallback Fallback injection position. When the position is * not found it will try to fetch the fallback * position. * } * * @return bool|array Position info. */ final public function get_position_info( array $position ) { $default_position = [ 'type' => 'control', 'at' => 'after', ]; if ( ! empty( $position['type'] ) && 'section' === $position['type'] ) { $default_position['at'] = 'end'; } $position = array_merge( $default_position, $position ); if ( 'control' === $position['type'] && in_array( $position['at'], [ 'start', 'end' ], true ) || 'section' === $position['type'] && in_array( $position['at'], [ 'before', 'after' ], true ) ) { _doing_it_wrong( sprintf( '%s::%s', get_called_class(), __FUNCTION__ ), 'Invalid position arguments. Use `before` / `after` for control or `start` / `end` for section.', '1.7.0' ); return false; } $target_control_index = $this->get_control_index( $position['of'] ); if ( false === $target_control_index ) { if ( ! empty( $position['fallback'] ) ) { return $this->get_position_info( $position['fallback'] ); } return false; } $target_section_index = $target_control_index; $registered_controls = $this->get_controls(); $controls_keys = array_keys( $registered_controls ); while ( Controls_Manager::SECTION !== $registered_controls[ $controls_keys[ $target_section_index ] ]['type'] ) { $target_section_index--; } if ( 'section' === $position['type'] ) { $target_control_index++; if ( 'end' === $position['at'] ) { while ( Controls_Manager::SECTION !== $registered_controls[ $controls_keys[ $target_control_index ] ]['type'] ) { if ( ++$target_control_index >= count( $registered_controls ) ) { break; } } } } $target_control = $registered_controls[ $controls_keys[ $target_control_index ] ]; if ( 'after' === $position['at'] ) { $target_control_index++; } $section_id = $registered_controls[ $controls_keys[ $target_section_index ] ]['name']; $position_info = [ 'index' => $target_control_index, 'section' => $this->get_section_args( $section_id ), ]; if ( ! empty( $target_control['tabs_wrapper'] ) ) { $position_info['tab'] = [ 'tabs_wrapper' => $target_control['tabs_wrapper'], 'inner_tab' => $target_control['inner_tab'], ]; } return $position_info; } /** * Get control key. * * Retrieve the key of the control based on a given index of the control. * * @since 1.9.2 * @access public * * @param string $control_index Control index. * * @return int Control key. */ final public function get_control_key( $control_index ) { $registered_controls = $this->get_controls(); $controls_keys = array_keys( $registered_controls ); return $controls_keys[ $control_index ]; } /** * Get control index. * * Retrieve the index of the control based on a given key of the control. * * @since 1.7.6 * @access public * * @param string $control_key Control key. * * @return false|int Control index. */ final public function get_control_index( $control_key ) { $controls = $this->get_controls(); $controls_keys = array_keys( $controls ); return array_search( $control_key, $controls_keys ); } /** * Get section controls. * * Retrieve all controls under a specific section. * * @since 1.7.6 * @access public * * @param string $section_id Section ID. * * @return array Section controls */ final public function get_section_controls( $section_id ) { $section_index = $this->get_control_index( $section_id ); $section_controls = []; $registered_controls = $this->get_controls(); $controls_keys = array_keys( $registered_controls ); while ( true ) { $section_index++; if ( ! isset( $controls_keys[ $section_index ] ) ) { break; } $control_key = $controls_keys[ $section_index ]; if ( Controls_Manager::SECTION === $registered_controls[ $control_key ]['type'] ) { break; } $section_controls[ $control_key ] = $registered_controls[ $control_key ]; }; return $section_controls; } /** * Add new group control to stack. * * Register a set of related controls grouped together as a single unified * control. For example grouping together like typography controls into a * single, easy-to-use control. * * @since 1.4.0 * @access public * * @param string $group_name Group control name. * @param array $args Group control arguments. Default is an empty array. * @param array $options Optional. Group control options. Default is an * empty array. */ final public function add_group_control( $group_name, array $args = [], array $options = [] ) { $group = Plugin::$instance->controls_manager->get_control_groups( $group_name ); if ( ! $group ) { wp_die( sprintf( '%s::%s: Group "%s" not found.', get_called_class(), __FUNCTION__, $group_name ) ); } $group->add_controls( $this, $args, $options ); } /** * Get scheme controls. * * Retrieve all the controls that use schemes. * * @since 1.4.0 * @access public * * @return array Scheme controls. */ final public function get_scheme_controls() { $enabled_schemes = Schemes_Manager::get_enabled_schemes(); return array_filter( $this->get_controls(), function( $control ) use ( $enabled_schemes ) { return ( ! empty( $control['scheme'] ) && in_array( $control['scheme']['type'], $enabled_schemes ) ); } ); } /** * Get style controls. * * Retrieve style controls for all active controls or, when requested, from * a specific set of controls. * * @since 1.4.0 * @since 2.0.9 Added the `settings` parameter. * @access public * * @param array $controls Optional. Controls list. Default is null. * @param array $settings Optional. Controls settings. Default is null. * * @return array Style controls. */ final public function get_style_controls( array $controls = null, array $settings = null ) { $controls = $this->get_active_controls( $controls, $settings ); $style_controls = []; foreach ( $controls as $control_name => $control ) { $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] ); if ( ! $control_obj instanceof Base_Data_Control ) { continue; } $control = array_merge( $control_obj->get_settings(), $control ); if ( Controls_Manager::REPEATER === $control['type'] ) { $style_fields = []; foreach ( $this->get_settings( $control_name ) as $item ) { $style_fields[] = $this->get_style_controls( $control['fields'], $item ); } $control['style_fields'] = $style_fields; } if ( ! empty( $control['selectors'] ) || ! empty( $control['dynamic'] ) || ! empty( $control['style_fields'] ) ) { $style_controls[ $control_name ] = $control; } } return $style_controls; } /** * Get class controls. * * Retrieve the controls that use the same prefix class from all the active * controls * * @since 1.4.0 * @deprecated 2.1.0 * @access public * * @return array Class controls. */ final public function get_class_controls() { _deprecated_function( __METHOD__, '2.1.0' ); return array_filter( $this->get_active_controls(), function( $control ) { return ( isset( $control['prefix_class'] ) ); } ); } /** * Get tabs controls. * * Retrieve all the tabs assigned to the control. * * @since 1.4.0 * @access public * * @return array Tabs controls. */ final public function get_tabs_controls() { return $this->get_stack()['tabs']; } /** * Add new responsive control to stack. * * Register a set of controls to allow editing based on user screen size. * This method registers three screen sizes: Desktop, Tablet and Mobile. * * @since 1.4.0 * @access public * * @param string $id Responsive control ID. * @param array $args Responsive control arguments. * @param array $options Optional. Responsive control options. Default is * an empty array. */ final public function add_responsive_control( $id, array $args, $options = [] ) { $args['responsive'] = []; $devices = [ self::RESPONSIVE_DESKTOP, self::RESPONSIVE_TABLET, self::RESPONSIVE_MOBILE, ]; if ( isset( $args['devices'] ) ) { $devices = array_intersect( $devices, $args['devices'] ); $args['responsive']['devices'] = $devices; unset( $args['devices'] ); } if ( isset( $args['default'] ) ) { $args['desktop_default'] = $args['default']; unset( $args['default'] ); } foreach ( $devices as $device_name ) { $control_args = $args; if ( isset( $control_args['device_args'] ) ) { if ( ! empty( $control_args['device_args'][ $device_name ] ) ) { $control_args = array_merge( $control_args, $control_args['device_args'][ $device_name ] ); } unset( $control_args['device_args'] ); } if ( ! empty( $args['prefix_class'] ) ) { $device_to_replace = self::RESPONSIVE_DESKTOP === $device_name ? '' : '-' . $device_name; $control_args['prefix_class'] = sprintf( $args['prefix_class'], $device_to_replace ); } $control_args['responsive']['max'] = $device_name; if ( isset( $control_args['min_affected_device'] ) ) { if ( ! empty( $control_args['min_affected_device'][ $device_name ] ) ) { $control_args['responsive']['min'] = $control_args['min_affected_device'][ $device_name ]; } unset( $control_args['min_affected_device'] ); } if ( isset( $control_args[ $device_name . '_default' ] ) ) { $control_args['default'] = $control_args[ $device_name . '_default' ]; } unset( $control_args['desktop_default'] ); unset( $control_args['tablet_default'] ); unset( $control_args['mobile_default'] ); $id_suffix = self::RESPONSIVE_DESKTOP === $device_name ? '' : '_' . $device_name; if ( ! empty( $options['overwrite'] ) ) { $this->update_control( $id . $id_suffix, $control_args, [ 'recursive' => ! empty( $options['recursive'] ), ] ); } else { $this->add_control( $id . $id_suffix, $control_args, $options ); } } } /** * Update responsive control in stack. * * Change the value of an existing responsive control in the stack. When you * add new control you set the `$args` parameter, this method allows you to * update the arguments by passing new data. * * @since 1.4.0 * @access public * * @param string $id Responsive control ID. * @param array $args Responsive control arguments. * @param array $options Optional. Additional options. */ final public function update_responsive_control( $id, array $args, array $options = [] ) { $this->add_responsive_control( $id, $args, [ 'overwrite' => true, 'recursive' => ! empty( $options['recursive'] ), ] ); } /** * Remove responsive control from stack. * * Unregister an existing responsive control and remove it from the stack. * * @since 1.4.0 * @access public * * @param string $id Responsive control ID. */ final public function remove_responsive_control( $id ) { $devices = [ self::RESPONSIVE_DESKTOP, self::RESPONSIVE_TABLET, self::RESPONSIVE_MOBILE, ]; foreach ( $devices as $device_name ) { $id_suffix = self::RESPONSIVE_DESKTOP === $device_name ? '' : '_' . $device_name; $this->remove_control( $id . $id_suffix ); } } /** * Get class name. * * Retrieve the name of the current class. * * @since 1.4.0 * @access public * * @return string Class name. */ final public function get_class_name() { return get_called_class(); } /** * Get the config. * * Retrieve the config or, if non set, use the initial config. * * @since 1.4.0 * @access public * * @return array|null The config. */ final public function get_config() { if ( null === $this->config ) { // TODO: This is for backwards compatibility starting from 2.9.0 // This if statement should be removed when the method is hard-deprecated if ( method_exists( $this, '_get_initial_config' ) ) { $this->config = $this->_get_initial_config(); } else { $this->config = $this->get_initial_config(); } } return $this->config; } /** * Get frontend settings keys. * * Retrieve settings keys for all frontend controls. * * @since 1.6.0 * @access public * * @return array Settings keys for each control. */ final public function get_frontend_settings_keys() { $controls = []; foreach ( $this->get_controls() as $control ) { if ( ! empty( $control['frontend_available'] ) ) { $controls[] = $control['name']; } } return $controls; } /** * Get controls pointer index. * * Retrieve pointer index where the next control should be added. * * While using injection point, it will return the injection point index. * Otherwise index of the last control plus one. * * @since 1.9.2 * @access public * * @return int Controls pointer index. */ public function get_pointer_index() { if ( null !== $this->injection_point ) { return $this->injection_point['index']; } return count( $this->get_controls() ); } /** * Get the raw data. * * Retrieve all the items or, when requested, a specific item. * * @since 1.4.0 * @access public * * @param string $item Optional. The requested item. Default is null. * * @return mixed The raw data. */ public function get_data( $item = null ) { if ( ! $this->settings_sanitized && ( ! $item || 'settings' === $item ) ) { $this->data['settings'] = $this->sanitize_settings( $this->data['settings'] ); $this->settings_sanitized = true; } return self::get_items( $this->data, $item ); } /** * @since 2.0.14 * @access public */ public function get_parsed_dynamic_settings( $setting = null ) { if ( null === $this->parsed_dynamic_settings ) { $this->parsed_dynamic_settings = $this->parse_dynamic_settings( $this->get_settings() ); } return self::get_items( $this->parsed_dynamic_settings, $setting ); } /** * Get active settings. * * Retrieve the settings from all the active controls. * * @since 1.4.0 * @since 2.1.0 Added the `controls` and the `settings` parameters. * @access public * * @param array $controls Optional. An array of controls. Default is null. * @param array $settings Optional. Controls settings. Default is null. * * @return array Active settings. */ public function get_active_settings( $settings = null, $controls = null ) { $is_first_request = ! $settings && ! $this->active_settings; if ( ! $settings ) { if ( $this->active_settings ) { return $this->active_settings; } $settings = $this->get_controls_settings(); $controls = $this->get_controls(); } $active_settings = []; foreach ( $settings as $setting_key => $setting ) { if ( ! isset( $controls[ $setting_key ] ) ) { $active_settings[ $setting_key ] = $setting; continue; } $control = $controls[ $setting_key ]; if ( $this->is_control_visible( $control, $settings ) ) { if ( Controls_Manager::REPEATER === $control['type'] ) { foreach ( $setting as & $item ) { $item = $this->get_active_settings( $item, $control['fields'] ); } } $active_settings[ $setting_key ] = $setting; } else { $active_settings[ $setting_key ] = null; } } if ( $is_first_request ) { $this->active_settings = $active_settings; } return $active_settings; } /** * Get settings for display. * * Retrieve all the settings or, when requested, a specific setting for display. * * Unlike `get_settings()` method, this method retrieves only active settings * that passed all the conditions, rendered all the shortcodes and all the dynamic * tags. * * @since 2.0.0 * @access public * * @param string $setting_key Optional. The key of the requested setting. * Default is null. * * @return mixed The settings. */ public function get_settings_for_display( $setting_key = null ) { if ( ! $this->parsed_active_settings ) { $this->parsed_active_settings = $this->get_active_settings( $this->get_parsed_dynamic_settings(), $this->get_controls() ); } return self::get_items( $this->parsed_active_settings, $setting_key ); } /** * Parse dynamic settings. * * Retrieve the settings with rendered dynamic tags. * * @since 2.0.0 * @access public * * @param array $settings Optional. The requested setting. Default is null. * @param array $controls Optional. The controls array. Default is null. * @param array $all_settings Optional. All the settings. Default is null. * * @return array The settings with rendered dynamic tags. */ public function parse_dynamic_settings( $settings, $controls = null, $all_settings = null ) { if ( null === $all_settings ) { $all_settings = $this->get_settings(); } if ( null === $controls ) { $controls = $this->get_controls(); } foreach ( $controls as $control ) { $control_name = $control['name']; $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] ); if ( ! $control_obj instanceof Base_Data_Control ) { continue; } if ( 'repeater' === $control_obj->get_type() ) { foreach ( $settings[ $control_name ] as & $field ) { $field = $this->parse_dynamic_settings( $field, $control['fields'], $field ); } continue; } $dynamic_settings = $control_obj->get_settings( 'dynamic' ); if ( ! $dynamic_settings ) { $dynamic_settings = []; } if ( ! empty( $control['dynamic'] ) ) { $dynamic_settings = array_merge( $dynamic_settings, $control['dynamic'] ); } if ( empty( $dynamic_settings ) || ! isset( $all_settings[ Manager::DYNAMIC_SETTING_KEY ][ $control_name ] ) ) { continue; } if ( ! empty( $dynamic_settings['active'] ) && ! empty( $all_settings[ Manager::DYNAMIC_SETTING_KEY ][ $control_name ] ) ) { $parsed_value = $control_obj->parse_tags( $all_settings[ Manager::DYNAMIC_SETTING_KEY ][ $control_name ], $dynamic_settings ); $dynamic_property = ! empty( $dynamic_settings['property'] ) ? $dynamic_settings['property'] : null; if ( $dynamic_property ) { $settings[ $control_name ][ $dynamic_property ] = $parsed_value; } else { $settings[ $control_name ] = $parsed_value; } } } return $settings; } /** * Get frontend settings. * * Retrieve the settings for all frontend controls. * * @since 1.6.0 * @access public * * @return array Frontend settings. */ public function get_frontend_settings() { $frontend_settings = array_intersect_key( $this->get_active_settings(), array_flip( $this->get_frontend_settings_keys() ) ); foreach ( $frontend_settings as $key => $setting ) { if ( in_array( $setting, [ null, '' ], true ) ) { unset( $frontend_settings[ $key ] ); } } return $frontend_settings; } /** * Filter controls settings. * * Receives controls, settings and a callback function to filter the settings by * and returns filtered settings. * * @since 1.5.0 * @access public * * @param callable $callback The callback function. * @param array $settings Optional. Control settings. Default is an empty * array. * @param array $controls Optional. Controls list. Default is an empty * array. * * @return array Filtered settings. */ public function filter_controls_settings( callable $callback, array $settings = [], array $controls = [] ) { if ( ! $settings ) { $settings = $this->get_settings(); } if ( ! $controls ) { $controls = $this->get_controls(); } return array_reduce( array_keys( $settings ), function( $filtered_settings, $setting_key ) use ( $controls, $settings, $callback ) { if ( isset( $controls[ $setting_key ] ) ) { $result = $callback( $settings[ $setting_key ], $controls[ $setting_key ] ); if ( null !== $result ) { $filtered_settings[ $setting_key ] = $result; } } return $filtered_settings; }, [] ); } /** * Whether the control is visible or not. * * Used to determine whether the control is visible or not. * * @since 1.4.0 * @access public * * @param array $control The control. * @param array $values Optional. Condition values. Default is null. * * @return bool Whether the control is visible. */ public function is_control_visible( $control, $values = null ) { if ( null === $values ) { $values = $this->get_settings(); } if ( ! empty( $control['conditions'] ) && ! Conditions::check( $control['conditions'], $values ) ) { return false; } if ( empty( $control['condition'] ) ) { return true; } foreach ( $control['condition'] as $condition_key => $condition_value ) { preg_match( '/([a-z_\-0-9]+)(?:\[([a-z_]+)])?(!?)$/i', $condition_key, $condition_key_parts ); $pure_condition_key = $condition_key_parts[1]; $condition_sub_key = $condition_key_parts[2]; $is_negative_condition = ! ! $condition_key_parts[3]; if ( ! isset( $values[ $pure_condition_key ] ) || null === $values[ $pure_condition_key ] ) { return false; } $instance_value = $values[ $pure_condition_key ]; if ( $condition_sub_key && is_array( $instance_value ) ) { if ( ! isset( $instance_value[ $condition_sub_key ] ) ) { return false; } $instance_value = $instance_value[ $condition_sub_key ]; } /** * If the $condition_value is a non empty array - check if the $condition_value contains the $instance_value, * If the $instance_value is a non empty array - check if the $instance_value contains the $condition_value * otherwise check if they are equal. ( and give the ability to check if the value is an empty array ) */ if ( is_array( $condition_value ) && ! empty( $condition_value ) ) { $is_contains = in_array( $instance_value, $condition_value, true ); } elseif ( is_array( $instance_value ) && ! empty( $instance_value ) ) { $is_contains = in_array( $condition_value, $instance_value, true ); } else { $is_contains = $instance_value === $condition_value; } if ( $is_negative_condition && $is_contains || ! $is_negative_condition && ! $is_contains ) { return false; } } return true; } /** * Start controls section. * * Used to add a new section of controls. When you use this method, all the * registered controls from this point will be assigned to this section, * until you close the section using `end_controls_section()` method. * * This method should be used inside `_register_controls()`. * * @since 1.4.0 * @access public * * @param string $section_id Section ID. * @param array $args Section arguments Optional. */ public function start_controls_section( $section_id, array $args = [] ) { $section_name = $this->get_name(); /** * Before section start. * * Fires before Elementor section starts in the editor panel. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param string $section_id Section ID. * @param array $args Section arguments. */ do_action( 'elementor/element/before_section_start', $this, $section_id, $args ); /** * Before section start. * * Fires before Elementor section starts in the editor panel. * * The dynamic portions of the hook name, `$section_name` and `$section_id`, refers to the section name and section ID, respectively. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param array $args Section arguments. */ do_action( "elementor/element/{$section_name}/{$section_id}/before_section_start", $this, $args ); $args['type'] = Controls_Manager::SECTION; $this->add_control( $section_id, $args ); if ( null !== $this->current_section ) { wp_die( sprintf( 'Elementor: You can\'t start a section before the end of the previous section "%s".', $this->current_section['section'] ) ); // XSS ok. } $this->current_section = $this->get_section_args( $section_id ); if ( $this->injection_point ) { $this->injection_point['section'] = $this->current_section; } /** * After section start. * * Fires after Elementor section starts in the editor panel. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param string $section_id Section ID. * @param array $args Section arguments. */ do_action( 'elementor/element/after_section_start', $this, $section_id, $args ); /** * After section start. * * Fires after Elementor section starts in the editor panel. * * The dynamic portions of the hook name, `$section_name` and `$section_id`, refers to the section name and section ID, respectively. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param array $args Section arguments. */ do_action( "elementor/element/{$section_name}/{$section_id}/after_section_start", $this, $args ); } /** * End controls section. * * Used to close an existing open controls section. When you use this method * it stops adding new controls to this section. * * This method should be used inside `_register_controls()`. * * @since 1.4.0 * @access public */ public function end_controls_section() { $stack_name = $this->get_name(); // Save the current section for the action. $current_section = $this->current_section; $section_id = $current_section['section']; $args = [ 'tab' => $current_section['tab'], ]; /** * Before section end. * * Fires before Elementor section ends in the editor panel. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param string $section_id Section ID. * @param array $args Section arguments. */ do_action( 'elementor/element/before_section_end', $this, $section_id, $args ); /** * Before section end. * * Fires before Elementor section ends in the editor panel. * * The dynamic portions of the hook name, `$stack_name` and `$section_id`, refers to the stack name and section ID, respectively. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param array $args Section arguments. */ do_action( "elementor/element/{$stack_name}/{$section_id}/before_section_end", $this, $args ); $this->current_section = null; /** * After section end. * * Fires after Elementor section ends in the editor panel. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param string $section_id Section ID. * @param array $args Section arguments. */ do_action( 'elementor/element/after_section_end', $this, $section_id, $args ); /** * After section end. * * Fires after Elementor section ends in the editor panel. * * The dynamic portions of the hook name, `$stack_name` and `$section_id`, refers to the section name and section ID, respectively. * * @since 1.4.0 * * @param Controls_Stack $this The control. * @param array $args Section arguments. */ do_action( "elementor/element/{$stack_name}/{$section_id}/after_section_end", $this, $args ); } /** * Start controls tabs. * * Used to add a new set of tabs inside a section. You should use this * method before adding new individual tabs using `start_controls_tab()`. * Each tab added after this point will be assigned to this group of tabs, * until you close it using `end_controls_tabs()` method. * * This method should be used inside `_register_controls()`. * * @since 1.4.0 * @access public * * @param string $tabs_id Tabs ID. * @param array $args Tabs arguments. */ public function start_controls_tabs( $tabs_id, array $args = [] ) { if ( null !== $this->current_tab ) { wp_die( sprintf( 'Elementor: You can\'t start tabs before the end of the previous tabs "%s".', $this->current_tab['tabs_wrapper'] ) ); // XSS ok. } $args['type'] = Controls_Manager::TABS; $this->add_control( $tabs_id, $args ); $this->current_tab = [ 'tabs_wrapper' => $tabs_id, ]; foreach ( [ 'condition', 'conditions' ] as $key ) { if ( ! empty( $args[ $key ] ) ) { $this->current_tab[ $key ] = $args[ $key ]; } } if ( $this->injection_point ) { $this->injection_point['tab'] = $this->current_tab; } } /** * End controls tabs. * * Used to close an existing open controls tabs. When you use this method it * stops adding new controls to this tabs. * * This method should be used inside `_register_controls()`. * * @since 1.4.0 * @access public */ public function end_controls_tabs() { $this->current_tab = null; } /** * Start controls tab. * * Used to add a new tab inside a group of tabs. Use this method before * adding new individual tabs using `start_controls_tab()`. * Each tab added after this point will be assigned to this group of tabs, * until you close it using `end_controls_tab()` method. * * This method should be used inside `_register_controls()`. * * @since 1.4.0 * @access public * * @param string $tab_id Tab ID. * @param array $args Tab arguments. */ public function start_controls_tab( $tab_id, $args ) { if ( ! empty( $this->current_tab['inner_tab'] ) ) { wp_die( sprintf( 'Elementor: You can\'t start a tab before the end of the previous tab "%s".', $this->current_tab['inner_tab'] ) ); // XSS ok. } $args['type'] = Controls_Manager::TAB; $args['tabs_wrapper'] = $this->current_tab['tabs_wrapper']; $this->add_control( $tab_id, $args ); $this->current_tab['inner_tab'] = $tab_id; if ( $this->injection_point ) { $this->injection_point['tab']['inner_tab'] = $this->current_tab['inner_tab']; } } /** * End controls tab. * * Used to close an existing open controls tab. When you use this method it * stops adding new controls to this tab. * * This method should be used inside `_register_controls()`. * * @since 1.4.0 * @access public */ public function end_controls_tab() { unset( $this->current_tab['inner_tab'] ); } /** * Start popover. * * Used to add a new set of controls in a popover. When you use this method, * all the registered controls from this point will be assigned to this * popover, until you close the popover using `end_popover()` method. * * This method should be used inside `_register_controls()`. * * @since 1.9.0 * @access public */ final public function start_popover() { $this->current_popover = [ 'initialized' => false, ]; } /** * End popover. * * Used to close an existing open popover. When you use this method it stops * adding new controls to this popover. * * This method should be used inside `_register_controls()`. * * @since 1.9.0 * @access public */ final public function end_popover() { $this->current_popover = null; $last_control_key = $this->get_control_key( $this->get_pointer_index() - 1 ); $args = [ 'popover' => [ 'end' => true, ], ]; $options = [ 'recursive' => true, ]; $this->update_control( $last_control_key, $args, $options ); } /** * Print element template. * * Used to generate the element template on the editor. * * @since 2.0.0 * @access public */ public function print_template() { ob_start(); // TODO: This is for backwards compatibility starting from 2.9.0 // This `if` statement should be removed when the method is removed if ( method_exists( $this, '_content_template' ) ) { $this->_content_template(); } else { $this->content_template(); } $template_content = ob_get_clean(); $element_type = $this->get_type(); /** * Template content. * * Filters the controls stack template content before it's printed in the editor. * * The dynamic portion of the hook name, `$element_type`, refers to the element type. * * @since 1.0.0 * * @param string $content_template The controls stack template in the editor. * @param Controls_Stack $this The controls stack. */ $template_content = apply_filters( "elementor/{$element_type}/print_template", $template_content, $this ); if ( empty( $template_content ) ) { return; } ?> <script type="text/html" id="tmpl-elementor-<?php echo esc_attr( $this->get_name() ); ?>-content"> <?php $this->print_template_content( $template_content ); ?> </script> <?php } /** * Start injection. * * Used to inject controls and sections to a specific position in the stack. * * When you use this method, all the registered controls and sections will * be injected to a specific position in the stack, until you stop the * injection using `end_injection()` method. * * @since 1.7.1 * @access public * * @param array $position { * The position where to start the injection. * * @type string $type Injection type, either `control` or `section`. * Default is `control`. * @type string $at Where to inject. If `$type` is `control` accepts * `before` and `after`. If `$type` is `section` * accepts `start` and `end`. Default values based on * the `type`. * @type string $of Control/Section ID. * } */ final public function start_injection( array $position ) { if ( $this->injection_point ) { wp_die( 'A controls injection is already opened. Please close current injection before starting a new one (use `end_injection`).' ); } $this->injection_point = $this->get_position_info( $position ); } /** * End injection. * * Used to close an existing opened injection point. * * When you use this method it stops adding new controls and sections to * this point and continue to add controls to the regular position in the * stack. * * @since 1.7.1 * @access public */ final public function end_injection() { $this->injection_point = null; } /** * Get injection point. * * Retrieve the injection point in the stack where new controls and sections * will be inserted. * * @since 1.9.2 * @access public * * @return array|null An array when an injection point is defined, null * otherwise. */ final public function get_injection_point() { return $this->injection_point; } /** * Register controls. * * Used to add new controls to any element type. For example, external * developers use this method to register controls in a widget. * * Should be inherited and register new controls using `add_control()`, * `add_responsive_control()` and `add_group_control()`, inside control * wrappers like `start_controls_section()`, `start_controls_tabs()` and * `start_controls_tab()`. * * @since 1.4.0 * @access protected */ protected function _register_controls() {} /** * Get default data. * * Retrieve the default data. Used to reset the data on initialization. * * @since 1.4.0 * @access protected * * @return array Default data. */ protected function get_default_data() { return [ 'id' => 0, 'settings' => [], ]; } /** * @since 2.3.0 * @access protected */ protected function get_init_settings() { $settings = $this->get_data( 'settings' ); foreach ( $this->get_controls() as $control ) { $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] ); if ( ! $control_obj instanceof Base_Data_Control ) { continue; } $control = array_merge_recursive( $control_obj->get_settings(), $control ); $settings[ $control['name'] ] = $control_obj->get_value( $control, $settings ); } return $settings; } /** * Get initial config. * * Retrieve the current element initial configuration - controls list and * the tabs assigned to the control. * * @since 2.9.0 * @access protected * * @return array The initial config. */ protected function get_initial_config() { return [ 'controls' => $this->get_controls(), ]; } /** * Get initial config. * * Retrieve the current element initial configuration - controls list and * the tabs assigned to the control. * * @since 1.4.0 * @deprecated 2.9.0 use `get_initial_config()` instead * @access protected * * @return array The initial config. */ protected function _get_initial_config() { // _deprecated_function( __METHOD__, '2.9.0', 'get_initial_config' ); return $this->get_initial_config(); } /** * Get section arguments. * * Retrieve the section arguments based on section ID. * * @since 1.4.0 * @access protected * * @param string $section_id Section ID. * * @return array Section arguments. */ protected function get_section_args( $section_id ) { $section_control = $this->get_controls( $section_id ); $section_args_keys = [ 'tab', 'condition' ]; $args = array_intersect_key( $section_control, array_flip( $section_args_keys ) ); $args['section'] = $section_id; return $args; } /** * Render element. * * Generates the final HTML on the frontend. * * @since 2.0.0 * @access protected */ protected function render() {} /** * Print content template. * * Used to generate the content template on the editor, using a * Backbone JavaScript template. * * @access protected * @since 2.0.0 * * @param string $template_content Template content. */ protected function print_template_content( $template_content ) { echo $template_content; } /** * Render element output in the editor. * * Used to generate the live preview, using a Backbone JavaScript template. * * @since 2.9.0 * @access protected */ protected function content_template() {} /** * Render element output in the editor. * * Used to generate the live preview, using a Backbone JavaScript template. * * @since 2.0.0 * @deprecated 2.9.0 use `content_template()` instead * @access protected */ protected function _content_template() { // _deprecated_function( __METHOD__, '2.9.0', 'content_template' ); $this->content_template(); } /** * Initialize controls. * * Register the all controls added by `_register_controls()`. * * @since 2.0.0 * @access protected */ protected function init_controls() { Plugin::$instance->controls_manager->open_stack( $this ); // TODO: This is for backwards compatibility starting from 2.9.0 // This `if` statement should be removed when the method is removed if ( method_exists( $this, '_register_controls' ) ) { $this->_register_controls(); } else { $this->register_controls(); } } /** * Initialize the class. * * Set the raw data, the ID and the parsed settings. * * @since 2.9.0 * @access protected * * @param array $data Initial data. */ protected function init( $data ) { $this->data = array_merge( $this->get_default_data(), $data ); $this->id = $data['id']; } /** * Initialize the class. * * Set the raw data, the ID and the parsed settings. * * @since 1.4.0 * @deprecated 2.9.0 use `init()` instead * @access protected * * @param array $data Initial data. */ protected function _init( $data ) { // _deprecated_function( __METHOD__, '2.9.0', 'init' ); $this->init( $data ); } /** * Sanitize initial data. * * Performs settings cleaning and sanitization. * * @since 2.1.5 * @access private * * @param array $settings Settings to sanitize. * @param array $controls Optional. An array of controls. Default is an * empty array. * * @return array Sanitized settings. */ private function sanitize_settings( array $settings, array $controls = [] ) { if ( ! $controls ) { $controls = $this->get_controls(); } foreach ( $controls as $control ) { if ( 'repeater' === $control['type'] ) { if ( empty( $settings[ $control['name'] ] ) ) { continue; } foreach ( $settings[ $control['name'] ] as $index => $repeater_row_data ) { $sanitized_row_data = $this->sanitize_settings( $repeater_row_data, $control['fields'] ); $settings[ $control['name'] ][ $index ] = $sanitized_row_data; } continue; } $is_dynamic = isset( $settings[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ); if ( ! $is_dynamic ) { continue; } $value_to_check = $settings[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ]; $tag_text_data = Plugin::$instance->dynamic_tags->tag_text_to_tag_data( $value_to_check ); if ( ! Plugin::$instance->dynamic_tags->get_tag_info( $tag_text_data['name'] ) ) { unset( $settings[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ); } } return $settings; } /** * Controls stack constructor. * * Initializing the control stack class using `$data`. The `$data` is required * for a normal instance. It is optional only for internal `type instance`. * * @since 1.4.0 * @access public * * @param array $data Optional. Control stack data. Default is an empty array. */ public function __construct( array $data = [] ) { if ( $data ) { // TODO: This is for backwards compatibility starting from 2.9.0 // This if statement should be removed when the method is hard-deprecated if ( method_exists( $this, '_init' ) ) { $this->_init( $data ); } else { $this->init( $data ); } } } } element-base.php 0000666 00000056740 15214232264 0007637 0 ustar 00 <?php namespace Elementor; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor element base. * * An abstract class to register new Elementor elements. It extended the * `Controls_Stack` class to inherit its properties. * * This abstract class must be extended in order to register new elements. * * @since 1.0.0 * @abstract */ abstract class Element_Base extends Controls_Stack { /** * Child elements. * * Holds all the child elements of the element. * * @access private * * @var Element_Base[] */ private $children; /** * Element render attributes. * * Holds all the render attributes of the element. Used to store data like * the HTML class name and the class value, or HTML element ID name and value. * * @access private * * @var array */ private $render_attributes = []; /** * Element default arguments. * * Holds all the default arguments of the element. Used to store additional * data. For example WordPress widgets use this to store widget names. * * @access private * * @var array */ private $default_args = []; /** * Is type instance. * * Whether the element is an instance of that type or not. * * @access private * * @var bool */ private $is_type_instance = true; /** * Depended scripts. * * Holds all the element depended scripts to enqueue. * * @since 1.9.0 * @access private * * @var array */ private $depended_scripts = []; /** * Depended styles. * * Holds all the element depended styles to enqueue. * * @since 1.9.0 * @access private * * @var array */ private $depended_styles = []; /** * Add script depends. * * Register new script to enqueue by the handler. * * @since 1.9.0 * @access public * * @param string $handler Depend script handler. */ public function add_script_depends( $handler ) { $this->depended_scripts[] = $handler; } /** * Add style depends. * * Register new style to enqueue by the handler. * * @since 1.9.0 * @access public * * @param string $handler Depend style handler. */ public function add_style_depends( $handler ) { $this->depended_styles[] = $handler; } /** * Get script dependencies. * * Retrieve the list of script dependencies the element requires. * * @since 1.3.0 * @access public * * @return array Element scripts dependencies. */ public function get_script_depends() { return $this->depended_scripts; } /** * Enqueue scripts. * * Registers all the scripts defined as element dependencies and enqueues * them. Use `get_script_depends()` method to add custom script dependencies. * * @since 1.3.0 * @access public */ final public function enqueue_scripts() { $deprecated_scripts = [ 'jquery-slick' => [ 'version' => '2.7.0', 'replacement' => 'Swiper', ], ]; foreach ( $this->get_script_depends() as $script ) { if ( isset( $deprecated_scripts[ $script ] ) ) { Utils::handle_deprecation( $script, $deprecated_scripts[ $script ]['version'], $deprecated_scripts[ $script ]['replacement'] ); } wp_enqueue_script( $script ); } } /** * Get style dependencies. * * Retrieve the list of style dependencies the element requires. * * @since 1.9.0 * @access public * * @return array Element styles dependencies. */ public function get_style_depends() { return $this->depended_styles; } /** * Enqueue styles. * * Registers all the styles defined as element dependencies and enqueues * them. Use `get_style_depends()` method to add custom style dependencies. * * @since 1.9.0 * @access public */ final public function enqueue_styles() { foreach ( $this->get_style_depends() as $style ) { wp_enqueue_style( $style ); } } /** * @since 1.0.0 * @deprecated 2.6.0 * @access public * @static */ final public static function add_edit_tool() {} /** * @since 2.2.0 * @deprecated 2.6.0 * @access public * @static */ final public static function is_edit_buttons_enabled() { return get_option( 'elementor_edit_buttons' ); } /** * Get default child type. * * Retrieve the default child type based on element data. * * Note that not all elements support children. * * @since 1.0.0 * @access protected * @abstract * * @param array $element_data Element data. * * @return Element_Base */ abstract protected function _get_default_child_type( array $element_data ); /** * Before element rendering. * * Used to add stuff before the element. * * @since 1.0.0 * @access public */ public function before_render() {} /** * After element rendering. * * Used to add stuff after the element. * * @since 1.0.0 * @access public */ public function after_render() {} /** * Get element title. * * Retrieve the element title. * * @since 1.0.0 * @access public * * @return string Element title. */ public function get_title() { return ''; } /** * Get element icon. * * Retrieve the element icon. * * @since 1.0.0 * @access public * * @return string Element icon. */ public function get_icon() { return 'eicon-columns'; } public function get_help_url() { return 'https://go.elementor.com/widget-' . $this->get_name(); } public function get_custom_help_url() { return ''; } /** * Whether the reload preview is required. * * Used to determine whether the reload preview is required or not. * * @since 1.0.0 * @access public * * @return bool Whether the reload preview is required. */ public function is_reload_preview_required() { return false; } /** * @since 2.3.1 * @access protected */ protected function should_print_empty() { return true; } /** * Get child elements. * * Retrieve all the child elements of this element. * * @since 1.0.0 * @access public * * @return Element_Base[] Child elements. */ public function get_children() { if ( null === $this->children ) { $this->init_children(); } return $this->children; } /** * Get default arguments. * * Retrieve the element default arguments. Used to return all the default * arguments or a specific default argument, if one is set. * * @since 1.0.0 * @access public * * @param array $item Optional. Default is null. * * @return array Default argument(s). */ public function get_default_args( $item = null ) { return self::get_items( $this->default_args, $item ); } /** * Add new child element. * * Register new child element to allow hierarchy. * * @since 1.0.0 * @access public * @param array $child_data Child element data. * @param array $child_args Child element arguments. * * @return Element_Base|false Child element instance, or false if failed. */ public function add_child( array $child_data, array $child_args = [] ) { if ( null === $this->children ) { $this->init_children(); } $child_type = $this->get_child_type( $child_data ); if ( ! $child_type ) { return false; } $child = Plugin::$instance->elements_manager->create_element_instance( $child_data, $child_args, $child_type ); if ( $child ) { $this->children[] = $child; } return $child; } /** * Add render attribute. * * Used to add attributes to a specific HTML element. * * The HTML tag is represented by the element parameter, then you need to * define the attribute key and the attribute key. The final result will be: * `<element attribute_key="attribute_value">`. * * Example usage: * * `$this->add_render_attribute( 'wrapper', 'class', 'custom-widget-wrapper-class' );` * `$this->add_render_attribute( 'widget', 'id', 'custom-widget-id' );` * `$this->add_render_attribute( 'button', [ 'class' => 'custom-button-class', 'id' => 'custom-button-id' ] );` * * @since 1.0.0 * @access public * * @param array|string $element The HTML element. * @param array|string $key Optional. Attribute key. Default is null. * @param array|string $value Optional. Attribute value. Default is null. * @param bool $overwrite Optional. Whether to overwrite existing * attribute. Default is false, not to overwrite. * * @return Element_Base Current instance of the element. */ public function add_render_attribute( $element, $key = null, $value = null, $overwrite = false ) { if ( is_array( $element ) ) { foreach ( $element as $element_key => $attributes ) { $this->add_render_attribute( $element_key, $attributes, null, $overwrite ); } return $this; } if ( is_array( $key ) ) { foreach ( $key as $attribute_key => $attributes ) { $this->add_render_attribute( $element, $attribute_key, $attributes, $overwrite ); } return $this; } if ( empty( $this->render_attributes[ $element ][ $key ] ) ) { $this->render_attributes[ $element ][ $key ] = []; } settype( $value, 'array' ); if ( $overwrite ) { $this->render_attributes[ $element ][ $key ] = $value; } else { $this->render_attributes[ $element ][ $key ] = array_merge( $this->render_attributes[ $element ][ $key ], $value ); } return $this; } /** * Add link render attributes. * * Used to add link tag attributes to a specific HTML element. * * The HTML link tag is represented by the element parameter. The `url_control` parameter * needs to be an array of link settings in the same format they are set by Elementor's URL control. * * Example usage: * * `$this->add_link_attributes( 'button', $settings['link'] );` * * @since 2.8.0 * @access public * * @param array|string $element The HTML element. * @param array $url_control Array of link settings. * @param bool $overwrite Optional. Whether to overwrite existing * attribute. Default is false, not to overwrite. * * @return Element_Base Current instance of the element. */ public function add_link_attributes( $element, array $url_control, $overwrite = false ) { $attributes = []; if ( ! empty( $url_control['url'] ) ) { $attributes['href'] = $url_control['url']; } if ( ! empty( $url_control['is_external'] ) ) { $attributes['target'] = '_blank'; } if ( ! empty( $url_control['nofollow'] ) ) { $attributes['rel'] = 'nofollow'; } if ( ! empty( $url_control['custom_attributes'] ) ) { // Custom URL attributes should come as a string of comma-delimited key|value pairs $custom_attributes = explode( ',', $url_control['custom_attributes'] ); $blacklist = [ 'onclick', 'onfocus', 'onblur', 'onchange', 'onresize', 'onmouseover', 'onmouseout', 'onkeydown', 'onkeyup' ]; foreach ( $custom_attributes as $attribute ) { // Trim in case users inserted unwanted spaces $attr_key_value = explode( '|', $attribute ); $attr_key = $attr_key_value[0]; // Cover cases where key/value have spaces both before and/or after the actual value preg_match( '/[^=]+/', $attr_key, $attr_key_matches ); $attr_key = trim( $attr_key_matches[0] ); // Implement attribute blacklist if ( in_array( strtolower( $attr_key ), $blacklist, true ) ) { continue; } if ( isset( $attr_key_value[1] ) ) { $attr_value = trim( $attr_key_value[1] ); } else { $attr_value = ''; } $attributes[ $attr_key ] = $attr_value; } } if ( $attributes ) { $this->add_render_attribute( $element, $attributes, $overwrite ); } return $this; } /** * Get Render Attributes * * Used to retrieve render attribute. * * The returned array is either all elements and their attributes if no `$element` is specified, an array of all * attributes of a specific element or a specific attribute properties if `$key` is specified. * * Returns null if one of the requested parameters isn't set. * * @since 2.2.6 * @access public * @param string $element * @param string $key * * @return array */ public function get_render_attributes( $element = '', $key = '' ) { $attributes = $this->render_attributes; if ( $element ) { if ( ! isset( $attributes[ $element ] ) ) { return null; } $attributes = $attributes[ $element ]; if ( $key ) { if ( ! isset( $attributes[ $key ] ) ) { return null; } $attributes = $attributes[ $key ]; } } return $attributes; } /** * Set render attribute. * * Used to set the value of the HTML element render attribute or to update * an existing render attribute. * * @since 1.0.0 * @access public * * @param array|string $element The HTML element. * @param array|string $key Optional. Attribute key. Default is null. * @param array|string $value Optional. Attribute value. Default is null. * * @return Element_Base Current instance of the element. */ public function set_render_attribute( $element, $key = null, $value = null ) { return $this->add_render_attribute( $element, $key, $value, true ); } /** * Remove render attribute. * * Used to remove an element (with its keys and their values), key (with its values), * or value/s from an HTML element's render attribute. * * @since 2.7.0 * @access public * * @param string $element The HTML element. * @param string $key Optional. Attribute key. Default is null. * @param array|string $values Optional. Attribute value/s. Default is null. */ public function remove_render_attribute( $element, $key = null, $values = null ) { if ( $key && ! isset( $this->render_attributes[ $element ][ $key ] ) ) { return; } if ( $values ) { $values = (array) $values; $this->render_attributes[ $element ][ $key ] = array_diff( $this->render_attributes[ $element ][ $key ], $values ); return; } if ( $key ) { unset( $this->render_attributes[ $element ][ $key ] ); return; } if ( isset( $this->render_attributes[ $element ] ) ) { unset( $this->render_attributes[ $element ] ); } } /** * Get render attribute string. * * Used to retrieve the value of the render attribute. * * @since 1.0.0 * @access public * * @param string $element The element. * * @return string Render attribute string, or an empty string if the attribute * is empty or not exist. */ public function get_render_attribute_string( $element ) { if ( empty( $this->render_attributes[ $element ] ) ) { return ''; } return Utils::render_html_attributes( $this->render_attributes[ $element ] ); } /** * Print render attribute string. * * Used to output the rendered attribute. * * @since 2.0.0 * @access public * * @param array|string $element The element. */ public function print_render_attribute_string( $element ) { echo $this->get_render_attribute_string( $element ); // XSS ok. } /** * Print element. * * Used to generate the element final HTML on the frontend and the editor. * * @since 1.0.0 * @access public */ public function print_element() { $element_type = $this->get_type(); /** * Before frontend element render. * * Fires before Elementor element is rendered in the frontend. * * @since 2.2.0 * * @param Element_Base $this The element. */ do_action( 'elementor/frontend/before_render', $this ); /** * Before frontend element render. * * Fires before Elementor element is rendered in the frontend. * * The dynamic portion of the hook name, `$element_type`, refers to the element type. * * @since 1.0.0 * * @param Element_Base $this The element. */ do_action( "elementor/frontend/{$element_type}/before_render", $this ); ob_start(); $this->_print_content(); $content = ob_get_clean(); $should_render = ( ! empty( $content ) || $this->should_print_empty() ); /** * Should the element be rendered for frontend * * Filters if the element should be rendered on frontend. * * @since 2.3.3 * * @param bool true The element. * @param Element_Base $this The element. */ $should_render = apply_filters( "elementor/frontend/{$element_type}/should_render", $should_render, $this ); if ( $should_render ) { $this->_add_render_attributes(); $this->before_render(); echo $content; $this->after_render(); $this->enqueue_scripts(); $this->enqueue_styles(); } /** * After frontend element render. * * Fires after Elementor element is rendered in the frontend. * * The dynamic portion of the hook name, `$element_type`, refers to the element type. * * @since 1.0.0 * * @param Element_Base $this The element. */ do_action( "elementor/frontend/{$element_type}/after_render", $this ); /** * After frontend element render. * * Fires after Elementor element is rendered in the frontend. * * @since 2.3.0 * * @param Element_Base $this The element. */ do_action( 'elementor/frontend/after_render', $this ); } /** * Get the element raw data. * * Retrieve the raw element data, including the id, type, settings, child * elements and whether it is an inner element. * * The data with the HTML used always to display the data, but the Elementor * editor uses the raw data without the HTML in order not to render the data * again. * * @since 1.0.0 * @access public * * @param bool $with_html_content Optional. Whether to return the data with * HTML content or without. Used for caching. * Default is false, without HTML. * * @return array Element raw data. */ public function get_raw_data( $with_html_content = false ) { $data = $this->get_data(); $elements = []; foreach ( $this->get_children() as $child ) { $elements[] = $child->get_raw_data( $with_html_content ); } return [ 'id' => $this->get_id(), 'elType' => $data['elType'], 'settings' => $data['settings'], 'elements' => $elements, 'isInner' => $data['isInner'], ]; } /** * Get unique selector. * * Retrieve the unique selector of the element. Used to set a unique HTML * class for each HTML element. This way Elementor can set custom styles for * each element. * * @since 1.0.0 * @access public * * @return string Unique selector. */ public function get_unique_selector() { return '.elementor-element-' . $this->get_id(); } /** * Is type instance. * * Used to determine whether the element is an instance of that type or not. * * @since 1.0.0 * @access public * * @return bool Whether the element is an instance of that type. */ public function is_type_instance() { return $this->is_type_instance; } /** * Add render attributes. * * Used to add attributes to the current element wrapper HTML tag. * * @since 1.3.0 * @access protected */ protected function _add_render_attributes() { $id = $this->get_id(); $settings = $this->get_settings_for_display(); $frontend_settings = $this->get_frontend_settings(); $controls = $this->get_controls(); $this->add_render_attribute( '_wrapper', [ 'class' => [ 'elementor-element', 'elementor-element-' . $id, ], 'data-id' => $id, 'data-element_type' => $this->get_type(), ] ); $class_settings = []; foreach ( $settings as $setting_key => $setting ) { if ( isset( $controls[ $setting_key ]['prefix_class'] ) ) { $class_settings[ $setting_key ] = $setting; } } foreach ( $class_settings as $setting_key => $setting ) { if ( empty( $setting ) && '0' !== $setting ) { continue; } $this->add_render_attribute( '_wrapper', 'class', $controls[ $setting_key ]['prefix_class'] . $setting ); } if ( ! empty( $settings['animation'] ) || ! empty( $settings['_animation'] ) ) { // Hide the element until the animation begins $this->add_render_attribute( '_wrapper', 'class', 'elementor-invisible' ); } if ( ! empty( $settings['_element_id'] ) ) { $this->add_render_attribute( '_wrapper', 'id', trim( $settings['_element_id'] ) ); } if ( $frontend_settings ) { $this->add_render_attribute( '_wrapper', 'data-settings', wp_json_encode( $frontend_settings ) ); } /** * After element attribute rendered. * * Fires after the attributes of the element HTML tag are rendered. * * @since 2.3.0 * * @param Element_Base $this The element. */ do_action( 'elementor/element/after_add_attributes', $this ); } /** * Get default data. * * Retrieve the default element data. Used to reset the data on initialization. * * @since 1.0.0 * @access protected * * @return array Default data. */ protected function get_default_data() { $data = parent::get_default_data(); return array_merge( $data, [ 'elements' => [], 'isInner' => false, ] ); } /** * Print element content. * * Output the element final HTML on the frontend. * * @since 1.0.0 * @access protected */ protected function _print_content() { foreach ( $this->get_children() as $child ) { $child->print_element(); } } /** * Get initial config. * * Retrieve the current element initial configuration. * * Adds more configuration on top of the controls list and the tabs assigned * to the control. This method also adds element name, type, icon and more. * * @since 2.9.0 * @access protected * * @return array The initial config. */ protected function get_initial_config() { $config = [ 'name' => $this->get_name(), 'elType' => $this->get_type(), 'title' => $this->get_title(), 'icon' => $this->get_icon(), 'reload_preview' => $this->is_reload_preview_required(), ]; if ( preg_match( '/^' . __NAMESPACE__ . '(Pro)?\\\\/', get_called_class() ) ) { $config['help_url'] = $this->get_help_url(); } else { $config['help_url'] = $this->get_custom_help_url(); } if ( ! $this->is_editable() ) { $config['editable'] = false; } return $config; } /** * Get child type. * * Retrieve the element child type based on element data. * * @since 2.0.0 * @access private * * @param array $element_data Element ID. * * @return Element_Base|false Child type or false if type not found. */ private function get_child_type( $element_data ) { $child_type = $this->_get_default_child_type( $element_data ); // If it's not a valid widget ( like a deactivated plugin ) if ( ! $child_type ) { return false; } /** * Element child type. * * Filters the child type of the element. * * @since 1.0.0 * * @param Element_Base $child_type The child element. * @param array $element_data The original element ID. * @param Element_Base $this The original element. */ $child_type = apply_filters( 'elementor/element/get_child_type', $child_type, $element_data, $this ); return $child_type; } /** * Initialize children. * * Initializing the element child elements. * * @since 2.0.0 * @access private */ private function init_children() { $this->children = []; $children_data = $this->get_data( 'elements' ); if ( ! $children_data ) { return; } foreach ( $children_data as $child_data ) { if ( ! $child_data ) { continue; } $this->add_child( $child_data ); } } /** * Element base constructor. * * Initializing the element base class using `$data` and `$args`. * * The `$data` parameter is required for a normal instance because of the * way Elementor renders data when initializing elements. * * @since 1.0.0 * @access public * * @param array $data Optional. Element data. Default is an empty array. * @param array|null $args Optional. Element default arguments. Default is null. **/ public function __construct( array $data = [], array $args = null ) { if ( $data ) { $this->is_type_instance = false; } elseif ( $args ) { $this->default_args = $args; } parent::__construct( $data ); } } widget-base.php 0000666 00000055054 15214232264 0007466 0 ustar 00 <?php namespace Elementor; use Elementor\Core\Settings\Manager as SettingsManager; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor widget base. * * An abstract class to register new Elementor widgets. It extended the * `Element_Base` class to inherit its properties. * * This abstract class must be extended in order to register new widgets. * * @since 1.0.0 * @abstract */ abstract class Widget_Base extends Element_Base { /** * Whether the widget has content. * * Used in cases where the widget has no content. When widgets uses only * skins to display dynamic content generated on the server. For example the * posts widget in Elementor Pro. Default is true, the widget has content * template. * * @access protected * * @var bool */ protected $_has_template_content = true; /** * Get element type. * * Retrieve the element type, in this case `widget`. * * @since 1.0.0 * @access public * @static * * @return string The type. */ public static function get_type() { return 'widget'; } /** * Get widget icon. * * Retrieve the widget icon. * * @since 1.0.0 * @access public * * @return string Widget icon. */ public function get_icon() { return 'eicon-apps'; } /** * Get widget keywords. * * Retrieve the widget keywords. * * @since 1.0.10 * @access public * * @return array Widget keywords. */ public function get_keywords() { return []; } /** * Get widget categories. * * Retrieve the widget categories. * * @since 1.0.10 * @access public * * @return array Widget categories. */ public function get_categories() { return [ 'general' ]; } /** * Widget base constructor. * * Initializing the widget base class. * * @since 1.0.0 * @access public * * @throws \Exception If arguments are missing when initializing a full widget * instance. * * @param array $data Widget data. Default is an empty array. * @param array|null $args Optional. Widget default arguments. Default is null. */ public function __construct( $data = [], $args = null ) { parent::__construct( $data, $args ); $is_type_instance = $this->is_type_instance(); if ( ! $is_type_instance && null === $args ) { throw new \Exception( '`$args` argument is required when initializing a full widget instance.' ); } if ( $is_type_instance ) { $this->_register_skins(); $widget_name = $this->get_name(); /** * Widget skin init. * * Fires when Elementor widget is being initialized. * * The dynamic portion of the hook name, `$widget_name`, refers to the widget name. * * @since 1.0.0 * * @param Widget_Base $this The current widget. */ do_action( "elementor/widget/{$widget_name}/skins_init", $this ); } } /** * Get stack. * * Retrieve the widget stack of controls. * * @since 1.9.2 * @access public * * @param bool $with_common_controls Optional. Whether to include the common controls. Default is true. * * @return array Widget stack of controls. */ public function get_stack( $with_common_controls = true ) { $stack = parent::get_stack(); if ( $with_common_controls && 'common' !== $this->get_unique_name() ) { /** @var Widget_Common $common_widget */ $common_widget = Plugin::$instance->widgets_manager->get_widget_types( 'common' ); $stack['controls'] = array_merge( $stack['controls'], $common_widget->get_controls() ); $stack['tabs'] = array_merge( $stack['tabs'], $common_widget->get_tabs_controls() ); } return $stack; } /** * Get widget controls pointer index. * * Retrieve widget pointer index where the next control should be added. * * While using injection point, it will return the injection point index. Otherwise index of the last control of the * current widget itself without the common controls, plus one. * * @since 1.9.2 * @access public * * @return int Widget controls pointer index. */ public function get_pointer_index() { $injection_point = $this->get_injection_point(); if ( null !== $injection_point ) { return $injection_point['index']; } return count( $this->get_stack( false )['controls'] ); } /** * Show in panel. * * Whether to show the widget in the panel or not. By default returns true. * * @since 1.0.0 * @access public * * @return bool Whether to show the widget in the panel or not. */ public function show_in_panel() { return true; } /** * Start widget controls section. * * Used to add a new section of controls to the widget. Regular controls and * skin controls. * * Note that when you add new controls to widgets they must be wrapped by * `start_controls_section()` and `end_controls_section()`. * * @since 1.0.0 * @access public * * @param string $section_id Section ID. * @param array $args Section arguments Optional. */ public function start_controls_section( $section_id, array $args = [] ) { parent::start_controls_section( $section_id, $args ); static $is_first_section = true; if ( $is_first_section ) { $this->register_skin_control(); $is_first_section = false; } } /** * Register the Skin Control if the widget has skins. * * An internal method that is used to add a skin control to the widget. * Added at the top of the controls section. * * @since 2.0.0 * @access private */ private function register_skin_control() { $skins = $this->get_skins(); if ( ! empty( $skins ) ) { $skin_options = []; if ( $this->_has_template_content ) { $skin_options[''] = __( 'Default', 'elementor' ); } foreach ( $skins as $skin_id => $skin ) { $skin_options[ $skin_id ] = $skin->get_title(); } // Get the first item for default value $default_value = array_keys( $skin_options ); $default_value = array_shift( $default_value ); if ( 1 >= count( $skin_options ) ) { $this->add_control( '_skin', [ 'label' => __( 'Skin', 'elementor' ), 'type' => Controls_Manager::HIDDEN, 'default' => $default_value, ] ); } else { $this->add_control( '_skin', [ 'label' => __( 'Skin', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => $default_value, 'options' => $skin_options, ] ); } } } /** * Register widget skins. * * This method is activated while initializing the widget base class. It is * used to assign skins to widgets with `add_skin()` method. * * Usage: * * protected function _register_skins() { * $this->add_skin( new Skin_Classic( $this ) ); * } * * @since 1.7.12 * @access protected */ protected function _register_skins() {} /** * Get initial config. * * Retrieve the current widget initial configuration. * * Adds more configuration on top of the controls list, the tabs assigned to * the control, element name, type, icon and more. This method also adds * widget type, keywords and categories. * * @since 2.9.0 * @access protected * * @return array The initial widget config. */ protected function get_initial_config() { $config = [ 'widget_type' => $this->get_name(), 'keywords' => $this->get_keywords(), 'categories' => $this->get_categories(), 'html_wrapper_class' => $this->get_html_wrapper_class(), 'show_in_panel' => $this->show_in_panel(), ]; $stack = Plugin::$instance->controls_manager->get_element_stack( $this ); if ( $stack ) { $config['controls'] = $this->get_stack( false )['controls']; $config['tabs_controls'] = $this->get_tabs_controls(); } return array_merge( parent::get_initial_config(), $config ); } /** * @since 2.3.1 * @access protected */ protected function should_print_empty() { return false; } /** * Print widget content template. * * Used to generate the widget content template on the editor, using a * Backbone JavaScript template. * * @since 2.0.0 * @access protected * * @param string $template_content Template content. */ protected function print_template_content( $template_content ) { ?> <div class="elementor-widget-container"> <?php echo $template_content; // XSS ok. ?> </div> <?php } /** * Parse text editor. * * Parses the content from rich text editor with shortcodes, oEmbed and * filtered data. * * @since 1.0.0 * @access protected * * @param string $content Text editor content. * * @return string Parsed content. */ protected function parse_text_editor( $content ) { /** This filter is documented in wp-includes/widgets/class-wp-widget-text.php */ $content = apply_filters( 'widget_text', $content, $this->get_settings() ); $content = shortcode_unautop( $content ); $content = do_shortcode( $content ); $content = wptexturize( $content ); if ( $GLOBALS['wp_embed'] instanceof \WP_Embed ) { $content = $GLOBALS['wp_embed']->autoembed( $content ); } return $content; } /** * Get HTML wrapper class. * * Retrieve the widget container class. Can be used to override the * container class for specific widgets. * * @since 2.0.9 * @access protected */ protected function get_html_wrapper_class() { return 'elementor-widget-' . $this->get_name(); } /** * Add widget render attributes. * * Used to add attributes to the current widget wrapper HTML tag. * * @since 1.0.0 * @access protected */ protected function _add_render_attributes() { parent::_add_render_attributes(); $this->add_render_attribute( '_wrapper', 'class', [ 'elementor-widget', $this->get_html_wrapper_class(), ] ); $settings = $this->get_settings(); $this->add_render_attribute( '_wrapper', 'data-widget_type', $this->get_name() . '.' . ( ! empty( $settings['_skin'] ) ? $settings['_skin'] : 'default' ) ); } /** * Add lightbox data to image link. * * Used to add lightbox data attributes to image link HTML. * * @since 2.9.1 * @access public * * @param string $link_html Image link HTML. * @param string $id Attachment id. * * @return string Image link HTML with lightbox data attributes. */ public function add_lightbox_data_to_image_link( $link_html, $id ) { $settings = $this->get_settings_for_display(); $open_lightbox = isset( $settings['open_lightbox'] ) ? $settings['open_lightbox'] : null; if ( Plugin::$instance->editor->is_edit_mode() ) { $this->add_render_attribute( 'link', 'class', 'elementor-clickable', true ); } $this->add_lightbox_data_attributes( 'link', $id, $open_lightbox, $this->get_id(), true ); return preg_replace( '/^<a/', '<a ' . $this->get_render_attribute_string( 'link' ), $link_html ); } /** * Add Light-Box attributes. * * Used to add Light-Box-related data attributes to links that open media files. * * @param array|string $element The link HTML element. * @param int $id The ID of the image * @param string $lightbox_setting_key The setting key that dictates weather to open the image in a lightbox * @param string $group_id Unique ID for a group of lightbox images * @param bool $overwrite Optional. Whether to overwrite existing * attribute. Default is false, not to overwrite. * * @return Widget_Base Current instance of the widget. * @since 2.9.0 * @access public * */ public function add_lightbox_data_attributes( $element, $id = null, $lightbox_setting_key = null, $group_id = null, $overwrite = false ) { $general_settings_model = SettingsManager::get_settings_managers( 'general' )->get_model(); $is_global_image_lightbox_enabled = 'yes' === $general_settings_model->get_settings( 'elementor_global_image_lightbox' ); if ( 'no' === $lightbox_setting_key ) { if ( $is_global_image_lightbox_enabled ) { $this->add_render_attribute( $element, 'data-elementor-open-lightbox', 'no' ); } return $this; } if ( 'yes' !== $lightbox_setting_key && ! $is_global_image_lightbox_enabled ) { return $this; } $attributes['data-elementor-open-lightbox'] = 'yes'; if ( $group_id ) { $attributes['data-elementor-lightbox-slideshow'] = $group_id; } if ( $id ) { $lightbox_image_attributes = Plugin::$instance->images_manager->get_lightbox_image_attributes( $id ); if ( isset( $lightbox_image_attributes['title'] ) ) { $attributes['data-elementor-lightbox-title'] = $lightbox_image_attributes['title']; } if ( isset( $lightbox_image_attributes['description'] ) ) { $attributes['data-elementor-lightbox-description'] = $lightbox_image_attributes['description']; } } $this->add_render_attribute( $element, $attributes, null, $overwrite ); return $this; } /** * Render widget output on the frontend. * * Used to generate the final HTML displayed on the frontend. * * Note that if skin is selected, it will be rendered by the skin itself, * not the widget. * * @since 1.0.0 * @access public */ public function render_content() { /** * Before widget render content. * * Fires before Elementor widget is being rendered. * * @since 1.0.0 * * @param Widget_Base $this The current widget. */ do_action( 'elementor/widget/before_render_content', $this ); ob_start(); $skin = $this->get_current_skin(); if ( $skin ) { $skin->set_parent( $this ); $skin->render(); } else { $this->render(); } $widget_content = ob_get_clean(); if ( empty( $widget_content ) ) { return; } ?> <div class="elementor-widget-container"> <?php /** * Render widget content. * * Filters the widget content before it's rendered. * * @since 1.0.0 * * @param string $widget_content The content of the widget. * @param Widget_Base $this The widget. */ $widget_content = apply_filters( 'elementor/widget/render_content', $widget_content, $this ); echo $widget_content; // XSS ok. ?> </div> <?php } /** * Render widget plain content. * * Elementor saves the page content in a unique way, but it's not the way * WordPress saves data. This method is used to save generated HTML to the * database as plain content the WordPress way. * * When rendering plain content, it allows other WordPress plugins to * interact with the content - to search, check SEO and other purposes. It * also allows the site to keep working even if Elementor is deactivated. * * Note that if the widget uses shortcodes to display the data, the best * practice is to return the shortcode itself. * * Also note that if the widget don't display any content it should return * an empty string. For example Elementor Pro Form Widget uses this method * to return an empty string because there is no content to return. This way * if Elementor Pro will be deactivated there won't be any form to display. * * @since 1.0.0 * @access public */ public function render_plain_content() { $this->render_content(); } /** * Before widget rendering. * * Used to add stuff before the widget `_wrapper` element. * * @since 1.0.0 * @access public */ public function before_render() { ?> <div <?php $this->print_render_attribute_string( '_wrapper' ); ?>> <?php } /** * After widget rendering. * * Used to add stuff after the widget `_wrapper` element. * * @since 1.0.0 * @access public */ public function after_render() { ?> </div> <?php } /** * Get the element raw data. * * Retrieve the raw element data, including the id, type, settings, child * elements and whether it is an inner element. * * The data with the HTML used always to display the data, but the Elementor * editor uses the raw data without the HTML in order not to render the data * again. * * @since 1.0.0 * @access public * * @param bool $with_html_content Optional. Whether to return the data with * HTML content or without. Used for caching. * Default is false, without HTML. * * @return array Element raw data. */ public function get_raw_data( $with_html_content = false ) { $data = parent::get_raw_data( $with_html_content ); unset( $data['isInner'] ); $data['widgetType'] = $this->get_data( 'widgetType' ); if ( $with_html_content ) { ob_start(); $this->render_content(); $data['htmlCache'] = ob_get_clean(); } return $data; } /** * Print widget content. * * Output the widget final HTML on the frontend. * * @since 1.0.0 * @access protected */ protected function _print_content() { $this->render_content(); } /** * Get default data. * * Retrieve the default widget data. Used to reset the data on initialization. * * @since 1.0.0 * @access protected * * @return array Default data. */ protected function get_default_data() { $data = parent::get_default_data(); $data['widgetType'] = ''; return $data; } /** * Get default child type. * * Retrieve the widget child type based on element data. * * @since 1.0.0 * @access protected * * @param array $element_data Widget ID. * * @return array|false Child type or false if it's not a valid widget. */ protected function _get_default_child_type( array $element_data ) { return Plugin::$instance->elements_manager->get_element_types( 'section' ); } /** * Get repeater setting key. * * Retrieve the unique setting key for the current repeater item. Used to connect the current element in the * repeater to it's settings model and it's control in the panel. * * PHP usage (inside `Widget_Base::render()` method): * * $tabs = $this->get_settings( 'tabs' ); * foreach ( $tabs as $index => $item ) { * $tab_title_setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $index ); * $this->add_inline_editing_attributes( $tab_title_setting_key, 'none' ); * echo '<div ' . $this->get_render_attribute_string( $tab_title_setting_key ) . '>' . $item['tab_title'] . '</div>'; * } * * @since 1.8.0 * @access protected * * @param string $setting_key The current setting key inside the repeater item (e.g. `tab_title`). * @param string $repeater_key The repeater key containing the array of all the items in the repeater (e.g. `tabs`). * @param int $repeater_item_index The current item index in the repeater array (e.g. `3`). * * @return string The repeater setting key (e.g. `tabs.3.tab_title`). */ protected function get_repeater_setting_key( $setting_key, $repeater_key, $repeater_item_index ) { return implode( '.', [ $repeater_key, $repeater_item_index, $setting_key ] ); } /** * Add inline editing attributes. * * Define specific area in the element to be editable inline. The element can have several areas, with this method * you can set the area inside the element that can be edited inline. You can also define the type of toolbar the * user will see, whether it will be a basic toolbar or an advanced one. * * Note: When you use wysiwyg control use the advanced toolbar, with textarea control use the basic toolbar. Text * control should not have toolbar. * * PHP usage (inside `Widget_Base::render()` method): * * $this->add_inline_editing_attributes( 'text', 'advanced' ); * echo '<div ' . $this->get_render_attribute_string( 'text' ) . '>' . $this->get_settings( 'text' ) . '</div>'; * * @since 1.8.0 * @access protected * * @param string $key Element key. * @param string $toolbar Optional. Toolbar type. Accepted values are `advanced`, `basic` or `none`. Default is * `basic`. */ protected function add_inline_editing_attributes( $key, $toolbar = 'basic' ) { if ( ! Plugin::$instance->editor->is_edit_mode() ) { return; } $this->add_render_attribute( $key, [ 'class' => 'elementor-inline-editing', 'data-elementor-setting-key' => $key, ] ); if ( 'basic' !== $toolbar ) { $this->add_render_attribute( $key, [ 'data-elementor-inline-editing-toolbar' => $toolbar, ] ); } } /** * Add new skin. * * Register new widget skin to allow the user to set custom designs. Must be * called inside the `_register_skins()` method. * * @since 1.0.0 * @access public * * @param Skin_Base $skin Skin instance. */ public function add_skin( Skin_Base $skin ) { Plugin::$instance->skins_manager->add_skin( $this, $skin ); } /** * Get single skin. * * Retrieve a single skin based on skin ID, from all the skin assigned to * the widget. If the skin does not exist or not assigned to the widget, * return false. * * @since 1.0.0 * @access public * * @param string $skin_id Skin ID. * * @return string|false Single skin, or false. */ public function get_skin( $skin_id ) { $skins = $this->get_skins(); if ( isset( $skins[ $skin_id ] ) ) { return $skins[ $skin_id ]; } return false; } /** * Get current skin ID. * * Retrieve the ID of the current skin. * * @since 1.0.0 * @access public * * @return string Current skin. */ public function get_current_skin_id() { return $this->get_settings( '_skin' ); } /** * Get current skin. * * Retrieve the current skin, or if non exist return false. * * @since 1.0.0 * @access public * * @return Skin_Base|false Current skin or false. */ public function get_current_skin() { return $this->get_skin( $this->get_current_skin_id() ); } /** * Remove widget skin. * * Unregister an existing skin and remove it from the widget. * * @since 1.0.0 * @access public * * @param string $skin_id Skin ID. * * @return \WP_Error|true Whether the skin was removed successfully from the widget. */ public function remove_skin( $skin_id ) { return Plugin::$instance->skins_manager->remove_skin( $this, $skin_id ); } /** * Get widget skins. * * Retrieve all the skin assigned to the widget. * * @since 1.0.0 * @access public * * @return Skin_Base[] */ public function get_skins() { return Plugin::$instance->skins_manager->get_skins( $this ); } /** * @param string $plugin_title Plugin's title * @param string $since Plugin version widget was deprecated * @param string $last Plugin version in which the widget will be removed * @param string $replacement Widget replacement */ protected function deprecated_notice( $plugin_title, $since, $last = '', $replacement = '' ) { $this->start_controls_section( 'Deprecated', [ 'label' => __( 'Deprecated', 'elementor' ), ] ); $this->add_control( 'deprecated_notice', [ 'type' => Controls_Manager::DEPRECATED_NOTICE, 'widget' => $this->get_title(), 'since' => $since, 'last' => $last, 'plugin' => $plugin_title, 'replacement' => $replacement, ] ); $this->end_controls_section(); } }
dvadf
dvadf
| ver. 1.4 |
Github
|
.
| PHP 7.0.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings