���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC favorites/module.php000064400000011547146731356320010566 0ustar00register( Widgets::class ); $this->populate(); Plugin::instance()->data_manager_v2->register_controller( new Controller() ); add_filter( 'elementor/tracker/send_tracking_data_params', [ $this, 'add_tracking_data' ] ); } /** * Add usage data related to favorites. * * @param $params * * @return array */ public function add_tracking_data( $params ) { $params['usages']['favorites'] = $this->get(); return $params; } public function get_name() { return 'favorites'; } /** * Get user favorites by type. * * @param string[]|string $type * * @return array */ public function get( $type = null ) { if ( null === $type ) { $type = array_keys( $this->types ); } if ( is_array( $type ) ) { return array_intersect_key( $this->combined(), array_flip( (array) $type ) ); } return $this->type_instance( $type ) ->values(); } /** * Merge new user favorites to a type. * * @param string $type * @param array|string $favorites * @param bool $store * * @return array|bool */ public function merge( $type, $favorites, $store = true ) { return $this->update( $type, $favorites, static::ACTION_MERGE, $store ); } /** * Delete existing favorites from a type. * * @param string $type * @param array|string $favorites * @param bool $store * * @return array|int */ public function delete( $type, $favorites, $store = true ) { return $this->update( $type, $favorites, static::ACTION_DELETE, $store ); } /** * Update favorites on a type by merging or deleting from it. * * @param $type * @param $favorites * @param $action * @param bool $store * * @return array|boolean */ public function update( $type, $favorites, $action, $store = true ) { $type_instance = $this->type_instance( $type ); $favorites = $type_instance->prepare( $favorites ); switch ( $action ) { case static::ACTION_MERGE: $type_instance->merge( $favorites ); break; case static::ACTION_DELETE: $type_instance->filter( function( $value ) use ( $favorites ) { return ! in_array( $value, $favorites, true ); } ); break; default: $this->action_doesnt_exists( $action ); } if ( $store && ! $this->store() ) { return false; } return $type_instance->values(); } /** * Get registered favorites type instance. * * @param string $type * * @return Favorites_Type */ public function type_instance( $type ) { return $this->types[ $type ]; } /** * Register a new type class. * * @param string $class */ public function register( $class ) { $type_instance = new $class(); $this->types[ $type_instance->get_name() ] = $type_instance; } /** * Returns all available types keys. * * @return string[] */ public function available() { return array_keys( $this->types ); } /** * Combine favorites from all types into a single array. * * @return array */ protected function combined() { $all = []; foreach ( $this->types as $type ) { $favorites = $type->values(); if ( ! empty( $favorites ) ) { $all[ $type->get_name() ] = $favorites; } } return $all; } /** * Populate all type classes with the stored data. */ protected function populate() { $combined = $this->retrieve(); foreach ( $this->types as $key => $type ) { if ( isset( $combined[ $key ] ) ) { $type->merge( $combined[ $key ] ); } } } /** * Retrieve stored user favorites types. * * @return mixed|false */ protected function retrieve() { return get_user_option( static::OPTION_NAME ); } /** * Update all changes to user favorites type. * * @return int|bool */ protected function store() { return update_user_option( get_current_user_id(), static::OPTION_NAME, $this->combined() ); } /** * Throw action doesn't exist exception. * * @param string $action */ public function action_doesnt_exists( $action ) { throw new \InvalidArgumentException( sprintf( "Action '%s' to apply on favorites doesn't exists", $action ) ); } } favorites/controller.php000064400000003720146731356320011456 0ustar00get_module(); $type = $request->get_param( 'id' ); $favorite = $request->get_param( 'favorite' ); $module->update( $type, $favorite, $module::ACTION_MERGE ); return $module->get( $type ); } public function delete_item( $request ) { $module = $this->get_module(); $type = $request->get_param( 'id' ); $favorite = $request->get_param( 'favorite' ); $module->update( $type, $favorite, $module::ACTION_DELETE ); return $module->get( $type ); } public function create_item_permissions_check( $request ) { return current_user_can( 'edit_posts' ); } public function delete_item_permissions_check( $request ) { return $this->create_item_permissions_check( $request ); } /** * Get the favorites module instance. * * @return Module */ protected function get_module() { return Plugin::instance()->modules_manager->get_modules( 'favorites' ); } public function register_endpoints() { $this->index_endpoint->register_item_route( \WP_REST_Server::CREATABLE, [ 'id_arg_type_regex' => '[\w]+', 'id' => [ 'description' => 'Type of favorites.', 'type' => 'string', 'required' => true, ], 'favorite' => [ 'description' => 'The favorite slug to create.', 'type' => 'string', 'required' => true, ], ] ); $this->index_endpoint->register_item_route( \WP_REST_Server::DELETABLE, [ 'id_arg_type_regex' => '[\w]+', 'id' => [ 'description' => 'Type of favorites.', 'type' => 'string', 'required' => true, ], 'favorite' => [ 'description' => 'The favorite slug to delete.', 'type' => 'string', 'required' => true, ], ] ); } } favorites/types/widgets.php000064400000002767146731356320012117 0ustar00get_available() ); } /** * Get all available widgets. * * @return string[] */ public function get_available() { return array_merge( array_keys( Plugin::instance()->widgets_manager->get_widget_types() ), array_keys( Plugin::instance()->elements_manager->get_element_types() ) ); } /** * Update the categories of a widget inside a filter. * * @param $document */ public function update_widget_categories( $document ) { foreach ( $this->values() as $favorite ) { $widget = Plugin::$instance->widgets_manager->get_widget_types( $favorite ); // If it's not a widget, maybe it's an element. if ( ! $widget ) { $widget = Plugin::$instance->elements_manager->get_element_types( $favorite ); } if ( $widget ) { $widget->set_config( 'categories', [ static::CATEGORY_SLUG ] ); } } } } favorites/favorites-type.php000064400000001446146731356320012257 0ustar00values(); } if ( ! is_array( $favorites ) ) { return [ $favorites ]; } return $favorites; } } wp-cli/module.php000064400000002451146731356320007751 0ustar00register_logger( 'cli', 'Elementor\Modules\WpCli\Cli_Logger' ); $logger->set_default_logger( 'cli' ); } public function init_common() { Plugin::$instance->init_common(); } /** * * @since 2.1.0 * @access public */ public function __construct() { add_action( 'cli_init', [ $this, 'init_common' ] ); add_action( 'elementor/loggers/register', [ $this, 'register_cli_logger' ] ); \WP_CLI::add_command( 'elementor', '\Elementor\Modules\WpCli\Command' ); \WP_CLI::add_command( 'elementor update', '\Elementor\Modules\WpCli\Update' ); \WP_CLI::add_command( 'elementor library', '\Elementor\Modules\WpCli\Library' ); } } wp-cli/command.php000064400000010116146731356320010077 0ustar00 'ids', 'number' => 0, ] ); foreach ( $blog_ids as $blog_id ) { switch_to_blog( $blog_id ); Plugin::$instance->files_manager->clear_cache(); \WP_CLI::success( 'Flushed the Elementor CSS Cache for site - ' . get_option( 'home' ) ); restore_current_blog(); } } else { Plugin::$instance->files_manager->clear_cache(); \WP_CLI::success( 'Flushed the Elementor CSS Cache' ); } } /** * Print system info powered by Elementor * * ## EXAMPLES * * 1. wp elementor system-info * - This will print the System Info in JSON format * * @since 3.0.11 * @access public * @alias system-info */ public function system_info() { echo wp_json_encode( \Elementor\Tracker::get_tracking_data() ); } /** * Replace old URLs with new URLs in all Elementor pages. * * [--force] * Suppress error messages. instead, return "0 database rows affected.". * * ## EXAMPLES * * 1. wp elementor replace-urls * - This will replace all URLs with the URL. * * 2. wp elementor replace-urls --force * - This will replace all URLs with the URL without throw errors. * * @access public * @alias replace-urls */ public function replace_urls( $args, $assoc_args ) { if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set the `old` URL' ); } if ( empty( $args[1] ) ) { \WP_CLI::error( 'Please set the `new` URL' ); } try { $results = Utils::replace_urls( $args[0], $args[1] ); \WP_CLI::success( $results ); } catch ( \Exception $e ) { if ( isset( $assoc_args['force'] ) ) { \WP_CLI::success( '0 database rows affected.' ); } else { \WP_CLI::error( $e->getMessage() ); } } } /** * Sync Elementor Library. * * ## EXAMPLES * * 1. wp elementor sync-library * - This will sync the library with Elementor cloud library. * * @since 2.1.0 * @access public * @alias sync-library */ public function sync_library( $args, $assoc_args ) { // TODO: // \WP_CLI::warning( 'command is deprecated since 2.8.0 Please use: wp elementor library sync' ); $data = Api::get_library_data( true ); if ( empty( $data ) ) { \WP_CLI::error( 'Cannot sync library.' ); } \WP_CLI::success( 'Library has been synced.' ); } /** * Import template files to the Library. * * ## EXAMPLES * * 1. wp elementor import-library * - This will import a file or a zip of multiple files to the library. * * @since 2.1.0 * @access public * @alias import-library */ public function import_library( $args, $assoc_args ) { // TODO: // \WP_CLI::warning( 'command is deprecated since 2.8.0 Please use: wp elementor library import' ); if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set file path.' ); } /** @var Source_Local $source */ $source = Plugin::$instance->templates_manager->get_source( 'local' ); $imported_items = $source->import_template( basename( $args[0] ), $args[0] ); if ( is_wp_error( $imported_items ) ) { \WP_CLI::error( $imported_items->get_error_message() ); } \WP_CLI::success( count( $imported_items ) . ' item(s) has been imported.' ); } } wp-cli/library.php000064400000015473146731356320010140 0ustar00 'ids', 'number' => 0, ] ); foreach ( $blog_ids as $blog_id ) { switch_to_blog( $blog_id ); \WP_CLI::line( 'Site #' . $blog_id . ' - ' . get_option( 'blogname' ) ); $this->do_sync( isset( $assoc_args['force'] ) ); \WP_CLI::success( 'Done! - ' . get_option( 'home' ) ); restore_current_blog(); } } else { $this->do_sync( isset( $assoc_args['force'] ) ); \WP_CLI::success( 'Done!' ); } } /** * Import template files to the Library. * * [--returnType] * Forms of output. Possible values are 'ids', 'info'. * if this parameter won't be specified, the import info will be output. * * ## EXAMPLES * * 1. wp elementor library import * - This will import a file or a zip of multiple files to the library. * - file-path can be a path or url. * * 2. wp elementor library import --returnType=info,ids * * @param $args * @param $assoc_args * * @since 2.8.0 * @access public */ public function import( $args, $assoc_args ) { if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set file path.' ); } $file = $args[0]; $imported_items_ids = []; $return_type = \WP_CLI\Utils\get_flag_value( $assoc_args, 'returnType', 'info' ); /** @var Source_Local $source */ $source = Plugin::$instance->templates_manager->get_source( 'local' ); if ( filter_var( $file, FILTER_VALIDATE_URL ) ) { $tmp_path = download_url( $file ); if ( is_wp_error( $tmp_path ) ) { \WP_CLI::error( $tmp_path->get_error_message() ); } $file = $tmp_path; } $imported_items = $source->import_template( basename( $file ), $file ); if ( is_wp_error( $imported_items ) ) { \WP_CLI::error( $imported_items->get_error_message() ); } foreach ( $imported_items as $item ) { $imported_items_ids[] = $item['template_id']; } $imported_items_ids = implode( ',', $imported_items_ids ); if ( 'ids' === $return_type ) { \WP_CLI::line( $imported_items_ids ); } else { \WP_CLI::success( count( $imported_items ) . ' item(s) has been imported.' ); } if ( isset( $tmp_path ) ) { // Remove the temporary file, now that we're done with it. Plugin::$instance->uploads_manager->remove_file_or_dir( $file ); } } /** * Import all template files from a directory. * * ## EXAMPLES * * 1. wp elementor library import-dir * - This will import all JSON files from * * @param $args * * @since 3.4.7 * @access public * @alias import-dir */ public function import_dir( $args ) { if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set dir path.' ); } $dir = $args[0]; if ( ! file_exists( $dir ) ) { \WP_CLI::error( "Dir `{$dir}` not found." ); } $files = glob( $dir . '/*.json' ); if ( empty( $files ) ) { \WP_CLI::error( 'Files not found.' ); } /** @var Source_Local $source */ $source = Plugin::$instance->templates_manager->get_source( 'local' ); $succeed = []; $errors = []; foreach ( $files as $file ) { $basename = basename( $file ); if ( ! file_exists( $file ) ) { $errors[ $basename ] = $file . ' file not found.'; continue; } $imported_items = $source->import_template( $basename, $file ); if ( is_wp_error( $imported_items ) ) { $errors[ $basename ] = $imported_items->get_error_message(); } else { $succeed[ $basename ] = true; } } $succeed_message = count( $succeed ) . ' item(s) has been imported.'; if ( ! empty( $errors ) ) { $error_message = var_export( $errors, 1 ); if ( ! empty( $succeed ) ) { $error_message = $succeed_message . ' ' . count( $errors ) . ' has errors: ' . $error_message; } \WP_CLI::error( $error_message ); } \WP_CLI::success( $succeed_message ); } /** * Connect site to Elementor Library. * (Network is not supported) * * --user * The user to connect * * --token * A connect token from Elementor Account Dashboard. * * ## EXAMPLES * * 1. wp elementor library connect --user=admin --token= * - This will connect the admin to Elementor library. * * @param $args * @param $assoc_args * * @since 2.8.0 * @access public */ public function connect( $args, $assoc_args ) { if ( ! get_current_user_id() ) { \WP_CLI::error( 'Please set user to connect (--user=).' ); } if ( empty( $assoc_args['token'] ) ) { \WP_CLI::error( 'Please set connect token.' ); } $_REQUEST['mode'] = 'cli'; $_REQUEST['token'] = $assoc_args['token']; $app = $this->get_library_app(); $app->set_auth_mode( 'cli' ); $app->action_authorize(); $app->action_get_token(); } /** * Disconnect site from Elementor Library. * * --user * The user to disconnect * * ## EXAMPLES * * 1. wp elementor library disconnect --user=admin * - This will disconnect the admin from Elementor library. * * @param $args * @param $assoc_args * * @since 2.8.0 * @access public */ public function disconnect() { if ( ! get_current_user_id() ) { \WP_CLI::error( 'Please set user to connect (--user=).' ); } $_REQUEST['mode'] = 'cli'; $this->get_library_app()->action_disconnect(); } private function do_sync() { $data = Api::get_library_data( true ); if ( empty( $data ) ) { \WP_CLI::error( 'Cannot sync library.' ); } } /** * @return \Elementor\Core\Common\Modules\Connect\Apps\Library */ private function get_library_app() { $connect = Plugin::$instance->common->get_component( 'connect' ); $app = $connect->get_app( 'library' ); // Before init. if ( ! $app ) { $connect->init(); $app = $connect->get_app( 'library' ); } return $app; } } wp-cli/update.php000064400000005054146731356320007750 0ustar00 'ids', 'number' => 0, ] ); foreach ( $blog_ids as $blog_id ) { switch_to_blog( $blog_id ); \WP_CLI::line( 'Site #' . $blog_id . ' - ' . get_option( 'blogname' ) ); $this->do_db_upgrade( $assoc_args ); \WP_CLI::success( 'Done! - ' . get_option( 'home' ) ); restore_current_blog(); } } else { $this->do_db_upgrade( $assoc_args ); } } protected function get_update_db_manager_class() { return '\Elementor\Core\Upgrade\Manager'; } protected function do_db_upgrade( $assoc_args ) { $manager_class = $this->get_update_db_manager_class(); /** @var \Elementor\Core\Upgrade\Manager $manager */ $manager = new $manager_class(); $updater = $manager->get_task_runner(); if ( $updater->is_process_locked() && empty( $assoc_args['force'] ) ) { \WP_CLI::warning( 'Oops! Process is already running. Use --force to force run.' ); return; } if ( ! $manager->should_upgrade() ) { \WP_CLI::success( 'The DB is already updated!' ); return; } $callbacks = $manager->get_upgrade_callbacks(); $did_tasks = false; if ( ! empty( $callbacks ) ) { Plugin::$instance->logger->get_logger()->info( 'Update DB has been started', [ 'meta' => [ 'plugin' => $manager->get_plugin_label(), 'from' => $manager->get_current_version(), 'to' => $manager->get_new_version(), ], ] ); $updater->handle_immediately( $callbacks ); $did_tasks = true; } $manager->on_runner_complete( $did_tasks ); \WP_CLI::success( count( $callbacks ) . ' updates(s) has been applied.' ); } } wp-cli/cli-logger.php000064400000001154146731356320010507 0ustar00format( 'raw' ); switch ( $item->type ) { case self::LEVEL_WARNING: \WP_CLI::warning( $message ); break; case self::LEVEL_ERROR: \WP_CLI::error( $message, false ); break; default: \WP_CLI::log( $message ); break; } parent::save_log( $item ); } } image-loading-optimization/module.php000064400000025707146731356320014010 0ustar00filter_images( $buffer ); } /** * Check for image in the content provided and apply optimization logic on them. * * @param string $content Content to be analyzed. * @return string Content with optimized images. */ private function filter_images( $content ) { return preg_replace_callback( '/]+>/', function ( $matches ) { return $this->loading_optimization_image( $matches[0] ); }, $content ); } /** * Apply loading optimization logic on the image. * * @param mixed $image Original image tag. * @return string Optimized image. */ public function loading_optimization_image( $image ) { if ( isset( self::$image_visited[ $image ] ) ) { return self::$image_visited[ $image ]; } $optimized_image = $this->add_loading_optimization_attrs( $image ); self::$image_visited[ $image ] = $optimized_image; return $optimized_image; } /** * Adds optimization attributes to an `img` HTML tag. * * @param string $image The HTML `img` tag where the attribute should be added. * @return string Converted `img` tag with optimization attributes added. */ private function add_loading_optimization_attrs( $image ) { $width = preg_match( '/ width=["\']([0-9]+)["\']/', $image, $match_width ) ? (int) $match_width[1] : null; $height = preg_match( '/ height=["\']([0-9]+)["\']/', $image, $match_height ) ? (int) $match_height[1] : null; $loading_val = preg_match( '/ loading=["\']([A-Za-z]+)["\']/', $image, $match_loading ) ? $match_loading[1] : null; $fetchpriority_val = preg_match( '/ fetchpriority=["\']([A-Za-z]+)["\']/', $image, $match_fetchpriority ) ? $match_fetchpriority[1] : null; // Images should have height and dimension width for the loading optimization attributes to be added. if ( ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) { return $image; } $optimization_attrs = $this->get_loading_optimization_attributes( [ 'width' => $width, 'height' => $height, 'loading' => $loading_val, 'fetchpriority' => $fetchpriority_val, ] ); if ( ! empty( $optimization_attrs['fetchpriority'] ) ) { $image = str_replace( 'increase_content_media_count( 0 ); $increase_count = true; if ( $content_media_count < $this->omit_threshold ) { $maybe_in_viewport = true; } else { $maybe_in_viewport = false; } } if ( $maybe_in_viewport ) { $loading_attrs = $this->maybe_add_fetchpriority_high_attr( $loading_attrs, $attr ); } else { $loading_attrs['loading'] = 'lazy'; } if ( $increase_count ) { $this->increase_content_media_count(); } elseif ( $maybe_increase_count ) { if ( $this->get_min_priority_img_pixels() <= $attr['width'] * $attr['height'] ) { $this->increase_content_media_count(); } } return $loading_attrs; } /** * Helper to get the minimum threshold for number of pixels an image needs to have to be considered "priority". * * @return int The minimum number of pixels (width * height). Default is 50000. */ private function get_min_priority_img_pixels() { /** * Filter the minimum pixel threshold used to determine if an image should have fetchpriority="high" applied. * * @see https://developer.wordpress.org/reference/hooks/wp_min_priority_img_pixels/ * * @param int $pixels The minimum number of pixels (with * height). * @return int The filtered value. */ return apply_filters( 'elementor/image-loading-optimization/min_priority_img_pixels', $this->min_priority_img_pixels ); } /** * Keeps a count of media image. * * @param int $amount Amount by which count must be increased. * @return int current image count. */ private function increase_content_media_count( $amount = 1 ) { static $content_media_count = 0; $content_media_count += $amount; return $content_media_count; } /** * Determines whether to add `fetchpriority='high'` to loading attributes. * * @param array $loading_attrs Array of the loading optimization attributes for the element. * @param array $attr Array of the attributes for the element. * @return array Updated loading optimization attributes for the element. */ private function maybe_add_fetchpriority_high_attr( $loading_attrs, $attr ) { if ( isset( $attr['fetchpriority'] ) ) { if ( 'high' === $attr['fetchpriority'] ) { $loading_attrs['fetchpriority'] = 'high'; $this->high_priority_element_flag( false ); } return $loading_attrs; } // Lazy-loading and `fetchpriority="high"` are mutually exclusive. if ( isset( $loading_attrs['loading'] ) && 'lazy' === $loading_attrs['loading'] ) { return $loading_attrs; } if ( ! $this->high_priority_element_flag() ) { return $loading_attrs; } if ( $this->get_min_priority_img_pixels() <= $attr['width'] * $attr['height'] ) { $loading_attrs['fetchpriority'] = 'high'; $this->high_priority_element_flag( false ); } return $loading_attrs; } /** * Accesses a flag that indicates if an element is a possible candidate for `fetchpriority='high'`. * * @param bool $value Optional. Used to change the static variable. Default null. * @return bool Returns true if high-priority element was marked already, otherwise false. */ private function high_priority_element_flag( $value = null ) { static $high_priority_element = true; if ( is_bool( $value ) ) { $high_priority_element = $value; } return $high_priority_element; } } history/module.php000064400000003007146731356320010255 0ustar00modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); return []; } /** * @since 2.3.0 * @access public */ public function add_templates() { Plugin::$instance->common->add_template( __DIR__ . '/views/history-panel-template.php' ); Plugin::$instance->common->add_template( __DIR__ . '/views/revisions-panel-template.php' ); } /** * History module constructor. * * Initializing Elementor history module. * * @since 1.7.0 * @access public */ public function __construct() { add_action( 'elementor/editor/init', [ $this, 'add_templates' ] ); } } history/views/revisions-panel-template.php000064400000006744146731356320015067 0ustar00 history/views/history-panel-template.php000064400000003664146731356320014545 0ustar00 history/revisions-manager.php000064400000023664146731356320012434 0ustar00documents->get( $post_id ); if ( $document && $document->is_built_with_elementor() ) { $post_content .= ''; } return $post_content; } /** * @since 2.0.0 * @access public * @static */ public static function remove_temp_post_content() { global $post; $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document || ! $document->is_built_with_elementor() ) { return; } $post->post_content = str_replace( '', '', $post->post_content ); } /** * @since 1.7.0 * @access public * @static * * @param int $post_id * @param array $query_args * @param bool $parse_result * * @return array */ public static function get_revisions( $post_id = 0, $query_args = [], $parse_result = true ) { $post = get_post( $post_id ); if ( ! $post || empty( $post->ID ) ) { return []; } $revisions = []; $default_query_args = [ 'posts_per_page' => self::MAX_REVISIONS_TO_DISPLAY, 'meta_key' => '_elementor_data', ]; $query_args = array_merge( $default_query_args, $query_args ); $posts = wp_get_post_revisions( $post->ID, $query_args ); if ( ! wp_revisions_enabled( $post ) ) { $autosave = Utils::get_post_autosave( $post->ID ); if ( $autosave ) { if ( $parse_result ) { array_unshift( $posts, $autosave ); } else { array_unshift( $posts, $autosave->ID ); } } } if ( $parse_result ) { array_unshift( $posts, $post ); } else { array_unshift( $posts, $post->ID ); return $posts; } $current_time = current_time( 'timestamp' ); /** @var \WP_Post $revision */ foreach ( $posts as $revision ) { $date = date_i18n( _x( 'M j @ H:i', 'revision date format', 'elementor' ), strtotime( $revision->post_modified ) ); $human_time = human_time_diff( strtotime( $revision->post_modified ), $current_time ); if ( $revision->ID === $post->ID ) { $type = 'current'; $type_label = esc_html__( 'Current Version', 'elementor' ); } elseif ( false !== strpos( $revision->post_name, 'autosave' ) ) { $type = 'autosave'; $type_label = esc_html__( 'Autosave', 'elementor' ); } else { $type = 'revision'; $type_label = esc_html__( 'Revision', 'elementor' ); } if ( ! isset( self::$authors[ $revision->post_author ] ) ) { self::$authors[ $revision->post_author ] = [ 'avatar' => get_avatar( $revision->post_author, 22 ), 'display_name' => get_the_author_meta( 'display_name', $revision->post_author ), ]; } $revisions[] = [ 'id' => $revision->ID, 'author' => self::$authors[ $revision->post_author ]['display_name'], 'timestamp' => strtotime( $revision->post_modified ), 'date' => sprintf( /* translators: 1: Human readable time difference, 2: Date. */ esc_html__( '%1$s ago (%2$s)', 'elementor' ), '', '' ), 'type' => $type, 'typeLabel' => $type_label, 'gravatar' => self::$authors[ $revision->post_author ]['avatar'], ]; } return $revisions; } /** * @since 1.9.2 * @access public * @static */ public static function update_autosave( $autosave_data ) { self::save_revision( $autosave_data['ID'] ); } /** * @since 1.7.0 * @access public * @static */ public static function save_revision( $revision_id ) { $parent_id = wp_is_post_revision( $revision_id ); if ( $parent_id ) { Plugin::$instance->db->safe_copy_elementor_meta( $parent_id, $revision_id ); } } /** * @since 1.7.0 * @access public * @static */ public static function restore_revision( $parent_id, $revision_id ) { $parent = Plugin::$instance->documents->get( $parent_id ); $revision = Plugin::$instance->documents->get( $revision_id ); if ( ! $parent || ! $revision ) { return; } $is_built_with_elementor = $revision->is_built_with_elementor(); $parent->set_is_built_with_elementor( $is_built_with_elementor ); if ( ! $is_built_with_elementor ) { return; } Plugin::$instance->db->copy_elementor_meta( $revision_id, $parent_id ); $post_css = Post_CSS::create( $parent_id ); $post_css->update(); } /** * @since 2.3.0 * @access public * @static * * @param $data * * @return array * @throws \Exception */ public static function ajax_get_revision_data( array $data ) { if ( ! isset( $data['id'] ) ) { throw new \Exception( 'You must set the revision ID.' ); } $revision = Plugin::$instance->documents->get_with_permissions( $data['id'] ); return [ 'settings' => $revision->get_settings(), 'elements' => $revision->get_elements_data(), ]; } /** * @since 1.7.0 * @access public * @static */ public static function add_revision_support_for_all_post_types() { $post_types = get_post_types_by_support( 'elementor' ); foreach ( $post_types as $post_type ) { add_post_type_support( $post_type, 'revisions' ); } } /** * @since 2.0.0 * @access public * @static * @param array $return_data * @param Document $document * * @return array */ public static function on_ajax_save_builder_data( $return_data, $document ) { $post_id = $document->get_main_id(); $latest_revisions = self::get_revisions( $post_id, [ 'posts_per_page' => 1, ] ); $all_revision_ids = self::get_revisions( $post_id, [ 'fields' => 'ids', ], false ); // Send revisions data only if has revisions. if ( ! empty( $latest_revisions ) ) { $current_revision_id = self::current_revision_id( $post_id ); $return_data = array_replace_recursive( $return_data, [ 'config' => [ 'document' => [ 'revisions' => [ 'current_id' => $current_revision_id, ], ], ], 'latest_revisions' => $latest_revisions, 'revisions_ids' => $all_revision_ids, ] ); } return $return_data; } /** * @since 1.7.0 * @access public * @static */ public static function db_before_save( $status, $has_changes ) { if ( $has_changes ) { self::handle_revision(); } } public static function document_config( $settings, $post_id ) { $settings['revisions'] = [ 'enabled' => ( $post_id && wp_revisions_enabled( get_post( $post_id ) ) ), 'current_id' => self::current_revision_id( $post_id ), ]; return $settings; } /** * Localize settings. * * Add new localized settings for the revisions manager. * * Fired by `elementor/editor/editor_settings` filter. * * @since 1.7.0 * @deprecated 3.1.0 * @access public * @static */ public static function editor_settings() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); return []; } /** * @throws \Exception */ public static function ajax_get_revisions( $data ) { Plugin::$instance->documents->check_permissions( $data['editor_post_id'] ); return self::get_revisions(); } /** * @since 2.3.0 * @access public * @static */ public static function register_ajax_actions( Ajax $ajax ) { $ajax->register_ajax_action( 'get_revisions', [ __CLASS__, 'ajax_get_revisions' ] ); $ajax->register_ajax_action( 'get_revision_data', [ __CLASS__, 'ajax_get_revision_data' ] ); } /** * @since 1.7.0 * @access private * @static */ private static function register_actions() { add_action( 'wp_restore_post_revision', [ __CLASS__, 'restore_revision' ], 10, 2 ); add_action( 'init', [ __CLASS__, 'add_revision_support_for_all_post_types' ], 9999 ); add_filter( 'elementor/document/config', [ __CLASS__, 'document_config' ], 10, 2 ); add_action( 'elementor/db/before_save', [ __CLASS__, 'db_before_save' ], 10, 2 ); add_action( '_wp_put_post_revision', [ __CLASS__, 'save_revision' ] ); add_action( 'wp_creating_autosave', [ __CLASS__, 'update_autosave' ] ); add_action( 'elementor/ajax/register_actions', [ __CLASS__, 'register_ajax_actions' ] ); // Hack to avoid delete the auto-save revision in WP editor. add_filter( 'edit_post_content', [ __CLASS__, 'avoid_delete_auto_save' ], 10, 2 ); add_action( 'edit_form_after_title', [ __CLASS__, 'remove_temp_post_content' ] ); if ( wp_doing_ajax() ) { add_filter( 'elementor/documents/ajax_save/return_data', [ __CLASS__, 'on_ajax_save_builder_data' ], 10, 2 ); } } /** * @since 1.9.0 * @access private * @static */ private static function current_revision_id( $post_id ) { $current_revision_id = $post_id; $autosave = Utils::get_post_autosave( $post_id ); if ( is_object( $autosave ) ) { $current_revision_id = $autosave->ID; } return $current_revision_id; } } ai/module.php000064400000064676146731356320007170 0ustar00register_layout_experiment(); add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) { $connect_module->register_app( 'ai', Ai::get_class_name() ); } ); add_action( 'elementor/ajax/register_actions', function( $ajax ) { $handlers = [ 'ai_get_user_information' => [ $this, 'ajax_ai_get_user_information' ], 'ai_get_remote_config' => [ $this, 'ajax_ai_get_remote_config' ], 'ai_get_completion_text' => [ $this, 'ajax_ai_get_completion_text' ], 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ], 'ai_get_custom_code' => [ $this, 'ajax_ai_get_custom_code' ], 'ai_get_custom_css' => [ $this, 'ajax_ai_get_custom_css' ], 'ai_set_get_started' => [ $this, 'ajax_ai_set_get_started' ], 'ai_set_status_feedback' => [ $this, 'ajax_ai_set_status_feedback' ], 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ], 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ], 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ], 'ai_get_image_to_image_mask' => [ $this, 'ajax_ai_get_image_to_image_mask' ], 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ], 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ], 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ], 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ], 'ai_upload_image' => [ $this, 'ajax_ai_upload_image' ], 'ai_generate_layout' => [ $this, 'ajax_ai_generate_layout' ], 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ], 'ai_get_history' => [ $this, 'ajax_ai_get_history' ], 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ], 'ai_toggle_favorite_history_item' => [ $this, 'ajax_ai_toggle_favorite_history_item' ], ]; foreach ( $handlers as $tag => $callback ) { $ajax->register_ajax_action( $tag, $callback ); } } ); add_action( 'elementor/editor/before_enqueue_scripts', function() { $this->enqueue_main_script(); if ( $this->is_layout_active() ) { $this->enqueue_layout_script(); } } ); add_action( 'elementor/editor/after_enqueue_styles', function() { wp_enqueue_style( 'elementor-ai-editor', $this->get_css_assets_url( 'modules/ai/editor' ), [], ELEMENTOR_VERSION ); } ); add_action( 'elementor/preview/enqueue_styles', function() { if ( $this->is_layout_active() ) { wp_enqueue_style( 'elementor-ai-layout-preview', $this->get_css_assets_url( 'modules/ai/layout-preview' ), [], ELEMENTOR_VERSION ); } } ); add_filter( 'elementor/document/save/data', function ( $data ) { if ( $this->is_layout_active() ) { return $this->remove_temporary_containers( $data ); } return $data; } ); } private function register_layout_experiment() { Plugin::$instance->experiments->add_feature( [ 'name' => static::LAYOUT_EXPERIMENT, 'title' => esc_html__( 'Build with AI', 'elementor' ), 'description' => esc_html__( 'Tap into the potential of AI to easily create and customize containers to your specifications, right within Elementor. This feature comes packed with handy AI tools, including generation, variations, and URL references.', 'elementor' ), 'default' => Experiments_Manager::STATE_ACTIVE, 'release_status' => Experiments_Manager::RELEASE_STATUS_STABLE, 'dependencies' => [ 'container', ], ] ); } private function enqueue_main_script() { wp_enqueue_script( 'elementor-ai', $this->get_js_assets_url( 'ai' ), [ 'react', 'react-dom', 'backbone-marionette', 'elementor-web-cli', 'wp-date', 'elementor-common', 'elementor-editor-modules', 'elementor-editor-document', 'elementor-v2-ui', 'elementor-v2-icons', ], ELEMENTOR_VERSION, true ); $config = [ 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ), 'connect_url' => $this->get_ai_connect_url(), ]; if ( $this->get_ai_app()->is_connected() ) { // Use a cached version, don't call the API on every editor load. $config['usage'] = $this->get_ai_app()->get_cached_usage(); } wp_localize_script( 'elementor-ai', 'ElementorAiConfig', $config ); wp_set_script_translations( 'elementor-ai', 'elementor' ); if ( $this->get_ai_app()->is_connected() && ! empty( $config['is_get_started'] ) ) { $remote_config = Utils::get_cached_callback( [ $this->get_ai_app(), 'get_remote_config' ], 'ai_remote_config-' . get_current_user_id(), HOUR_IN_SECONDS ); if ( ! is_wp_error( $remote_config ) && ! empty( $remote_config['config']['remoteIntegrationUrl'] ) ) { wp_enqueue_script( 'elementor-ai-integration', $remote_config['config']['remoteIntegrationUrl'], [ 'elementor-ai', ], ELEMENTOR_VERSION, true ); } add_filter( 'script_loader_tag', function( $tag, $handle ) { if ( 'elementor-ai-integration' === $handle ) { return str_replace( ' src', ' type="module" src', $tag ); } return $tag; }, 10, 2 ); } } private function enqueue_layout_script() { wp_enqueue_script( 'elementor-ai-layout', $this->get_js_assets_url( 'ai-layout' ), [ 'react', 'react-dom', 'backbone-marionette', 'elementor-common', 'elementor-web-cli', 'elementor-editor-modules', 'elementor-ai', 'elementor-v2-ui', 'elementor-v2-icons', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-ai-layout', 'elementor' ); } private function is_layout_active() { return Plugin::$instance->experiments->is_feature_active( self::LAYOUT_EXPERIMENT ); } private function remove_temporary_containers( $data ) { if ( empty( $data['elements'] ) ) { return $data; } // If for some reason the document has been saved during an AI Layout session, // ensure that the temporary containers are removed from the data. $data['elements'] = array_filter( $data['elements'], function( $element ) { $is_preview_container = strpos( $element['id'], 'e-ai-preview-container' ) === 0; $is_screenshot_container = strpos( $element['id'], 'e-ai-screenshot-container' ) === 0; return ! $is_preview_container && ! $is_screenshot_container; } ); return $data; } private function get_ai_connect_url() { $app = $this->get_ai_app(); return $app->get_admin_url( 'authorize', [ 'utm_source' => 'ai-popup', 'utm_campaign' => 'connect-account', 'utm_medium' => 'wp-dash', 'source' => 'generic', ] ); } public function ajax_ai_get_user_information( $data ) { $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { return [ 'is_connected' => false, 'connect_url' => $this->get_ai_connect_url(), ]; } $user_usage = wp_parse_args( $app->get_usage(), [ 'hasAiSubscription' => false, 'usedQuota' => 0, 'quota' => 100, ] ); return [ 'is_connected' => true, 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ), 'usage' => $user_usage, ]; } public function ajax_ai_get_remote_config() { $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { return []; } return $app->get_remote_config(); } private function verify_permissions( $editor_post_id ) { $document = Plugin::$instance->documents->get( $editor_post_id ); if ( ! $document ) { throw new \Exception( 'Document not found' ); } if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) { throw new \Exception( 'Access denied' ); } } public function ajax_ai_get_image_prompt_enhancer( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_prompt_enhanced( $data['prompt'], [], $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_completion_text( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_completion_text( $data['payload']['prompt'], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } private function get_ai_app() : Ai { return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' ); } private function get_request_context( $data ) { if ( empty( $data['context'] ) ) { return []; } return $data['context']; } private function get_request_ids( $data ) { if ( empty( $data['requestIds'] ) ) { return new \stdClass(); } return $data['requestIds']; } public function ajax_ai_get_edit_text( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['input'] ) ) { throw new \Exception( 'Missing input' ); } if ( empty( $data['payload']['instruction'] ) ) { throw new \Exception( 'Missing instruction' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_edit_text( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_custom_code( $data ) { $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( empty( $data['payload']['language'] ) ) { throw new \Exception( 'Missing language' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_custom_code( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_custom_css( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( empty( $data['payload']['html_markup'] ) ) { $data['html_markup'] = ''; } if ( empty( $data['payload']['element_id'] ) ) { throw new \Exception( 'Missing element_id' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_custom_css( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_set_get_started( $data ) { $app = $this->get_ai_app(); User::set_introduction_viewed( [ 'introductionKey' => 'ai_get_started', ] ); return $app->set_get_started(); } public function ajax_ai_set_status_feedback( $data ) { if ( empty( $data['response_id'] ) ) { throw new \Exception( 'Missing response_id' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $app->set_status_feedback( $data['response_id'] ); return []; } public function ajax_ai_get_text_to_image( $data ) { $this->verify_permissions( $data['editor_post_id'] ); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_text_to_image( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['settings'] ) ) { throw new \Exception( 'Missing prompt settings' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image( [ 'prompt' => $data['payload']['prompt'], 'promptSettings' => $data['payload']['settings'], 'attachment_id' => $data['payload']['image']['id'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_upscale( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['promptSettings'] ) ) { throw new \Exception( 'Missing prompt settings' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_upscale( [ 'promptSettings' => $data['payload']['promptSettings'], 'attachment_id' => $data['payload']['image']['id'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_replace_background( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Prompt Missing' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_replace_background( [ 'attachment_id' => $data['payload']['image']['id'], 'prompt' => $data['payload']['prompt'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_remove_background( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_remove_background( [ 'attachment_id' => $data['payload']['image']['id'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_mask( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['settings'] ) ) { throw new \Exception( 'Missing prompt settings' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } if ( empty( $data['payload']['mask'] ) ) { throw new \Exception( 'Missing Mask' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_mask( [ 'prompt' => $data['payload']['prompt'], 'promptSettings' => $data['payload']['settings'], 'attachment_id' => $data['payload']['image']['id'], 'mask' => $data['payload']['mask'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_outpainting( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } if ( empty( $data['payload']['mask'] ) ) { throw new \Exception( 'Missing Expended Image' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_out_painting( [ 'prompt' => $data['payload']['prompt'], 'mask' => $data['payload']['mask'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_upload_image( $data ) { if ( empty( $data['image'] ) ) { throw new \Exception( 'Missing image data' ); } $image = $data['image']; if ( empty( $image['image_url'] ) ) { throw new \Exception( 'Missing image_url' ); } $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] ); if ( is_wp_error( $image_data ) ) { throw new \Exception( $image_data->get_error_message() ); } if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) { $app = $this->get_ai_app(); $app->set_used_gallery_image( $image['id'] ); } return [ 'image' => array_merge( $image_data, $data ), ]; } public function ajax_ai_generate_layout( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['prompt'] ) && empty( $data['attachments'] ) ) { throw new \Exception( 'Missing prompt / attachments' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $result = $app->generate_layout( $data, $this->prepare_generate_layout_context() ); if ( is_wp_error( $result ) ) { $message = $result->get_error_message(); if ( is_array( $message ) ) { $message = implode( ', ', $message ); throw new \Exception( $message ); } $this->throw_on_error( $result ); } $elements = $result['text']['elements'] ?? []; $base_template_id = $result['baseTemplateId'] ?? null; $template_type = $result['templateType'] ?? null; if ( empty( $elements ) || ! is_array( $elements ) ) { throw new \Exception( 'unknown_error' ); } if ( 1 === count( $elements ) ) { $template = $elements[0]; } else { $template = [ 'elType' => 'container', 'elements' => $elements, 'settings' => [ 'content_width' => 'full', 'flex_gap' => [ 'column' => '0', 'row' => '0', 'unit' => 'px', ], 'padding' => [ 'unit' => 'px', 'top' => '0', 'right' => '0', 'bottom' => '0', 'left' => '0', 'isLinked' => true, ], ], ]; } return [ 'all' => [], 'text' => $template, 'response_id' => $result['responseId'], 'usage' => $result['usage'], 'base_template_id' => $base_template_id, 'template_type' => $template_type, ]; } public function ajax_ai_get_layout_prompt_enhancer( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $result = $app->get_layout_prompt_enhanced( $data['prompt'], $data['enhance_type'], $this->prepare_generate_layout_context() ); $this->throw_on_error( $result ); return [ 'text' => $result['text'] ?? $data['prompt'], 'response_id' => $result['responseId'] ?? '', 'usage' => $result['usage'] ?? '', ]; } private function prepare_generate_layout_context() { $kit = Plugin::$instance->kits_manager->get_active_kit(); if ( ! $kit ) { return []; } $kits_data = Collection::make( $kit->get_data()['settings'] ?? [] ); $colors = $kits_data ->filter( function ( $_, $key ) { return in_array( $key, [ 'system_colors', 'custom_colors' ], true ); } ) ->flatten() ->filter( function ( $val ) { return ! empty( $val['_id'] ); } ) ->map( function ( $val ) { return [ 'id' => $val['_id'], 'label' => $val['title'] ?? null, 'value' => $val['color'] ?? null, ]; } ); $typography = $kits_data ->filter( function ( $_, $key ) { return in_array( $key, [ 'system_typography', 'custom_typography' ], true ); } ) ->flatten() ->filter( function ( $val ) { return ! empty( $val['_id'] ); } ) ->map( function ( $val ) { $font_size = null; if ( isset( $val['typography_font_size']['unit'], $val['typography_font_size']['size'] ) ) { $prop = $val['typography_font_size']; $font_size = 'custom' === $prop['unit'] ? $prop['size'] : $prop['size'] . $prop['unit']; } return [ 'id' => $val['_id'], 'label' => $val['title'] ?? null, 'value' => [ 'family' => $val['typography_font_family'] ?? null, 'weight' => $val['typography_font_weight'] ?? null, 'style' => $val['typography_font_style'] ?? null, 'size' => $font_size, ], ]; } ); return [ 'globals' => [ 'colors' => $colors->all(), 'typography' => $typography->all(), ], ]; } private function upload_image( $image_url, $image_title, $parent_post_id = 0 ) { if ( ! current_user_can( 'upload_files' ) ) { throw new \Exception( 'Not Allowed to Upload images' ); } $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' ); if ( ! empty( $attachment_id['error'] ) ) { return new \WP_Error( 'upload_error', $attachment_id['error'] ); } return [ 'id' => $attachment_id, 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ), 'alt' => $image_title, 'source' => 'library', ]; } public function ajax_ai_get_history( $data ): array { $type = $data['type'] ?? self::HISTORY_TYPE_ALL; if ( ! in_array( $type, self::VALID_HISTORY_TYPES, true ) ) { throw new \Exception( 'Invalid history type' ); } $page = sanitize_text_field( $data['page'] ?? 1 ); $limit = sanitize_text_field( $data['limit'] ?? 10 ); $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $result = $app->get_history_by_type( $type, $page, $limit, $context ); if ( is_wp_error( $result ) ) { throw new \Exception( $result->get_error_message() ); } return $result; } public function ajax_ai_delete_history_item( $data ): array { if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) { throw new \Exception( 'Missing id parameter' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $result = $app->delete_history_item( $data['id'], $context ); if ( is_wp_error( $result ) ) { throw new \Exception( $result->get_error_message() ); } return []; } public function ajax_ai_toggle_favorite_history_item( $data ): array { if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) { throw new \Exception( 'Missing id parameter' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $result = $app->toggle_favorite_history_item( $data['id'], $context ); if ( is_wp_error( $result ) ) { throw new \Exception( $result->get_error_message() ); } return []; } /** * @param mixed $result */ private function throw_on_error( $result ): void { if ( is_wp_error( $result ) ) { wp_send_json_error( [ 'message' => $result->get_error_message(), 'extra_data' => $result->get_error_data(), ] ); } } } ai/connect/ai.php000064400000040345146731356320007710 0ustar00ai_request( 'POST', 'status/check', [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_cached_usage() { $cache_key = 'elementor_ai_usage'; $cache_time = 24 * HOUR_IN_SECONDS; $usage = get_site_transient( $cache_key ); if ( ! $usage ) { $usage = $this->get_usage(); set_site_transient( $cache_key, $usage, $cache_time ); } return $usage; } public function get_remote_config() { return $this->ai_request( 'GET', 'remote-config/config', [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } /** * get_file_payload * @param $filename * @param $file_type * @param $file_path * @param $boundary * * @return string */ private function get_file_payload( $filename, $file_type, $file_path, $boundary ) { $name = $filename ?? basename( $file_path ); $mine_type = 'image' === $file_type ? image_type_to_mime_type( exif_imagetype( $file_path ) ) : $file_type; $payload = ''; // Upload the file $payload .= '--' . $boundary; $payload .= "\r\n"; $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"; filename="' . esc_attr( $name ) . '"' . "\r\n"; $payload .= 'Content-Type: ' . $mine_type . "\r\n"; $payload .= "\r\n"; $payload .= file_get_contents( $file_path ); $payload .= "\r\n"; return $payload; } private function get_upload_request_body( $body, $file, $boundary, $file_name = '' ) { $payload = ''; // add all body fields as standard POST fields: foreach ( $body as $name => $value ) { $payload .= '--' . $boundary; $payload .= "\r\n"; $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"' . "\r\n\r\n"; $payload .= $value; $payload .= "\r\n"; } if ( is_array( $file ) ) { foreach ( $file as $key => $file_data ) { $payload .= $this->get_file_payload( $file_data['name'], $file_data['type'], $file_data['path'], $boundary ); } } else { $image_mime = image_type_to_mime_type( exif_imagetype( $file ) ); // @todo: add validation for supported image types if ( empty( $file_name ) ) { $file_name = basename( $file ); } $payload .= $this->get_file_payload( $file_name, $image_mime, $file, $boundary ); } $payload .= '--' . $boundary . '--'; return $payload; } private function ai_request( $method, $endpoint, $body, $file = false, $file_name = '', $format = 'default' ) { $headers = [ 'x-elementor-ai-version' => '2', ]; if ( $file ) { $boundary = wp_generate_password( 24, false ); $body = $this->get_upload_request_body( $body, $file, $boundary, $file_name ); // add content type header $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary; } elseif ( 'json' === $format ) { $headers['Content-Type'] = 'application/json'; $body = wp_json_encode( $body ); } return $this->http_request( $method, $endpoint, [ 'timeout' => 100, 'headers' => $headers, 'body' => $body, ], [ 'return_type' => static::HTTP_RETURN_TYPE_ARRAY, 'with_error_data' => true, ] ); } public function set_get_started() { return $this->ai_request( 'POST', 'status/get-started', [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function set_status_feedback( $response_id ) { return $this->ai_request( 'POST', 'status/feedback/' . $response_id, [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function set_used_gallery_image( $image_id ) { return $this->ai_request( 'POST', 'status/used-gallery-image/' . $image_id, [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_completion_text( $prompt, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/completion', [ 'prompt' => $prompt, 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } /** * get_image_prompt_enhanced * @param $prompt * * @return mixed|\WP_Error */ public function get_image_prompt_enhanced( $prompt, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/enhance-image-prompt', [ 'prompt' => $prompt, 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_edit_text( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/edit', [ 'input' => $data['payload']['input'], 'instruction' => $data['payload']['instruction'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_custom_code( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/custom-code', [ 'prompt' => $data['payload']['prompt'], 'language' => $data['payload']['language'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_custom_css( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/custom-css', [ 'prompt' => $data['payload']['prompt'], 'html_markup' => $data['payload']['html_markup'], 'element_id' => $data['payload']['element_id'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } /** * get_text_to_image * @param $prompt * @param $prompt_settings * * @return mixed|\WP_Error */ public function get_text_to_image( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'image/text-to-image', [ self::PROMPT => $data['payload']['prompt'], self::IMAGE_TYPE => $data['payload']['settings'][ self::IMAGE_TYPE ] . '/' . $data['payload']['settings'][ self::STYLE_PRESET ], self::ASPECT_RATIO => $data['payload']['settings'][ self::ASPECT_RATIO ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } /** * get_image_to_image * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image', [ self::PROMPT => $image_data[ self::PROMPT ], self::IMAGE_TYPE => $image_data['promptSettings'][ self::IMAGE_TYPE ] . '/' . $image_data['promptSettings'][ self::STYLE_PRESET ], self::IMAGE_STRENGTH => $image_data['promptSettings'][ self::IMAGE_STRENGTH ], self::ASPECT_RATIO => $image_data['promptSettings'][ self::ASPECT_RATIO ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * get_image_to_image_upscale * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_upscale( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/upscale', [ self::IMAGE_RESOLUTION => $image_data['promptSettings']['upscale_to'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * get_image_to_image_remove_background * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_remove_background( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/remove-background', [ 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * get_image_to_image_remove_text * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_replace_background( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/replace-background', [ self::PROMPT => $image_data[ self::PROMPT ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * store_temp_file * used to store a temp file for the AI request and deletes it once the request is done * @param $file_content * @param $file_ext * * @return string */ private function store_temp_file( $file_content, $file_ext = '' ) { $temp_file = str_replace( '.tmp', '', wp_tempnam() . $file_ext ); file_put_contents( $temp_file, $file_content ); // make sure the temp file is deleted on shutdown register_shutdown_function( function () use ( $temp_file ) { if ( file_exists( $temp_file ) ) { unlink( $temp_file ); } } ); return $temp_file; } /** * get_image_to_image_out_painting * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_out_painting( $image_data, $context, $request_ids ) { $img_content = str_replace( ' ', '+', $image_data['mask'] ); $img_content = substr( $img_content, strpos( $img_content, ',' ) + 1 ); $img_content = base64_decode( $img_content ); $mask_file = $this->store_temp_file( $img_content, '.png' ); if ( ! $mask_file ) { throw new \Exception( 'Expended Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/outpainting', [ self::PROMPT => $image_data[ self::PROMPT ], self::IMAGE_TYPE => '', 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], [ [ 'name' => 'image', 'type' => 'image', 'path' => $mask_file, ], ] ); return $result; } /** * get_image_to_image_mask * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_mask( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); $mask_file = $this->store_temp_file( $image_data['mask'], '.svg' ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } if ( ! $mask_file ) { throw new \Exception( 'Mask file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/inpainting', [ self::PROMPT => $image_data[ self::PROMPT ], self::IMAGE_TYPE => $image_data['promptSettings'][ self::IMAGE_TYPE ] . '/' . $image_data['promptSettings'][ self::STYLE_PRESET ], self::IMAGE_STRENGTH => $image_data['promptSettings'][ self::IMAGE_STRENGTH ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], [ [ 'name' => 'image', 'type' => 'image', 'path' => $image_file, ], [ 'name' => 'mask_image', 'type' => 'image/svg+xml', 'path' => $mask_file, ], ] ); return $result; } public function generate_layout( $data, $context ) { $endpoint = 'generate/layout'; $body = [ 'prompt' => $data['prompt'], 'variationType' => (int) $data['variationType'], 'ids' => $data['ids'], ]; if ( ! empty( $data['prevGeneratedIds'] ) ) { $body['generatedBaseTemplatesIds'] = $data['prevGeneratedIds']; } if ( ! empty( $data['attachments'] ) ) { $attachment = $data['attachments'][0]; switch ( $attachment['type'] ) { case 'json': $endpoint = 'generate/generate-json-variation'; $body['json'] = [ 'type' => 'elementor', 'elements' => [ $attachment['content'] ], 'label' => $attachment['label'], 'source' => $attachment['source'], ]; break; case 'url': $endpoint = 'generate/html-to-elementor'; $html = wp_json_encode( $attachment['content'] ); $body['html'] = $html; break; } } $context['currentContext'] = $data['currentContext']; $context['features'] = [ 'supportedFeatures' => [], ]; if ( ElementorUtils::has_pro() ) { $context['features']['subscriptions'] = [ 'Pro' ]; } if ( Plugin::$instance->experiments->is_feature_active( 'container_grid' ) ) { $context['features']['supportedFeatures'][] = 'Grid'; } if ( Plugin::instance()->experiments->get_active_features()['nested-elements'] ) { $context['features']['supportedFeatures'][] = 'NestedElements'; } $metadata = [ 'context' => $context, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), 'config' => [ 'generate' => [ 'all' => true, ], ], ]; $body = array_merge( $body, $metadata ); // Temp hack for platforms that filters the http_request_args, and it breaks JSON requests. remove_all_filters( 'http_request_args' ); return $this->ai_request( 'POST', $endpoint, $body, false, '', 'json' ); } public function get_layout_prompt_enhanced( $prompt, $enhance_type, $context ) { return $this->ai_request( 'POST', 'generate/enhance-prompt', [ 'prompt' => $prompt, 'enhance_type' => $enhance_type, 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_history_by_type( $type, $page, $limit, $context = [] ) { $endpoint = Module::HISTORY_TYPE_ALL === $type ? 'history' : add_query_arg( [ 'page' => $page, 'limit' => $limit, ], "history/{$type}" ); return $this->ai_request( 'POST', $endpoint, [ 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function delete_history_item( $id, $context = [] ) { return $this->ai_request( 'DELETE', 'history/' . $id, [ 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function toggle_favorite_history_item( $id, $context = [] ) { return $this->ai_request( 'POST', sprintf( 'history/%s/favorite', $id ), [ 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } protected function init() {} } dev-tools/module.php000064400000002760146731356320010475 0ustar00deprecation = new Deprecation( ELEMENTOR_VERSION ); add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'register_scripts' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] ); add_action( 'elementor/frontend/after_register_scripts', [ $this, 'register_scripts' ] ); add_action( 'elementor/common/after_register_scripts', [ $this, 'register_scripts' ] ); } public function get_name() { return 'dev-tools'; } public function register_scripts() { wp_register_script( 'elementor-dev-tools', $this->get_js_assets_url( 'dev-tools' ), [], ELEMENTOR_VERSION, true ); $this->print_config( 'elementor-dev-tools' ); } protected function get_init_settings() { return [ 'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ), 'urls' => [ 'assets' => ELEMENTOR_ASSETS_URL, ], 'deprecation' => $this->deprecation->get_settings(), ]; } } dev-tools/deprecation.php000064400000023015146731356320011501 0ustar00current_version = $current_version; } public function get_settings() { return [ 'soft_notices' => $this->soft_deprecated_notices, 'soft_version_count' => self::SOFT_VERSIONS_COUNT, 'hard_version_count' => self::HARD_VERSIONS_COUNT, 'current_version' => ELEMENTOR_VERSION, ]; } /** * Get total of major. * * Since `get_total_major` cannot determine how much really versions between 2.9.0 and 3.3.0 if there is 2.10.0 version for example, * versions with major2 more then 9 will be added to total. * * @since 3.1.0 * * @param array $parsed_version * * @return int */ public function get_total_major( $parsed_version ) { $major1 = $parsed_version['major1']; $major2 = $parsed_version['major2']; $major2 = $major2 > 9 ? 9 : $major2; $minor = 0; $total = intval( "{$major1}{$major2}{$minor}" ); if ( $total > 99 ) { $total = $total / 10; } else { $total = intval( $total / 10 ); } if ( $parsed_version['major2'] > 9 ) { $total += $parsed_version['major2'] - 9; } return $total; } /** * Get next version. * * @since 3.1.0 * * @param string $version * @param int $count * * @return string|false */ public function get_next_version( $version, $count = 1 ) { $version = $this->parse_version( $version ); if ( ! $version ) { return false; } $version['total'] = $this->get_total_major( $version ) + $count; $total = $version['total']; if ( $total > 9 ) { $version['major1'] = intval( $total / 10 ); $version['major2'] = $total % 10; } else { $version['major1'] = 0; $version['major2'] = $total; } $version['minor'] = 0; return $this->implode_version( $version ); } /** * Implode parsed version to string version. * * @since 3.1.0 * * @param array $parsed_version * * @return string */ public function implode_version( $parsed_version ) { $major1 = $parsed_version['major1']; $major2 = $parsed_version['major2']; $minor = $parsed_version['minor']; return "{$major1}.{$major2}.{$minor}"; } /** * Parse to an informative array. * * @since 3.1.0 * * @param string $version * * @return array|false */ public function parse_version( $version ) { $version_explode = explode( '.', $version ); $version_explode_count = count( $version_explode ); if ( $version_explode_count < 3 || $version_explode_count > 4 ) { trigger_error( 'Invalid Semantic Version string provided' ); return false; } list( $major1, $major2, $minor ) = $version_explode; $result = [ 'major1' => intval( $major1 ), 'major2' => intval( $major2 ), 'minor' => intval( $minor ), ]; if ( $version_explode_count > 3 ) { $result['build'] = $version_explode[3]; } return $result; } /** * Compare two versions, result is equal to diff of major versions. * Notice: If you want to compare between 2.9.0 and 3.3.0, and there is also a 2.10.0 version, you cannot get the right comparison * Since $this->deprecation->get_total_major cannot determine how much really versions between 2.9.0 and 3.3.0. * * @since 3.1.0 * * @param {string} $version1 * @param {string} $version2 * * @return int|false */ public function compare_version( $version1, $version2 ) { $version1 = self::parse_version( $version1 ); $version2 = self::parse_version( $version2 ); if ( $version1 && $version2 ) { $versions = [ &$version1, &$version2 ]; foreach ( $versions as &$version ) { $version['total'] = self::get_total_major( $version ); } return $version1['total'] - $version2['total']; } return false; } /** * Check Deprecation * * Checks whether the given entity is valid. If valid, this method checks whether the deprecation * should be soft (browser console notice) or hard (use WordPress' native deprecation methods). * * @since 3.1.0 * * @param string $entity - The Deprecated entity (the function/hook itself) * @param string $version * @param string $replacement Optional * @param string $base_version Optional. Default is `null` * * @return bool|void * @throws \Exception */ private function check_deprecation( $entity, $version, $replacement, $base_version = null ) { if ( null === $base_version ) { $base_version = $this->current_version; } $diff = $this->compare_version( $base_version, $version ); if ( false === $diff ) { throw new \Exception( 'Invalid deprecation diff.' ); } $print_deprecated = false; if ( defined( 'WP_DEBUG' ) && WP_DEBUG && $diff <= self::SOFT_VERSIONS_COUNT ) { // Soft deprecated. if ( ! isset( $this->soft_deprecated_notices[ $entity ] ) ) { $this->soft_deprecated_notices[ $entity ] = [ $version, $replacement, ]; } if ( defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG ) { $print_deprecated = true; } } return $print_deprecated; } /** * Deprecated Function * * Handles the deprecation process for functions. * * @since 3.1.0 * * @param string $function * @param string $version * @param string $replacement Optional. Default is '' * @param string $base_version Optional. Default is `null` * @throws \Exception */ public function deprecated_function( $function, $version, $replacement = '', $base_version = null ) { $print_deprecated = $this->check_deprecation( $function, $version, $replacement, $base_version ); if ( $print_deprecated ) { // PHPCS - We need to echo special characters because they can exist in function calls. _deprecated_function( $function, esc_html( $version ), $replacement ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } /** * Deprecated Hook * * Handles the deprecation process for hooks. * * @since 3.1.0 * * @param string $hook * @param string $version * @param string $replacement Optional. Default is '' * @param string $base_version Optional. Default is `null` * @throws \Exception */ public function deprecated_hook( $hook, $version, $replacement = '', $base_version = null ) { $print_deprecated = $this->check_deprecation( $hook, $version, $replacement, $base_version ); if ( $print_deprecated ) { _deprecated_hook( esc_html( $hook ), esc_html( $version ), esc_html( $replacement ) ); } } /** * Deprecated Argument * * Handles the deprecation process for function arguments. * * @since 3.1.0 * * @param string $argument * @param string $version * @param string $replacement * @param string $message * @throws \Exception */ public function deprecated_argument( $argument, $version, $replacement = '', $message = '' ) { $print_deprecated = $this->check_deprecation( $argument, $version, $replacement ); if ( $print_deprecated ) { $message = empty( $message ) ? '' : ' ' . $message; // These arguments are escaped because they are printed later, and are not escaped when printed. $error_message_args = [ esc_html( $argument ), esc_html( $version ) ]; if ( $replacement ) { /* translators: 1: Function argument, 2: Elementor version number, 3: Replacement argument name. */ $translation_string = esc_html__( 'The %1$s argument is deprecated since version %2$s! Use %3$s instead.', 'elementor' ); $error_message_args[] = $replacement; } else { /* translators: 1: Function argument, 2: Elementor version number. */ $translation_string = esc_html__( 'The %1$s argument is deprecated since version %2$s!', 'elementor' ); } trigger_error( vsprintf( // PHPCS - $translation_string is already escaped above. $translation_string, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped // PHPCS - $error_message_args is an array. $error_message_args // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ) . esc_html( $message ), E_USER_DEPRECATED ); } } /** * Do Deprecated Action * * A method used to run deprecated actions through Elementor's deprecation process. * * @since 3.1.0 * * @param string $hook * @param array $args * @param string $version * @param string $replacement * @param null|string $base_version * * @throws \Exception */ public function do_deprecated_action( $hook, $args, $version, $replacement = '', $base_version = null ) { if ( ! has_action( $hook ) ) { return; } $this->deprecated_hook( $hook, $version, $replacement, $base_version ); do_action_ref_array( $hook, $args ); } /** * Apply Deprecated Filter * * A method used to run deprecated filters through Elementor's deprecation process. * * @since 3.2.0 * * @param string $hook * @param array $args * @param string $version * @param string $replacement * @param null|string $base_version * * @return mixed * @throws \Exception */ public function apply_deprecated_filter( $hook, $args, $version, $replacement = '', $base_version = null ) { if ( ! has_action( $hook ) ) { // `$args` should be an array, but in order to keep BC, we need to support non-array values. if ( is_array( $args ) ) { return $args[0] ?? null; } return $args; } // BC - See the comment above. if ( ! is_array( $args ) ) { $args = [ $args ]; } // Avoid associative arrays. $args = array_values( $args ); $this->deprecated_hook( $hook, $version, $replacement, $base_version ); return apply_filters_ref_array( $hook, $args ); } } nested-elements/module.php000064400000003134146731356320011651 0ustar00 self::EXPERIMENT_NAME, 'title' => esc_html__( 'Nested Elements', 'elementor' ), 'description' => sprintf( '%1$s %2$s', esc_html__( 'Create a rich user experience by layering widgets together inside "Nested" Tabs, etc. When turned on, we’ll automatically enable new nested features. Your old widgets won’t be affected.', 'elementor' ), esc_html__( 'Learn more', 'elementor' ) ), 'release_status' => Experiments_Manager::RELEASE_STATUS_BETA, 'default' => Experiments_Manager::STATE_INACTIVE, 'dependencies' => [ 'container', ], 'new_site' => [ 'default_active' => false, 'minimum_installation_version' => '3.10.0', ], ]; } public function get_name() { return 'nested-elements'; } public function __construct() { parent::__construct(); add_action( 'elementor/controls/register', function ( $controls_manager ) { $controls_manager->register( new Controls\Control_Nested_Repeater() ); } ); add_action( 'elementor/editor/before_enqueue_scripts', function () { wp_enqueue_script( $this->get_name(), $this->get_js_assets_url( $this->get_name() ), [ 'elementor-common', ], ELEMENTOR_VERSION, true ); } ); } } nested-elements/controls/control-nested-repeater.php000064400000000743146731356320016777 0ustar00 'container', * 'settings' => [ * '_title' => __( 'Tab #1', 'elementor' ), * ], * ], * @return string */ protected function get_default_children_title() { return esc_html__( 'Item #%d', 'elementor' ); } /** * Get default children placeholder selector, Empty string, means will be added at the end view. * * @return string */ protected function get_default_children_placeholder_selector() { return ''; } protected function get_default_children_container_placeholder_selector() { return ''; } /** * @inheritDoc * * To support nesting. */ protected function _get_default_child_type( array $element_data ) { return Plugin::$instance->elements_manager->get_element_types( $element_data['elType'] ); } /** * @inheritDoc * * Adding new 'defaults' config for handling children elements. */ protected function get_initial_config() { return array_merge( parent::get_initial_config(), [ 'defaults' => [ 'elements' => $this->get_default_children_elements(), 'elements_title' => $this->get_default_children_title(), 'elements_placeholder_selector' => $this->get_default_children_placeholder_selector(), 'child_container_placeholder_selector' => $this->get_default_children_container_placeholder_selector(), 'repeater_title_setting' => $this->get_default_repeater_title_setting_key(), ], 'support_nesting' => true, ] ); } /** * @inheritDoc * * Each element including its children elements. */ public function get_raw_data( $with_html_content = false ) { $elements = []; $data = $this->get_data(); $children = $this->get_children(); foreach ( $children as $child ) { $child_raw_data = $child->get_raw_data( $with_html_content ); $elements[] = $child_raw_data; } return [ 'id' => $this->get_id(), 'elType' => $data['elType'], 'widgetType' => $data['widgetType'], 'settings' => $data['settings'], 'elements' => $elements, ]; } /** * Print child, helper method to print the child element. * * @param int $index */ public function print_child( $index ) { $children = $this->get_children(); if ( ! empty( $children[ $index ] ) ) { $children[ $index ]->print_element(); } } protected function content_template_single_repeater_item() {} public function print_template() { parent::print_template(); if ( $this->get_initial_config()['support_improved_repeaters'] ?? false ) { ?> register_layout_experiment(); if ( ! $this->is_experiment_active() ) { return; } add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) { $hook_suffix = 'toplevel_page_elementor'; add_action( "admin_print_scripts-{$hook_suffix}", [ $this, 'enqueue_home_screen_scripts' ] ); }, 10, 2 ); add_filter( 'elementor/document/urls/edit', [ $this, 'add_active_document_to_edit_link' ] ); } public function enqueue_home_screen_scripts(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $min_suffix = Utils::is_script_debug() ? '' : '.min'; wp_enqueue_script( 'e-home-screen', ELEMENTOR_ASSETS_URL . 'js/e-home-screen' . $min_suffix . '.js', [ 'react', 'react-dom', 'elementor-common', 'elementor-v2-ui', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'e-home-screen', 'elementor' ); wp_localize_script( 'e-home-screen', 'elementorHomeScreenData', $this->get_app_js_config() ); } public function is_experiment_active(): bool { return Plugin::$instance->experiments->is_feature_active( self::PAGE_ID ); } public function add_active_document_to_edit_link( $edit_link ) { $active_document = Utils::get_super_global_value( $_GET, 'active-document' ) ?? null; $active_tab = Utils::get_super_global_value( $_GET, 'active-tab' ) ?? null; if ( $active_document ) { $edit_link = add_query_arg( 'active-document', $active_document, $edit_link ); } if ( $active_tab ) { $edit_link = add_query_arg( 'active-tab', $active_tab, $edit_link ); } return $edit_link; } private function register_layout_experiment(): void { Plugin::$instance->experiments->add_feature( [ 'name' => static::PAGE_ID, 'title' => esc_html__( 'Elementor Home Screen', 'elementor' ), 'description' => esc_html__( 'Default Elementor menu page.', 'elementor' ), 'hidden' => true, 'default' => Experiments_Manager::STATE_ACTIVE, ] ); } private function get_app_js_config(): array { return API::get_home_screen_items(); } public static function get_elementor_settings_page_id(): string { return Plugin::$instance->experiments->is_feature_active( self::PAGE_ID ) ? 'settings' : Settings::PAGE_ID; } } home/transformations/filter-plugins.php000064400000005207146731356320014420 0ustar00get_add_ons_installation_status( $home_screen_data['add_ons']['repeater'] ); return $home_screen_data; } private function is_plugin( $add_on ): bool { return 'link' !== $add_on['type']; } private function get_add_ons_installation_status( array $add_ons ): array { $transformed_add_ons = []; foreach ( $add_ons as $add_on ) { if ( $this->is_plugin( $add_on ) ) { $this->handle_plugin_add_on( $add_on, $transformed_add_ons ); } else { $transformed_add_ons[] = $add_on; } } return $transformed_add_ons; } private function get_plugin_installation_status( $add_on ): string { $plugin_path = $add_on['file_path']; if ( ! $this->plugin_status_adapter->is_plugin_installed( $plugin_path ) ) { if ( 'wporg' === $add_on['type'] ) { return self::PLUGIN_IS_NOT_INSTALLED_FROM_WPORG; } return self::PLUGIN_IS_NOT_INSTALLED_NOT_FROM_WPORG; } if ( $this->wordpress_adapter->is_plugin_active( $plugin_path ) ) { return self::PLUGIN_IS_ACTIVATED; } return self::PLUGIN_IS_INSTALLED_NOT_ACTIVATED; } private function handle_plugin_add_on( array $add_on, array &$transformed_add_ons ): void { $installation_status = $this->get_plugin_installation_status( $add_on ); if ( self::PLUGIN_IS_ACTIVATED === $installation_status ) { return; } switch ( $this->get_plugin_installation_status( $add_on ) ) { case self::PLUGIN_IS_NOT_INSTALLED_NOT_FROM_WPORG: break; case self::PLUGIN_IS_NOT_INSTALLED_FROM_WPORG: $installation_url = $this->plugin_status_adapter->get_install_plugin_url( $add_on['file_path'] ); $add_on['url'] = html_entity_decode( $installation_url ); $add_on['target'] = '_self'; break; case self::PLUGIN_IS_INSTALLED_NOT_ACTIVATED: $activation_url = $this->plugin_status_adapter->get_activate_plugin_url( $add_on['file_path'] ); $add_on['url'] = html_entity_decode( $activation_url ); $add_on['button_label'] = esc_html__( 'Activate', 'elementor' ); $add_on['target'] = '_self'; break; } $transformed_add_ons[] = $add_on; } } home/transformations/create-site-settings-url.php000064400000005536146731356320016324 0ustar00get_site_settings_url_config(); $home_screen_data['get_started']['repeater'] = array_map( function( $repeater_item ) use ( $site_settings_url_config ) { if ( ! in_array( $repeater_item['title'], static::SITE_SETTINGS_ITEMS, true ) ) { return $repeater_item; } if ( ! empty( $repeater_item['tab_id'] ) ) { $site_settings_url_config['url'] = add_query_arg( [ 'active-tab' => $repeater_item['tab_id'] ], $site_settings_url_config['url'] ); } return array_merge( $repeater_item, $site_settings_url_config ); }, $home_screen_data['get_started']['repeater'] ); return $home_screen_data; } private function get_site_settings_url_config(): array { $existing_elementor_page = $this->get_elementor_page(); $site_settings_url = ! empty( $existing_elementor_page ) ? $this->get_elementor_edit_url( $existing_elementor_page->ID ) : $this->get_elementor_create_new_page_url(); return [ 'new_page' => empty( $existing_elementor_page ), 'url' => $site_settings_url, 'type' => static::URL_TYPE, ]; } private function get_elementor_create_new_page_url(): string { $active_kit_id = Plugin::$instance->kits_manager->get_active_id(); if ( empty( $active_kit_id ) ) { return Plugin::$instance->documents->get_create_new_post_url( 'page' ); } return add_query_arg( [ 'active-document' => $active_kit_id ], Plugin::$instance->documents->get_create_new_post_url( 'page' ) ); } private function get_elementor_edit_url( int $post_id ): string { $active_kit_id = Plugin::$instance->kits_manager->get_active_id(); $document = Plugin::$instance->documents->get( $post_id ); if ( ! $document ) { return ''; } return add_query_arg( [ 'active-document' => $active_kit_id ], $document->get_edit_url() ); } private function get_elementor_page() { $args = [ 'meta_key' => Document::BUILT_WITH_ELEMENTOR_META_KEY, 'sort_order' => 'asc', 'sort_column' => 'post_date', ]; $pages = get_pages( $args ); if ( empty( $pages ) ) { return null; } $show_page_on_front = 'page' === get_option( 'show_on_front' ); if ( ! $show_page_on_front ) { return $pages[0]; } $home_page_id = get_option( 'page_on_front' ); foreach ( $pages as $page ) { if ( (string) $page->ID === $home_page_id ) { return $page; } } return $pages[0]; } } home/transformations/filter-get-started-by-license.php000064400000002007146731356320017205 0ustar00has_pro = Utils::has_pro(); } private function valid_item( $item ) { $has_pro_json_not_free = $this->has_pro && 'pro' === $item['license'][0]; $is_not_pro_json_not_pro = ! $this->has_pro && 'free' === $item['license'][0]; return $has_pro_json_not_free || $is_not_pro_json_not_pro; } public function transform( array $home_screen_data ): array { $new_get_started = []; foreach ( $home_screen_data['get_started'] as $index => $item ) { if ( $this->valid_item( $item ) ) { $new_get_started[] = $item; } } $home_screen_data['get_started'] = reset( $new_get_started ); return $home_screen_data; } } home/transformations/filter-condition-introduction-meta.php000064400000003617146731356320020373 0ustar00introduction_meta_data = User::get_introduction_meta() ?? []; } public function transform( array $home_screen_data ): array { $introduction_meta_conditions = $this->get_introduction_meta_conditions( $home_screen_data ); $active_addons = $this->get_activated_addons( $introduction_meta_conditions ); $home_screen_data['add_ons']['repeater'] = $this->get_inactive_addons( $home_screen_data, $active_addons ); return $home_screen_data; } private function get_introduction_meta_conditions( $home_screen_data ): array { $add_ons = $home_screen_data['add_ons']['repeater']; $conditions = []; foreach ( $add_ons as $add_on ) { if ( array_key_exists( 'condition', $add_on ) && 'introduction_meta' === $add_on['condition']['key'] ) { $conditions[ $add_on['title'] ] = $add_on['condition']['value']; } } return $conditions; } private function get_activated_addons( $conditions ): array { $active_addons = []; foreach ( $conditions as $add_on_title => $introduction_meta_value ) { if ( ! empty( $this->introduction_meta_data[ $introduction_meta_value ] ) ) { $active_addons[] = $add_on_title; } } return $active_addons; } private function get_inactive_addons( $home_screen_data, $active_addons ): array { $add_ons = $home_screen_data['add_ons']['repeater']; $inactive_add_ons = []; foreach ( $add_ons as $add_on ) { if ( ! in_array( $add_on['title'], $active_addons ) ) { $inactive_add_ons[] = $add_on; } } return $inactive_add_ons; } } home/transformations/base/transformations-abstract.php000064400000000762146731356320017421 0ustar00wordpress_adapter = $args['wordpress_adapter'] ?? null; $this->plugin_status_adapter = $args['plugin_status_adapter'] ?? null; } abstract public function transform( array $home_screen_data ): array; } home/transformations/filter-sidebar-upgrade-by-license.php000064400000002404146731356320020021 0ustar00has_pro = Utils::has_pro(); } private function valid_item( $item ) { $has_pro_json_not_free = $this->has_pro && 'pro' === $item['license'][0]; $is_not_pro_json_not_pro = ! $this->has_pro && 'free' === $item['license'][0]; $should_show = ! isset( $item['show'] ) || 'true' === $item['show']; return $has_pro_json_not_free && $should_show || $is_not_pro_json_not_pro && $should_show; } public function transform( array $home_screen_data ): array { $new_sidebar_upgrade = []; foreach ( $home_screen_data['sidebar_upgrade'] as $index => $item ) { if ( $this->valid_item( $item ) ) { $new_sidebar_upgrade[] = $item; } } if ( empty( $new_sidebar_upgrade ) ) { unset( $home_screen_data['sidebar_upgrade'] ); return $home_screen_data; } $home_screen_data['sidebar_upgrade'] = reset( $new_sidebar_upgrade ); return $home_screen_data; } } home/transformations/create-new-page-url.php000064400000000756146731356320015224 0ustar00documents->get_create_new_post_url( 'page' ); return $home_screen_data; } } home/api.php000064400000003325146731356320006773 0ustar00run_transformations(); } private static function fetch_data(): array { $response = wp_remote_get( self::HOME_SCREEN_DATA_URL ); if ( is_wp_error( $response ) ) { return []; } $data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $data['home-screen'] ) || ! is_array( $data['home-screen'] ) ) { return []; } return $data['home-screen']; } private static function get_transient( $cache_key ) { $cache = get_option( $cache_key ); if ( empty( $cache['timeout'] ) ) { return false; } if ( current_time( 'timestamp' ) > $cache['timeout'] ) { return false; } return json_decode( $cache['value'], true ); } private static function set_transient( $cache_key, $value, $expiration = '+12 hours' ): bool { $data = [ 'timeout' => strtotime( $expiration, current_time( 'timestamp' ) ), 'value' => json_encode( $value ), ]; return update_option( $cache_key, $data, false ); } } home/classes/transformations-manager.php000064400000003644146731356320014524 0ustar00home_screen_data = $home_screen_data; $this->wordpress_adapter = new Wordpress_Adapter(); $this->plugin_status_adapter = new Plugin_Status_Adapter( $this->wordpress_adapter ); $this->transformation_classes = $this->get_transformation_classes(); } public function run_transformations(): array { if ( ! empty( self::$cached_data ) ) { return self::$cached_data; } $transformations = self::TRANSFORMATIONS; foreach ( $transformations as $transformation_id ) { $this->home_screen_data = $this->transformation_classes[ $transformation_id ]->transform( $this->home_screen_data ); } self::$cached_data = $this->home_screen_data; return $this->home_screen_data; } private function get_transformation_classes(): array { $classes = []; $transformations = self::TRANSFORMATIONS; $arguments = [ 'wordpress_adapter' => $this->wordpress_adapter, 'plugin_status_adapter' => $this->plugin_status_adapter, ]; foreach ( $transformations as $transformation_id ) { $class_name = '\\Elementor\\Modules\\Home\\Transformations\\' . $transformation_id; $classes[ $transformation_id ] = new $class_name( $arguments ); } return $classes; } } notifications/module.php000064400000004277146731356320011437 0ustar00get_js_assets_url( 'admin-notifications' ), [ 'elementor-v2-ui', 'elementor-v2-icons', 'elementor-v2-query', 'wp-i18n', ], ELEMENTOR_VERSION, true ); wp_localize_script( 'e-admin-notifications', 'elementorNotifications', $this->get_app_js_config() ); }, 5 /* Before Elementor's admin enqueue scripts */ ); add_action( 'elementor/editor/v2/scripts/enqueue', [ $this, 'enqueue_editor_scripts' ] ); add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_editor_scripts' ] ); add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); } public function enqueue_editor_scripts() { $deps = [ 'elementor-editor', 'elementor-v2-ui', 'elementor-v2-icons', 'elementor-v2-query', 'wp-i18n', ]; $is_editor_v2 = current_action() === 'elementor/editor/v2/scripts/enqueue'; if ( $is_editor_v2 ) { $deps[] = 'elementor-v2-editor-app-bar'; } wp_enqueue_script( 'e-editor-notifications', $this->get_js_assets_url( 'editor-notifications' ), $deps, ELEMENTOR_VERSION, true ); wp_localize_script( 'e-editor-notifications', 'elementorNotifications', $this->get_app_js_config() ); } private function get_app_js_config() : array { return [ 'is_unread' => Options::has_unread_notifications(), ]; } public function register_ajax_actions( $ajax ) { $ajax->register_ajax_action( 'notifications_get', [ $this, 'ajax_get_notifications' ] ); } public function ajax_get_notifications() { $notifications = API::get_notifications_by_conditions( true ); Options::mark_notification_read( $notifications ); return $notifications; } } notifications/api.php000064400000011606146731356320010715 0ustar00parent() ) { $theme = wp_get_theme()->parent(); } if ( $theme->get_template() === $condition['theme'] ) { $version = $theme->version; } else { $version = ''; } $result = version_compare( $version, $condition['version'], $condition['operator'] ); break; case 'introduction_meta': $result = User::get_introduction_meta( $condition['meta'] ); break; default: /** * Filters the notification condition, whether to check the group or not. * * The dynamic portion of the hook name, `$condition['type']`, refers to the condition type. * * @since 3.19.0 * * @param bool $result Whether to check the group. * @param array $condition Notification condition. */ $result = apply_filters( "elementor/notifications/condition/{$condition['type']}", $result, $condition ); break; } if ( ( $is_or_relation && $result ) || ( ! $is_or_relation && ! $result ) ) { return $result; } } return $result; } private static function get_transient( $cache_key ) { $cache = get_option( $cache_key ); if ( empty( $cache['timeout'] ) ) { return false; } if ( current_time( 'timestamp' ) > $cache['timeout'] ) { return false; } return json_decode( $cache['value'], true ); } private static function set_transient( $cache_key, $value, $expiration = '+12 hours' ) { $data = [ 'timeout' => strtotime( $expiration, current_time( 'timestamp' ) ), 'value' => json_encode( $value ), ]; return update_option( $cache_key, $data, false ); } } notifications/options.php000064400000003427146731356320011641 0ustar00ID}" ); if ( false === $unread_notifications ) { $notifications = API::get_notifications_by_conditions(); $notifications_ids = wp_list_pluck( $notifications, 'id' ); $unread_notifications = array_diff( $notifications_ids, static::get_notifications_dismissed() ); set_transient( "elementor_unread_notifications_{$current_user->ID}", $unread_notifications, HOUR_IN_SECONDS ); } return ! empty( $unread_notifications ); } public static function get_notifications_dismissed() { $current_user = wp_get_current_user(); if ( ! $current_user ) { return []; } $notifications_dismissed = get_user_meta( $current_user->ID, '_e_notifications_dismissed', true ); if ( ! is_array( $notifications_dismissed ) ) { $notifications_dismissed = []; } return $notifications_dismissed; } public static function mark_notification_read( $notifications ) : bool { $current_user = wp_get_current_user(); if ( ! $current_user ) { return false; } $notifications_dismissed = static::get_notifications_dismissed(); foreach ( $notifications as $notification ) { if ( ! in_array( $notification['id'], $notifications_dismissed, true ) ) { $notifications_dismissed[] = $notification['id']; } } $notifications_dismissed = array_unique( $notifications_dismissed ); update_user_meta( $current_user->ID, '_e_notifications_dismissed', $notifications_dismissed ); delete_transient( "elementor_unread_notifications_{$current_user->ID}" ); return true; } } shapes/module.php000064400000002521146731356320010037 0ustar00 esc_html__( 'Wave', 'elementor' ), 'arc' => esc_html__( 'Arc', 'elementor' ), 'circle' => esc_html__( 'Circle', 'elementor' ), 'line' => esc_html__( 'Line', 'elementor' ), 'oval' => esc_html__( 'Oval', 'elementor' ), 'spiral' => esc_html__( 'Spiral', 'elementor' ), ]; if ( $add_custom ) { $paths['custom'] = esc_html__( 'Custom', 'elementor' ); } return $paths; } /** * Get an SVG Path URL from the pre-defined ones. * * @param string $path - Path name. * * @return string */ public static function get_path_url( $path ) { return ELEMENTOR_ASSETS_URL . 'svg-paths/' . $path . '.svg'; } /** * Get the module's associated widgets. * * @return string[] */ protected function get_widgets() { return [ 'TextPath', ]; } /** * Retrieve the module name. * * @return string */ public function get_name() { return 'shapes'; } } shapes/widgets/text-path.php000064400000034403146731356320012142 0ustar00start_controls_section( 'section_content_text_path', [ 'label' => esc_html__( 'Text Path', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'text', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'label_block' => true, 'default' => esc_html__( 'Add Your Curvy Text Here', 'elementor' ), 'frontend_available' => true, 'render_type' => 'none', 'dynamic' => [ 'active' => true, ], ] ); $this->add_control( 'path', [ 'label' => esc_html__( 'Path Type', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => Shapes_Module::get_paths(), 'default' => 'wave', ] ); $this->add_control( 'custom_path', [ 'label' => esc_html__( 'SVG', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'media_types' => [ 'svg', ], 'condition' => [ 'path' => 'custom', ], 'dynamic' => [ 'active' => true, ], 'description' => sprintf( '%1$s %2$s', esc_html__( 'Want to create custom text paths with SVG?', 'elementor' ), esc_html__( 'Learn more', 'elementor' ) ), ] ); $this->add_control( 'link', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'label_block' => true, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Paste URL or type', 'elementor' ), 'frontend_available' => true, ] ); $this->add_responsive_control( 'align', [ 'label' => esc_html__( 'Alignment', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'default' => '', 'options' => [ 'left' => [ 'title' => esc_html__( 'Left', 'elementor' ), 'icon' => 'eicon-text-align-left', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-text-align-center', ], 'right' => [ 'title' => esc_html__( 'Right', 'elementor' ), 'icon' => 'eicon-text-align-right', ], ], 'selectors' => [ '{{WRAPPER}}' => '--alignment: {{VALUE}}', ], 'frontend_available' => true, ] ); $this->add_control( 'text_path_direction', [ 'label' => esc_html__( 'Text Direction', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => '', 'options' => [ '' => esc_html__( 'Default', 'elementor' ), 'rtl' => esc_html__( 'RTL', 'elementor' ), 'ltr' => esc_html__( 'LTR', 'elementor' ), ], 'selectors' => [ '{{WRAPPER}}' => '--direction: {{VALUE}}', ], 'frontend_available' => true, ] ); $this->add_control( 'show_path', [ 'label' => esc_html__( 'Show Path', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'On', 'elementor' ), 'label_off' => esc_html__( 'Off', 'elementor' ), 'return_value' => self::DEFAULT_PATH_FILL, 'separator' => 'before', 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--path-stroke: {{VALUE}}; --path-fill: transparent;', ], ] ); $this->end_controls_section(); } /** * Register style controls under style tab. */ protected function register_style_tab() { /** * Text Path styling section. */ $this->start_controls_section( 'section_style_text_path', [ 'label' => esc_html__( 'Text Path', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'range' => [ '%' => [ 'min' => 0, 'max' => 100, 'step' => 10, ], 'px' => [ 'max' => 800, 'step' => 50, ], ], 'default' => [ 'size' => 500, ], 'tablet_default' => [ 'size' => 500, ], 'mobile_default' => [ 'size' => 500, ], 'selectors' => [ '{{WRAPPER}}' => '--width: {{SIZE}}{{UNIT}};', ], ] ); $this->add_responsive_control( 'rotation', [ 'label' => esc_html__( 'Rotate', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'deg', 'grad', 'rad', 'turn', 'custom' ], 'default' => [ 'unit' => 'deg', ], 'tablet_default' => [ 'unit' => 'deg', ], 'mobile_default' => [ 'unit' => 'deg', ], 'selectors' => [ '{{WRAPPER}}' => '--rotate: {{SIZE}}{{UNIT}};', ], ] ); $this->add_control( 'text_heading', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'text_typography', 'selector' => '{{WRAPPER}}', 'global' => [ 'default' => Global_Typography::TYPOGRAPHY_TEXT, ], 'fields_options' => [ 'font_size' => [ 'default' => [ 'size' => '20', 'unit' => 'px', ], 'size_units' => [ 'px' ], ], // Text decoration isn't an inherited property, so it's required to explicitly // target the specific `textPath` element. 'text_decoration' => [ 'selectors' => [ '{{WRAPPER}} textPath' => 'text-decoration: {{VALUE}};', ], ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'text_stroke', 'selector' => '{{WRAPPER}} textPath', ] ); $this->add_responsive_control( 'word_spacing', [ 'label' => esc_html__( 'Word Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'min' => -20, 'max' => 20, ], 'em' => [ 'min' => -1, 'max' => 1, ], 'rem' => [ 'min' => -1, 'max' => 1, ], ], 'default' => [ 'size' => '', ], 'tablet_default' => [ 'size' => '', ], 'mobile_default' => [ 'size' => '', ], 'selectors' => [ '{{WRAPPER}}' => '--word-spacing: {{SIZE}}{{UNIT}};', ], ] ); $this->add_control( 'start_point', [ 'label' => esc_html__( 'Starting Point', 'elementor' ) . ' (%)', 'type' => Controls_Manager::SLIDER, 'size_units' => [ '%' ], 'range' => [ 'px' => [ 'min' => -100, 'max' => 100, 'step' => 1, ], ], 'default' => [ 'unit' => '%', 'size' => 0, ], 'frontend_available' => true, 'render_type' => 'none', ] ); $this->start_controls_tabs( 'text_style' ); /** * Normal tab. */ $this->start_controls_tab( 'text_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'text_color_normal', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--text-color: {{VALUE}};', ], ] ); $this->end_controls_tab(); /** * Hover tab. */ $this->start_controls_tab( 'text_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'text_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--text-color-hover: {{VALUE}};', ], ] ); $this->add_control( 'hover_animation', [ 'label' => esc_html__( 'Hover Animation', 'elementor' ), 'type' => Controls_Manager::HOVER_ANIMATION, ] ); $this->add_control( 'hover_transition', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 's', 'ms', 'custom' ], 'default' => [ 'unit' => 's', 'size' => 0.3, ], 'selectors' => [ '{{WRAPPER}}' => '--transition: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); /** * Path styling section. */ $this->start_controls_section( 'section_style_path', [ 'label' => esc_html__( 'Path', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, 'condition' => [ 'show_path!' => '', ], ] ); $this->start_controls_tabs( 'path_style' ); /** * Normal tab. */ $this->start_controls_tab( 'path_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'path_fill_normal', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--path-fill: {{VALUE}};', ], ] ); $this->add_control( 'stroke_heading_normal', [ 'label' => esc_html__( 'Stroke', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_control( 'stroke_color_normal', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => self::DEFAULT_PATH_FILL, 'selectors' => [ '{{WRAPPER}}' => '--stroke-color: {{VALUE}};', ], ] ); $this->add_control( 'stroke_width_normal', [ 'label' => esc_html__( 'Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'default' => [ 'size' => 1, ], 'range' => [ 'px' => [ 'min' => 1, 'max' => 20, ], 'em' => [ 'max' => 2, ], 'rem' => [ 'max' => 2, ], ], 'selectors' => [ '{{WRAPPER}}' => '--stroke-width: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); /** * Hover tab. */ $this->start_controls_tab( 'path_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'path_fill_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--path-fill-hover: {{VALUE}};', ], ] ); $this->add_control( 'stroke_heading_hover', [ 'label' => esc_html__( 'Stroke', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_control( 'stroke_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--stroke-color-hover: {{VALUE}};', ], ] ); $this->add_control( 'stroke_width_hover', [ 'label' => esc_html__( 'Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'default' => [ 'size' => '', ], 'range' => [ 'px' => [ 'min' => 1, 'max' => 20, ], 'em' => [ 'max' => 2, ], 'rem' => [ 'max' => 2, ], ], 'selectors' => [ '{{WRAPPER}}' => '--stroke-width-hover: {{SIZE}}{{UNIT}}', ], ] ); $this->add_control( 'stroke_transition', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 's', 'ms', 'custom' ], 'default' => [ 'unit' => 's', 'size' => 0.3, ], 'selectors' => [ '{{WRAPPER}}' => '--stroke-transition: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); } /** * Register Text Path widget controls. * * Adds different input fields to allow the user to change and customize the widget settings. * * @access protected */ protected function register_controls() { $this->register_content_tab(); $this->register_style_tab(); } /** * Render Text Path widget output on the frontend. * * Written in PHP and used to generate the final HTML. * * @access protected */ protected function render() { $settings = $this->get_settings_for_display(); // Get the path URL. $path_url = ( 'custom' === $settings['path'] ) ? wp_get_attachment_url( $settings['custom_path']['id'] ) : Shapes_Module::get_path_url( $settings['path'] ); // Remove the HTTP protocol to prevent Mixed Content error. $path_url = preg_replace( '/^https?:/i', '', $path_url ); // Add Text Path attributes. $this->add_render_attribute( 'text_path', [ 'class' => 'e-text-path', 'data-text' => htmlentities( esc_attr( $settings['text'] ) ), 'data-url' => esc_url( $path_url ), 'data-link-url' => esc_url( $settings['link']['url'] ?? '' ), ] ); // Add hover animation. if ( ! empty( $settings['hover_animation'] ) ) { $this->add_render_attribute( 'text_path', 'class', 'elementor-animation-' . $settings['hover_animation'] ); } // Render. ?>
print_render_attribute_string( 'text_path' ); ?>>
system_info_page = $system_info_page; } public function is_visible() { return true; } public function get_parent_slug() { return Settings::PAGE_ID; } public function get_label() { return esc_html__( 'System Info', 'elementor' ); } public function get_page_title() { return esc_html__( 'System Info', 'elementor' ); } public function get_capability() { return $this->system_info_page->get_capability(); } public function render() { $this->system_info_page->display_page(); } } system-info/module.php000064400000021706146731356320011037 0ustar00 [], 'wordpress' => [], 'theme' => [], 'user' => [], 'plugins' => [], 'network_plugins' => [], 'mu_plugins' => [], ]; public function get_capability() { return $this->capability; } /** * Main system info page constructor. * * Initializing Elementor system info page. * * @since 2.9.0 * @access public */ public function __construct() { $this->add_actions(); } /** * Get default settings. * * Retrieve the default settings. Used to reset the report settings on * initialization. * * @since 2.9.0 * @access protected * * @return array Default settings. */ protected function get_init_settings() { $settings = []; $reporter_properties = Base::get_properties_keys(); array_push( $reporter_properties, 'category', 'name', 'class_name' ); $settings['reporter_properties'] = $reporter_properties; $settings['reportFilePrefix'] = ''; return $settings; } /** * Add actions. * * Register filters and actions for the main system info page. * * @since 2.9.0 * @access private */ private function add_actions() { if ( ! Plugin::$instance->experiments->is_feature_active( 'admin_menu_rearrangement' ) ) { add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu_manager ) { $this->register_menu( $admin_menu_manager ); }, Settings::ADMIN_MENU_PRIORITY + 30 ); } add_action( 'wp_ajax_elementor_system_info_download_file', [ $this, 'download_file' ] ); } /** * Register admin menu. * * Add new Elementor system info admin menu. * * Fired by `admin_menu` action. * * @since 2.9.0 * @access private */ private function register_menu( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( 'elementor-system-info', new System_Info_Menu_Item( $this ) ); } /** * Display page. * * Output the content for the main system info page. * * @since 2.9.0 * @access public */ public function display_page() { $reports_info = self::get_allowed_reports(); $reports = $this->load_reports( $reports_info ); ?>

print_report( $reports, 'html' ); ?>


capability ) ) { wp_die( esc_html__( 'You do not have permission to download this file.', 'elementor' ) ); } $reports_info = self::get_allowed_reports(); $reports = $this->load_reports( $reports_info ); $domain = parse_url( site_url(), PHP_URL_HOST ); header( 'Content-Type: text/plain' ); header( 'Content-Disposition:attachment; filename=system-info-' . $domain . '-' . gmdate( 'd-m-Y' ) . '.txt' ); $this->print_report( $reports ); die; } /** * Get report class. * * Retrieve the class of the report for any given report type. * * @since 2.9.0 * @access public * * @param string $reporter_type The type of the report. * * @return string The class of the report. */ public function get_reporter_class( $reporter_type ) { return __NAMESPACE__ . '\Reporters\\' . ucfirst( $reporter_type ); } /** * Load reports. * * Retrieve the system info reports. * * @since 2.9.0 * @access public * * @param array $reports An array of system info reports. * * @return array An array of system info reports. */ public function load_reports( $reports ) { $result = []; foreach ( $reports as $report_name => $report_info ) { $reporter_params = [ 'name' => $report_name, ]; $reporter_params = array_merge( $reporter_params, $report_info ); $reporter = $this->create_reporter( $reporter_params ); if ( ! $reporter instanceof Base ) { continue; } $result[ $report_name ] = [ 'report' => $reporter, 'label' => $reporter->get_title(), ]; if ( ! empty( $report_info['sub'] ) ) { $result[ $report_name ]['sub'] = $this->load_reports( $report_info['sub'] ); } } return $result; } /** * Create a report. * * Register a new report that will be displayed in Elementor system info page. * * @param array $properties Report properties. * * @return \WP_Error|false|Base Base instance if the report was created, * False or WP_Error otherwise. *@since 2.9.0 * @access public * */ public function create_reporter( array $properties ) { $properties = Model_Helper::prepare_properties( $this->get_settings( 'reporter_properties' ), $properties ); $reporter_class = $properties['class_name'] ? $properties['class_name'] : $this->get_reporter_class( $properties['name'] ); $reporter = new $reporter_class( $properties ); if ( ! ( $reporter instanceof Base ) ) { return new \WP_Error( 'Each reporter must to be an instance or sub-instance of `Base` class.' ); } if ( ! $reporter->is_enabled() ) { return false; } return $reporter; } /** * Print report. * * Output the system info page reports using an output template. * * @since 2.9.0 * @access public * * @param array $reports An array of system info reports. * @param string $template Output type from the templates folder. Available * templates are `raw` and `html`. Default is `raw`. */ public function print_report( $reports, $template = 'raw' ) { static $tabs_count = 0; $template_path = __DIR__ . '/templates/' . $template . '.php'; require $template_path; } /** * Get allowed reports. * * Retrieve the available reports in Elementor system info page. * * @since 2.9.0 * @access public * @static * * @return array Available reports in Elementor system info page. */ public static function get_allowed_reports() { do_action( 'elementor/system_info/get_allowed_reports' ); return self::$reports; } /** * Add report. * * Register a new report to Elementor system info page. * * @since 2.9.0 * @access public * @static * * @param string $report_name The name of the report. * @param array $report_info Report info. */ public static function add_report( $report_name, $report_info ) { self::$reports[ $report_name ] = $report_info; } } system-info/reporters/theme.php000064400000012020146731356320012666 0ustar00 'Name', 'version' => 'Version', 'author' => 'Author', 'is_child_theme' => 'Child Theme', ]; if ( $this->get_parent_theme() ) { $parent_fields = [ 'parent_name' => 'Parent Theme Name', 'parent_version' => 'Parent Theme Version', 'parent_author' => 'Parent Theme Author', ]; $fields = array_merge( $fields, $parent_fields ); } return $fields; } /** * Get theme. * * Retrieve the theme. * * @since 1.0.0 * @deprecated 3.1.0 Use `get_theme()` method instead. * @access protected * * @return \WP_Theme WordPress theme object. */ protected function _get_theme() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', 'get_theme()' ); return $this->get_theme(); } /** * Get theme. * * Retrieve the theme. * * @since 3.1.0 * @access private * * @return \WP_Theme WordPress theme object. */ private function get_theme() { if ( is_null( $this->theme ) ) { $this->theme = wp_get_theme(); } return $this->theme; } /** * Get parent theme. * * Retrieve the parent theme. * * @since 1.0.0 * @access protected * * @return \WP_Theme|false WordPress theme object, or false if the current theme is not a child theme. */ protected function get_parent_theme() { return $this->get_theme()->parent(); } /** * Get theme name. * * Retrieve the theme name. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The theme name. * } */ public function get_name() { return [ 'value' => $this->get_theme()->get( 'Name' ), ]; } /** * Get theme author. * * Retrieve the theme author. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The theme author. * } */ public function get_author() { return [ 'value' => $this->get_theme()->get( 'Author' ), ]; } /** * Get theme version. * * Retrieve the theme version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The theme version. * } */ public function get_version() { return [ 'value' => $this->get_theme()->get( 'Version' ), ]; } /** * Is the theme is a child theme. * * Whether the theme is a child theme. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Yes if the theme is a child theme, No otherwise. * @type string $recommendation Theme source code modification recommendation. * } */ public function get_is_child_theme() { $is_child_theme = is_child_theme(); $result = [ 'value' => $is_child_theme ? 'Yes' : 'No', ]; if ( ! $is_child_theme ) { $result['recommendation'] = sprintf( /* translators: %s: WordPress child themes documentation. */ __( 'If you want to modify the source code of your theme, we recommend using a child theme.', 'elementor' ), 'https://go.elementor.com/wordpress-child-themes/' ); } return $result; } /** * Get parent theme version. * * Retrieve the parent theme version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The parent theme version. * } */ public function get_parent_version() { return [ 'value' => $this->get_parent_theme()->get( 'Version' ), ]; } /** * Get parent theme author. * * Retrieve the parent theme author. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The parent theme author. * } */ public function get_parent_author() { return [ 'value' => $this->get_parent_theme()->get( 'Author' ), ]; } /** * Get parent theme name. * * Retrieve the parent theme name. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The parent theme name. * } */ public function get_parent_name() { return [ 'value' => $this->get_parent_theme()->get( 'Name' ), ]; } } system-info/reporters/base.php000064400000012511146731356320012503 0ustar00get_report( 'html' ) as $field ) { $warning_class = ! empty( $field['warning'] ) ? ' class="elementor-warning"' : ''; $log_label = ! empty( $field['label'] ) ? $field['label'] . ':' : ''; ?> > get_report( 'raw' ); echo PHP_EOL . $indent . '== ' . $this->get_title() . ' =='; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo PHP_EOL; foreach ( $report as $field_name => $field ) : $sub_indent = str_repeat( "\t", $tabs_count ); $label = $field['label']; if ( ! empty( $label ) ) { $label .= ': '; } echo "{$sub_indent}{$label}{$field['value']}" . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped endforeach; } /** * Get report. * * Retrieve the report with all it's containing fields. * * @since 2.9.0 * @access public * * @return \WP_Error | array { * Report fields. * * @type string $name Field name. * @type string $label Field label. * } */ final public function get_report( $format = '' ) { $result = []; $format = ( empty( $format ) ) ? '' : $format . '_'; foreach ( $this->get_fields() as $field_name => $field_label ) { $method = 'get_' . $format . $field_name; if ( ! method_exists( $this, $method ) ) { $method = 'get_' . $field_name; //fallback: if ( ! method_exists( $this, $method ) ) { return new \WP_Error( sprintf( "Getter method for the field '%s' wasn't found in %s.", $field_name, get_called_class() ) ); } } $reporter_field = [ 'name' => $field_name, 'label' => $field_label, ]; $reporter_field = array_merge( $reporter_field, $this->$method() ); $result[ $field_name ] = $reporter_field; } return $result; } /** * Get properties keys. * * Retrieve the keys of the properties. * * @since 2.9.0 * @access public * @static * * @return array { * Property keys. * * @type string $name Property name. * @type string $fields Property fields. * } */ public static function get_properties_keys() { return [ 'name', 'format', 'fields', ]; } /** * Filter possible properties. * * Retrieve possible properties filtered by property keys. * * @since 2.9.0 * @access public * @static * * @param array $properties Properties to filter. * * @return array Possible properties filtered by property keys. */ final public static function filter_possible_properties( $properties ) { return Model_Helper::filter_possible_properties( self::get_properties_keys(), $properties ); } /** * Set properties. * * Add/update properties to the report. * * @since 2.9.0 * @access public * * @param array $key Property key. * @param array $value Optional. Property value. Default is `null`. */ final public function set_properties( $key, $value = null ) { if ( is_array( $key ) ) { $key = self::filter_possible_properties( $key ); foreach ( $key as $sub_key => $sub_value ) { $this->set_properties( $sub_key, $sub_value ); } return; } if ( ! in_array( $key, self::get_properties_keys(), true ) ) { return; } $this->_properties[ $key ] = $value; } /** * Reporter base constructor. * * Initializing the reporter base class. * * @since 2.9.0 * @access public * * @param array $properties Optional. Properties to filter. Default is `null`. */ public function __construct( $properties = null ) { $this->_properties = array_fill_keys( self::get_properties_keys(), null ); if ( $properties ) { $this->set_properties( $properties, null ); } } } system-info/reporters/server.php000064400000027505146731356320013110 0ustar00 'Operating System', 'software' => 'Software', 'mysql_version' => 'MySQL version', 'php_version' => 'PHP Version', 'php_memory_limit' => 'PHP Memory Limit', 'php_max_input_vars' => 'PHP Max Input Vars', 'php_max_post_size' => 'PHP Max Post Size', 'gd_installed' => 'GD Installed', 'zip_installed' => 'ZIP Installed', 'write_permissions' => 'Write Permissions', 'elementor_library' => 'Elementor Library', ]; } /** * Get server operating system. * * Retrieve the server operating system. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Server operating system. * } */ public function get_os() { return [ 'value' => PHP_OS, ]; } /** * Get server software. * * Retrieve the server software. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Server software. * } */ public function get_software() { return [ 'value' => Utils::get_super_global_value( $_SERVER, 'SERVER_SOFTWARE' ), ]; } /** * Get PHP version. * * Retrieve the PHP version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value PHP version. * @type string $recommendation Minimum PHP version recommendation. * @type bool $warning Whether to display a warning. * } */ public function get_php_version() { $result = [ 'value' => PHP_VERSION, ]; $recommended_php_version = '7.4'; if ( version_compare( $result['value'], $recommended_php_version, '<' ) ) { $result['recommendation'] = sprintf( /* translators: %s: Recommended PHP version. */ esc_html__( 'We recommend using PHP version %s or higher.', 'elementor' ), $recommended_php_version ); $result['warning'] = true; } return $result; } /** * Get PHP memory limit. * * Retrieve the PHP memory limit. * * @return array { * Report data. * * @type string $value PHP memory limit. * @type string $recommendation Recommendation memory limit. * @type bool $warning Whether to display a warning. True if the limit * is below the recommended 128M, False otherwise. * } */ public function get_php_memory_limit() { $result = [ 'value' => (string) ini_get( 'memory_limit' ), ]; $min_recommended_memory = '128M'; $preferred_memory = '256M'; $memory_limit_bytes = wp_convert_hr_to_bytes( $result['value'] ); $min_recommended_bytes = wp_convert_hr_to_bytes( $min_recommended_memory ); if ( $memory_limit_bytes < $min_recommended_bytes ) { $result['recommendation'] = sprintf( /* translators: 1: Minimum recommended_memory, 2: Preferred memory, 3: WordPress wp-config memory documentation. */ __( 'We recommend setting memory to at least %1$s. (%2$s or higher is preferred) For more information, read about how to increase memory allocated to PHP.', 'elementor' ), $min_recommended_memory, $preferred_memory, 'https://go.elementor.com/wordpress-wp-config-memory/' ); $result['warning'] = true; } return $result; } /** * Get PHP `max_input_vars`. * * Retrieve the value of `max_input_vars` from `php.ini` configuration file. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value PHP `max_input_vars`. * } */ public function get_php_max_input_vars() { return [ 'value' => ini_get( 'max_input_vars' ), ]; } /** * Get PHP `post_max_size`. * * Retrieve the value of `post_max_size` from `php.ini` configuration file. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value PHP `post_max_size`. * } */ public function get_php_max_post_size() { return [ 'value' => ini_get( 'post_max_size' ), ]; } /** * Get GD installed. * * Whether the GD extension is installed. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Yes if the GD extension is installed, No otherwise. * @type bool $warning Whether to display a warning. True if the GD extension is installed, False otherwise. * } */ public function get_gd_installed() { $gd_installed = extension_loaded( 'gd' ); return [ 'value' => $gd_installed ? 'Yes' : 'No', 'warning' => ! $gd_installed, ]; } /** * Get ZIP installed. * * Whether the ZIP extension is installed. * * @since 2.1.0 * @access public * * @return array { * Report data. * * @type string $value Yes if the ZIP extension is installed, No otherwise. * @type bool $warning Whether to display a warning. True if the ZIP extension is installed, False otherwise. * } */ public function get_zip_installed() { $zip_installed = extension_loaded( 'zip' ); return [ 'value' => $zip_installed ? 'Yes' : 'No', 'warning' => ! $zip_installed, ]; } /** * Get MySQL version. * * Retrieve the MySQL version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value MySQL version. * } */ public function get_mysql_version() { global $wpdb; $db_server_version = $wpdb->get_results( "SHOW VARIABLES WHERE `Variable_name` IN ( 'version_comment', 'innodb_version' )", OBJECT_K ); $db_server_version_string = $db_server_version['version_comment']->Value . ' v'; // On some hosts, `innodb_version` is empty, in PHP 8.1. if ( isset( $db_server_version['innodb_version'] ) ) { $db_server_version_string .= $db_server_version['innodb_version']->Value; } else { $db_server_version_string .= $wpdb->get_var( 'SELECT VERSION() AS version' ); } return [ 'value' => $db_server_version_string, ]; } /** * Get write permissions. * Check whether the required paths for have writing permissions. * * @since 1.9.0 * @access public * * @return array { * Report data. * * @type string $value Writing permissions status. * @type bool $warning Whether to display a warning. True if some required * folders don't have writing permissions, False otherwise. * } */ public function get_write_permissions() : array { $paths_to_check = [ static::KEY_PATH_WP_ROOT_DIR => $this->get_system_path( static::KEY_PATH_WP_ROOT_DIR ), static::KEY_PATH_HTACCESS_FILE => $this->get_system_path( static::KEY_PATH_HTACCESS_FILE ), static::KEY_PATH_UPLOADS_DIR => $this->get_system_path( static::KEY_PATH_UPLOADS_DIR ), static::KEY_PATH_ELEMENTOR_UPLOADS_DIR => $this->get_system_path( static::KEY_PATH_ELEMENTOR_UPLOADS_DIR ), ]; $paths_permissions = $this->get_paths_permissions( $paths_to_check ); $write_problems = []; if ( ! $paths_permissions[ static::KEY_PATH_WP_ROOT_DIR ]['write'] ) { $write_problems[] = 'WordPress root directory'; } if ( ! $paths_permissions[ static::KEY_PATH_UPLOADS_DIR ]['write'] ) { $write_problems[] = 'WordPress uploads directory'; } if ( $paths_permissions[ self::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['exists'] && ! $paths_permissions[ self::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['write'] ) { $write_problems[] = 'Elementor uploads directory'; } if ( $paths_permissions[ self::KEY_PATH_HTACCESS_FILE ]['exists'] && ! $paths_permissions[ self::KEY_PATH_HTACCESS_FILE ]['write'] ) { $write_problems[] = '.htaccess file'; } if ( $write_problems ) { $value = 'There are some writing permissions issues with the following directories/files:' . "\n\t\t - "; $value .= implode( "\n\t\t - ", $write_problems ); } else { $value = 'All right'; } return [ 'value' => $value, 'warning' => ! ! $write_problems, ]; } /** * Check for elementor library connectivity. * * Check whether the remote elementor library is reachable. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The status of elementor library connectivity. * @type bool $warning Whether to display a warning. True if elementor * * library is not reachable, False otherwise. * } */ public function get_elementor_library() { $response = wp_remote_get( Api::$api_info_url, [ 'timeout' => 5, 'body' => [ // Which API version is used 'api_version' => ELEMENTOR_VERSION, // Which language to return 'site_lang' => get_bloginfo( 'language' ), ], ] ); if ( is_wp_error( $response ) ) { return [ 'value' => 'Not connected (' . $response->get_error_message() . ')', 'warning' => true, ]; } $http_response_code = wp_remote_retrieve_response_code( $response ); if ( 200 !== (int) $http_response_code ) { $error_msg = 'HTTP Error (' . $http_response_code . ')'; return [ 'value' => 'Not connected (' . $error_msg . ')', 'warning' => true, ]; } $info_data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $info_data ) ) { return [ 'value' => 'Not connected (Returns invalid JSON)', 'warning' => true, ]; } return [ 'value' => 'Connected', ]; } /** * @param $paths [] Paths to check permissions. * @return array []{exists: bool, read: bool, write: bool, execute: bool} */ public function get_paths_permissions( $paths ) : array { $permissions = []; foreach ( $paths as $key_path => $path ) { $permissions[ $key_path ] = $this->get_path_permissions( $path ); } return $permissions; } /** * Get path by path key. * * @param $path_key * @return string */ public function get_system_path( $path_key ) : string { switch ( $path_key ) { case static::KEY_PATH_WP_ROOT_DIR: return ABSPATH; case static::KEY_PATH_WP_CONTENT_DIR: return WP_CONTENT_DIR; case static::KEY_PATH_HTACCESS_FILE: return file_exists( ABSPATH . '/.htaccess' ) ? ABSPATH . '/.htaccess' : ''; case static::KEY_PATH_UPLOADS_DIR: return wp_upload_dir()['basedir'] ?? ''; case static::KEY_PATH_ELEMENTOR_UPLOADS_DIR: if ( empty( wp_upload_dir()['basedir'] ) ) { return ''; } $elementor_uploads_dir = wp_upload_dir()['basedir'] . '/elementor'; return is_dir( $elementor_uploads_dir ) ? $elementor_uploads_dir : ''; default: return ''; } } /** * Check the permissions of a path. * * @param $path * @return array{exists: bool, read: bool, write: bool, execute: bool} */ public function get_path_permissions( $path ) : array { if ( empty( $path ) ) { return [ 'exists' => false, 'read' => false, 'write' => false, 'execute' => false, ]; } return [ 'exists' => true, 'read' => is_readable( $path ), 'write' => is_writeable( $path ), 'execute' => is_executable( $path ), ]; } } system-info/reporters/wordpress.php000064400000012544146731356320013627 0ustar00 'Version', 'site_url' => 'Site URL', 'home_url' => 'Home URL', 'is_multisite' => 'WP Multisite', 'max_upload_size' => 'Max Upload Size', 'memory_limit' => 'Memory limit', 'max_memory_limit' => 'Max Memory limit', 'permalink_structure' => 'Permalink Structure', 'language' => 'Language', 'timezone' => 'Timezone', 'admin_email' => 'Admin Email', 'debug_mode' => 'Debug Mode', ]; } /** * Get WordPress memory limit. * * Retrieve the WordPress memory limit. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress memory limit. * } */ public function get_memory_limit() { return [ 'value' => (string) WP_MEMORY_LIMIT, ]; } /** * Get WordPress max memory limit. * * Retrieve the WordPress max memory limit. * * @return array { * Report data. * * @type string $value WordPress max memory limit. * } */ public function get_max_memory_limit() { return [ 'value' => (string) WP_MAX_MEMORY_LIMIT, ]; } /** * Get WordPress version. * * Retrieve the WordPress version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress version. * } */ public function get_version() { return [ 'value' => get_bloginfo( 'version' ), ]; } /** * Is multisite. * * Whether multisite is enabled or not. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Yes if multisite is enabled, No otherwise. * } */ public function get_is_multisite() { return [ 'value' => is_multisite() ? 'Yes' : 'No', ]; } /** * Get site URL. * * Retrieve WordPress site URL. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress site URL. * } */ public function get_site_url() { return [ 'value' => get_site_url(), ]; } /** * Get home URL. * * Retrieve WordPress home URL. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress home URL. * } */ public function get_home_url() { return [ 'value' => get_home_url(), ]; } /** * Get permalink structure. * * Retrieve the permalink structure * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress permalink structure. * } */ public function get_permalink_structure() { global $wp_rewrite; $structure = $wp_rewrite->permalink_structure; if ( ! $structure ) { $structure = 'Plain'; } return [ 'value' => $structure, ]; } /** * Get site language. * * Retrieve the site language. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress site language. * } */ public function get_language() { return [ 'value' => get_locale(), ]; } /** * Get PHP `max_upload_size`. * * Retrieve the value of maximum upload file size defined in `php.ini` configuration file. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Maximum upload file size allowed. * } */ public function get_max_upload_size() { return [ 'value' => size_format( wp_max_upload_size() ), ]; } /** * Get WordPress timezone. * * Retrieve WordPress timezone. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress timezone. * } */ public function get_timezone() { $timezone = get_option( 'timezone_string' ); if ( ! $timezone ) { $timezone = get_option( 'gmt_offset' ); } return [ 'value' => $timezone, ]; } /** * Get WordPress administrator email. * * Retrieve WordPress administrator email. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress administrator email. * } */ public function get_admin_email() { return [ 'value' => get_option( 'admin_email' ), ]; } /** * Get debug mode. * * Whether WordPress debug mode is enabled or not. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Active if debug mode is enabled, Inactive otherwise. * } */ public function get_debug_mode() { return [ 'value' => WP_DEBUG ? 'Active' : 'Inactive', ]; } } system-info/reporters/network-plugins.php000064400000004273146731356320014747 0ustar00plugins ) { $active_plugins = get_site_option( 'active_sitewide_plugins' ); $this->plugins = array_intersect_key( get_plugins(), $active_plugins ); } return $this->plugins; } /** * Is enabled. * * Whether there are active network plugins or not. * * @since 1.0.0 * @access public * * @return bool True if the site has active network plugins, False otherwise. */ public function is_enabled() { if ( ! is_multisite() ) { return false; }; return ! ! $this->get_network_plugins(); } /** * Get network plugins report fields. * * Retrieve the required fields for the network plugins report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'network_active_plugins' => 'Network Plugins', ]; } /** * Get active network plugins. * * Retrieve the sites active network plugins. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The active network plugins list. * } */ public function get_network_active_plugins() { return [ 'value' => $this->get_network_plugins(), ]; } } system-info/reporters/mu-plugins.php000064400000003660146731356320013676 0ustar00plugins ) { $this->plugins = get_mu_plugins(); } return $this->plugins; } /** * Is enabled. * * Whether there are must-use plugins or not. * * @since 1.0.0 * @access public * * @return bool True if the site has must-use plugins, False otherwise. */ public function is_enabled() { return ! ! $this->get_mu_plugins(); } /** * Get must-use plugins reporter title. * * Retrieve must-use plugins reporter title. * * @since 1.0.0 * @access public * * @return string Reporter title. */ public function get_title() { return 'Must-Use Plugins'; } /** * Get must-use plugins report fields. * * Retrieve the required fields for the must-use plugins report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'must_use_plugins' => 'Must-Use Plugins', ]; } /** * Get must-use plugins. * * Retrieve the sites must-use plugins. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The must-use plugins list. * } */ public function get_must_use_plugins() { return [ 'value' => $this->get_mu_plugins(), ]; } } system-info/reporters/user.php000064400000003761146731356320012556 0ustar00ID; } /** * Get user reporter title. * * Retrieve user reporter title. * * @since 1.0.0 * @access public * * @return string Reporter title. */ public function get_title() { return 'User'; } /** * Get user report fields. * * Retrieve the required fields for the user report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'role' => 'Role', 'locale' => 'WP Profile lang', 'agent' => 'User Agent', ]; } /** * Get user role. * * Retrieve the user role. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The user role. * } */ public function get_role() { $role = null; $current_user = wp_get_current_user(); if ( ! empty( $current_user->roles ) ) { $role = $current_user->roles[0]; } return [ 'value' => $role, ]; } /** * Get user profile language. * * Retrieve the user profile language. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value User profile language. * } */ public function get_locale() { return [ 'value' => get_bloginfo( 'language' ), ]; } /** * Get user agent. * * Retrieve user agent. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value HTTP user agent. * } */ public function get_agent() { return [ 'value' => esc_html( Utils::get_super_global_value( $_SERVER, 'HTTP_USER_AGENT' ) ), ]; } } system-info/reporters/plugins.php000064400000003740146731356320013256 0ustar00plugins ) { $this->plugins = Plugin::$instance->wp->get_active_plugins()->all(); } return $this->plugins; } /** * Get active plugins reporter title. * * Retrieve active plugins reporter title. * * @since 1.0.0 * @access public * * @return string Reporter title. */ public function get_title() { return 'Active Plugins'; } /** * Is enabled. * * Whether there are active plugins or not. * * @since 1.0.0 * @access public * * @return bool True if the site has active plugins, False otherwise. */ public function is_enabled() { return ! ! $this->get_plugins(); } /** * Get active plugins report fields. * * Retrieve the required fields for the active plugins report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'active_plugins' => 'Active Plugins', ]; } /** * Get active plugins. * * Retrieve the sites active plugins. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The active plugins list. * } */ public function get_active_plugins() { return [ 'value' => $this->get_plugins(), ]; } } system-info/reporters/base-plugin.php000064400000004201146731356320013774 0ustar00get_report( 'html' ) as $field ) { foreach ( $field['value'] as $plugin_info ) : ?> %s', $plugin_info['PluginURI'], $plugin_info['Name'] ); else : $plugin_name = $plugin_info['Name']; endif; if ( $plugin_info['Version'] ) : $plugin_name .= ' - ' . $plugin_info['Version']; endif; Utils::print_unescaped_internal_string( $plugin_name ); ?> %s', $plugin_info['AuthorURI'], $plugin_info['Author'] ); else : $author = $plugin_info['Author']; endif; Utils::print_unescaped_internal_string( "By $author" ); endif; ?> get_report( 'raw' ) as $field_name => $field ) : $sub_indent = str_repeat( "\t", $tabs_count ); echo "== {$field['label']} ==" . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped foreach ( $field['value'] as $plugin_info ) : $plugin_properties = array_intersect_key( $plugin_info, $required_plugins_properties ); echo esc_html( $sub_indent . $plugin_info['Name'] ); foreach ( $plugin_properties as $property_name => $property ) : echo PHP_EOL . "{$sub_indent}\t{$property_name}: {$property}"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped endforeach; echo PHP_EOL . PHP_EOL; endforeach; endforeach; } } system-info/helpers/model-helper.php000064400000003312146731356320013562 0ustar00 $report ) : ?>
print_html(); ?>
print_html_label( ( $report['label'] ) ); ?>
$report ) : $report['report']->print_raw( $tabs_count ); if ( ! empty( $report['sub'] ) ) : $this->print_report( $report['sub'], $template, true ); endif; endforeach; $tabs_count--; usage/module.php000064400000041457146731356320007673 0ustar00 $elements ) { $doc_class = Plugin::$instance->documents->get_document_type( $doc_type ); if ( 'html' === $format && $doc_class ) { $doc_title = $doc_class::get_title(); } else { $doc_title = $doc_type; } $doc_count = $this->get_doc_type_count( $doc_class, $doc_type ); $tab_group = $doc_class::get_property( 'admin_tab_group' ); if ( 'html' === $format && $tab_group ) { $doc_title = ucwords( $tab_group ) . ' - ' . $doc_title; } // Replace element type with element title. foreach ( $elements as $element_type => $data ) { unset( $elements[ $element_type ] ); if ( in_array( $element_type, [ 'section', 'column' ], true ) ) { continue; } $widget_instance = Plugin::$instance->widgets_manager->get_widget_types( $element_type ); if ( 'html' === $format && $widget_instance ) { $widget_title = $widget_instance->get_title(); } else { $widget_title = $element_type; } $elements[ $widget_title ] = $data['count']; } // Sort elements by key. ksort( $elements ); $usage[ $doc_type ] = [ 'title' => $doc_title, 'elements' => $elements, 'count' => $doc_count, ]; // ' ? 1 : 0;' In sorters is compatibility for PHP8.0. // Sort usage by title. uasort( $usage, function( $a, $b ) { return ( $a['title'] > $b['title'] ) ? 1 : 0; } ); // If title includes '-' will have lower priority. uasort( $usage, function( $a ) { return strpos( $a['title'], '-' ) ? 1 : 0; } ); } return $usage; } /** * Before document Save. * * Called on elementor/document/before_save, remove document from global & set saving flag. * * @param Document $document * @param array $data new settings to save. */ public function before_document_save( $document, $data ) { $current_status = get_post_status( $document->get_post() ); $new_status = isset( $data['settings']['post_status'] ) ? $data['settings']['post_status'] : ''; if ( $current_status === $new_status ) { $this->remove_from_global( $document ); } $this->is_document_saving = true; } /** * After document save. * * Called on elementor/document/after_save, adds document to global & clear saving flag. * * @param Document $document */ public function after_document_save( $document ) { if ( Document::STATUS_PUBLISH === $document->get_post()->post_status || Document::STATUS_PRIVATE === $document->get_post()->post_status ) { $this->save_document_usage( $document ); } $this->is_document_saving = false; } /** * On status change. * * Called on transition_post_status. * * @param string $new_status * @param string $old_status * @param \WP_Post $post */ public function on_status_change( $new_status, $old_status, $post ) { if ( wp_is_post_autosave( $post ) ) { return; } // If it's from elementor editor, the usage should be saved via `before_document_save`/`after_document_save`. if ( $this->is_document_saving ) { return; } $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document ) { return; } $is_public_unpublish = 'publish' === $old_status && 'publish' !== $new_status; $is_private_unpublish = 'private' === $old_status && 'private' !== $new_status; if ( $is_public_unpublish || $is_private_unpublish ) { $this->remove_from_global( $document ); } $is_public_publish = 'publish' !== $old_status && 'publish' === $new_status; $is_private_publish = 'private' !== $old_status && 'private' === $new_status; if ( $is_public_publish || $is_private_publish ) { $this->save_document_usage( $document ); } } /** * On before delete post. * * Called on on_before_delete_post. * * @param int $post_id */ public function on_before_delete_post( $post_id ) { $document = Plugin::$instance->documents->get( $post_id ); if ( $document->get_id() !== $document->get_main_id() ) { return; } $this->remove_from_global( $document ); } /** * Add's tracking data. * * Called on elementor/tracker/send_tracking_data_params. * * @param array $params * * @return array */ public function add_tracking_data( $params ) { $params['usages']['elements'] = get_option( self::OPTION_NAME ); return $params; } /** * Recalculate usage. * * Recalculate usage for all elementor posts. * * @param int $limit * @param int $offset * * @return int */ public function recalc_usage( $limit = -1, $offset = 0 ) { // While requesting recalc_usage, data should be deleted. // if its in a batch the data should be deleted only on the first batch. if ( 0 === $offset ) { delete_option( self::OPTION_NAME ); } $post_types = get_post_types( array( 'public' => true ) ); $query = new \WP_Query( [ 'no_found_rows' => true, 'meta_key' => '_elementor_data', 'post_type' => $post_types, 'post_status' => [ 'publish', 'private' ], 'posts_per_page' => $limit, 'offset' => $offset, ] ); foreach ( $query->posts as $post ) { $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document ) { continue; } $this->after_document_save( $document ); } // Clear query memory before leave. wp_cache_flush(); return count( $query->posts ); } /** * Increase controls count. * * Increase controls count, for each element. * * @param array &$element_ref * @param string $tab * @param string $section * @param string $control * @param int $count */ private function increase_controls_count( &$element_ref, $tab, $section, $control, $count ) { if ( ! isset( $element_ref['controls'][ $tab ] ) ) { $element_ref['controls'][ $tab ] = []; } if ( ! isset( $element_ref['controls'][ $tab ][ $section ] ) ) { $element_ref['controls'][ $tab ][ $section ] = []; } if ( ! isset( $element_ref['controls'][ $tab ][ $section ][ $control ] ) ) { $element_ref['controls'][ $tab ][ $section ][ $control ] = 0; } $element_ref['controls'][ $tab ][ $section ][ $control ] += $count; } /** * Add Controls * * Add's controls to this element_ref, returns changed controls count. * * @param array $settings_controls * @param array $element_controls * @param array &$element_ref * * @return int ($changed_controls_count). */ private function add_controls( $settings_controls, $element_controls, &$element_ref ) { $changed_controls_count = 0; // Loop over all element settings. foreach ( $settings_controls as $control => $value ) { if ( empty( $element_controls[ $control ] ) ) { continue; } $control_config = $element_controls[ $control ]; if ( ! isset( $control_config['section'], $control_config['default'] ) ) { continue; } $tab = $control_config['tab']; $section = $control_config['section']; // If setting value is not the control default. if ( $value !== $control_config['default'] ) { $this->increase_controls_count( $element_ref, $tab, $section, $control, 1 ); $changed_controls_count++; } } return $changed_controls_count; } /** * Add general controls. * * Extract general controls to element ref, return clean `$settings_control`. * * @param array $settings_controls * @param array &$element_ref * * @return array ($settings_controls). */ private function add_general_controls( $settings_controls, &$element_ref ) { if ( ! empty( $settings_controls[ Manager::DYNAMIC_SETTING_KEY ] ) ) { $settings_controls = array_merge( $settings_controls, $settings_controls[ Manager::DYNAMIC_SETTING_KEY ] ); // Add dynamic count to controls under `general` tab. $this->increase_controls_count( $element_ref, self::GENERAL_TAB, Manager::DYNAMIC_SETTING_KEY, 'count', count( $settings_controls[ Manager::DYNAMIC_SETTING_KEY ] ) ); } return $settings_controls; } /** * Add to global. * * Add's usage to global (update database). * * @param string $doc_name * @param array $doc_usage */ private function add_to_global( $doc_name, $doc_usage ) { $global_usage = get_option( self::OPTION_NAME, [] ); foreach ( $doc_usage as $element_type => $element_data ) { if ( ! isset( $global_usage[ $doc_name ] ) ) { $global_usage[ $doc_name ] = []; } if ( ! isset( $global_usage[ $doc_name ][ $element_type ] ) ) { $global_usage[ $doc_name ][ $element_type ] = [ 'count' => 0, 'controls' => [], ]; } $global_element_ref = &$global_usage[ $doc_name ][ $element_type ]; $global_element_ref['count'] += $element_data['count']; if ( empty( $element_data['controls'] ) ) { continue; } foreach ( $element_data['controls'] as $tab => $sections ) { foreach ( $sections as $section => $controls ) { foreach ( $controls as $control => $count ) { $this->increase_controls_count( $global_element_ref, $tab, $section, $control, $count ); } } } } update_option( self::OPTION_NAME, $global_usage, false ); } /** * Remove from global. * * Remove's usage from global (update database). * * @param Document $document */ private function remove_from_global( $document ) { $prev_usage = $document->get_meta( self::META_KEY ); if ( empty( $prev_usage ) ) { return; } $doc_name = $document->get_name(); $global_usage = get_option( self::OPTION_NAME, [] ); foreach ( $prev_usage as $element_type => $doc_value ) { if ( isset( $global_usage[ $doc_name ][ $element_type ]['count'] ) ) { $global_usage[ $doc_name ][ $element_type ]['count'] -= $prev_usage[ $element_type ]['count']; if ( 0 === $global_usage[ $doc_name ][ $element_type ]['count'] ) { unset( $global_usage[ $doc_name ][ $element_type ] ); if ( 0 === count( $global_usage[ $doc_name ] ) ) { unset( $global_usage[ $doc_name ] ); } continue; } foreach ( $prev_usage[ $element_type ]['controls'] as $tab => $sections ) { foreach ( $sections as $section => $controls ) { foreach ( $controls as $control => $count ) { if ( isset( $global_usage[ $doc_name ][ $element_type ]['controls'][ $tab ][ $section ][ $control ] ) ) { $section_ref = &$global_usage[ $doc_name ][ $element_type ]['controls'][ $tab ][ $section ]; $section_ref[ $control ] -= $count; if ( 0 === $section_ref[ $control ] ) { unset( $section_ref[ $control ] ); } } } } } } } update_option( self::OPTION_NAME, $global_usage, false ); $document->delete_meta( self::META_KEY ); } /** * Get elements usage. * * Get's the current elements usage by passed elements array parameter. * * @param array $elements * * @return array */ private function get_elements_usage( $elements ) { $usage = []; Plugin::$instance->db->iterate_data( $elements, function ( $element ) use ( &$usage ) { if ( empty( $element['widgetType'] ) ) { $type = $element['elType']; $element_instance = Plugin::$instance->elements_manager->get_element_types( $type ); } else { $type = $element['widgetType']; $element_instance = Plugin::$instance->widgets_manager->get_widget_types( $type ); } if ( ! isset( $usage[ $type ] ) ) { $usage[ $type ] = [ 'count' => 0, 'control_percent' => 0, 'controls' => [], ]; } $usage[ $type ]['count']++; if ( ! $element_instance ) { return $element; } $element_controls = $element_instance->get_controls(); if ( isset( $element['settings'] ) ) { $settings_controls = $element['settings']; $element_ref = &$usage[ $type ]; // Add dynamic values. $settings_controls = $this->add_general_controls( $settings_controls, $element_ref ); $changed_controls_count = $this->add_controls( $settings_controls, $element_controls, $element_ref ); $percent = $changed_controls_count / ( count( $element_controls ) / 100 ); $usage[ $type ] ['control_percent'] = (int) round( $percent ); } return $element; } ); return $usage; } /** * Save document usage. * * Save requested document usage, and update global. * * @param Document $document */ private function save_document_usage( Document $document ) { if ( ! $document::get_property( 'is_editable' ) && ! $document->is_built_with_elementor() ) { return; } // Get data manually to avoid conflict with `\Elementor\Core\Base\Document::get_elements_data... convert_to_elementor`. $data = $document->get_json_meta( '_elementor_data' ); if ( ! empty( $data ) ) { try { $usage = $this->get_elements_usage( $document->get_elements_raw_data( $data ) ); $document->update_meta( self::META_KEY, $usage ); $this->add_to_global( $document->get_name(), $usage ); } catch ( \Exception $exception ) { Plugin::$instance->logger->get_logger()->error( $exception->getMessage(), [ 'document_id' => $document->get_id(), 'document_name' => $document->get_name(), ] ); return; }; } } public static function get_settings_usage() { $usage = []; $settings_tab = Plugin::$instance->settings->get_tabs(); $settings = array_merge( $settings_tab[ Settings::TAB_GENERAL ]['sections'], $settings_tab[ Settings::TAB_ADVANCED ]['sections'] ); foreach ( $settings as $setting_data ) { foreach ( $setting_data['fields'] as $field_name => $field_data ) { $is_hidden_field = ( empty( $field_data['field_args']['type'] ) || 'hidden' === $field_data['field_args']['type'] ); if ( $is_hidden_field ) { continue; } $setting_value = get_option( 'elementor_' . $field_name ); if ( empty( $setting_value ) ) { continue; } $is_default_value = ( ! empty( $field_data['field_args']['std'] ) && $setting_value === $field_data['field_args']['std'] ); if ( $is_default_value ) { continue; } $usage[ $field_name ] = $setting_value; } } $usage = apply_filters( 'elementor/system-info/usage/settings', $usage ); return $usage; } /** * Add system info report. */ public function add_system_info_report() { System_Info::add_report( 'usage', [ 'file_name' => __DIR__ . '/usage-reporter.php', 'class_name' => __NAMESPACE__ . '\Usage_Reporter', ] ); System_Info::add_report( 'settings', [ 'file_name' => __DIR__ . '/settings-reporter.php', 'class_name' => __NAMESPACE__ . '\Settings_Reporter', ] ); } /** * Usage module constructor. * * Initializing Elementor usage module. * * @access public */ public function __construct() { if ( ! Tracker::is_allow_track() ) { return; } add_action( 'transition_post_status', [ $this, 'on_status_change' ], 10, 3 ); add_action( 'before_delete_post', [ $this, 'on_before_delete_post' ] ); add_action( 'elementor/document/before_save', [ $this, 'before_document_save' ], 10, 2 ); add_action( 'elementor/document/after_save', [ $this, 'after_document_save' ] ); add_filter( 'elementor/tracker/send_tracking_data_params', [ $this, 'add_tracking_data' ] ); add_action( 'admin_init', [ $this, 'add_system_info_report' ], 50 ); } } usage/settings-reporter.php000064400000002360146731356320012074 0ustar00 '', ]; } public function get_settings() : array { $usage_settings_text = ''; $settings = Module::get_settings_usage(); foreach ( $settings as $setting_name => $setting_value ) { $setting_value_text = is_array( $setting_value ) ? implode( ', ', $setting_value ) : $setting_value; $usage_settings_text .= '' . $setting_name . '' . $setting_value_text . ''; } return [ 'value' => $usage_settings_text, ]; } public function get_raw_settings() : array { $usage_settings = PHP_EOL; $settings = Module::get_settings_usage(); foreach ( $settings as $setting_name => $setting_value ) { $setting_value_text = is_array( $setting_value ) ? implode( ', ', $setting_value ) : $setting_value; $usage_settings .= "\t" . $setting_name . ': ' . $setting_value_text . PHP_EOL; } return [ 'value' => $usage_settings, ]; } } usage/usage-reporter.php000064400000006011146731356320011335 0ustar00 '', ]; } public function print_html_label( $label ) { $title = $this->get_title(); if ( empty( $_GET[ self::RECALC_ACTION ] ) ) { // phpcs:ignore -- nonce validation is not required here. $nonce = wp_create_nonce( self::RECALC_ACTION ); $url = add_query_arg( [ self::RECALC_ACTION => 1, '_wpnonce' => $nonce, ] ); $title .= 'Recalculate'; } else { $title .= $this->get_remove_recalc_query_string_script(); } parent::print_html_label( $title ); } public function get_usage() { /** @var Module $module */ $module = Module::instance(); if ( ! empty( $_GET[ self::RECALC_ACTION ] ) ) { // phpcs:ignore $nonce = Utils::get_super_global_value( $_GET, '_wpnonce' ); if ( ! wp_verify_nonce( $nonce, self::RECALC_ACTION ) ) { wp_die( 'Invalid Nonce', 'Invalid Nonce', [ 'back_link' => true, ] ); } $module->recalc_usage(); } $usage = ''; foreach ( $module->get_formatted_usage() as $doc_type => $data ) { $usage .= '' . $data['title'] . ' ( ' . $data['count'] . ' )'; foreach ( $data['elements'] as $element => $count ) { $usage .= $element . ': ' . $count . PHP_EOL; } $usage .= ''; } return [ 'value' => $usage, ]; } public function get_raw_usage() { /** @var Module $module */ $module = Module::instance(); $usage = PHP_EOL; foreach ( $module->get_formatted_usage( 'raw' ) as $doc_type => $data ) { $usage .= "\t{$data['title']} : " . $data['count'] . PHP_EOL; foreach ( $data['elements'] as $element => $count ) { $usage .= "\t\t{$element} : {$count}" . PHP_EOL; } } return [ 'value' => $usage, ]; } /** * Removes the "elementor_usage_recalc" param from the query string to avoid recalc every refresh. * When using a redirect header in place of this approach it throws an error because some components have already output some content. * * @return string */ private function get_remove_recalc_query_string_script() { ob_start(); ?> get_js_assets_url( 'color-thief', 'assets/lib/color-thief/', true ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); } /** * Module constructor - Initialize the Eye-Dropper module. * * @return void */ public function __construct() { add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); } } styleguide/module.php000064400000006445146731356320010743 0ustar00preview->is_preview(); if ( ! $is_preview ) { return; } $this->enqueue_app_initiator( $is_preview ); } ); add_action( 'elementor/controls/register', [ $this, 'register_controls' ] ); add_action( 'elementor/element/after_section_start', [ $this, 'add_styleguide_enable_controls' ], 10, 3 ); } /** * Retrieve the module name. * * @return string */ public function get_name() { return 'styleguide'; } /** * Enqueue scripts. * * @return void */ public function enqueue_main_scripts() { wp_enqueue_script( static::ASSETS_HANDLE, $this->get_js_assets_url( static::ASSETS_SRC ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); $kit_id = Plugin::$instance->kits_manager->get_active_id(); wp_localize_script( static::ASSETS_HANDLE, 'elementorStyleguideConfig', [ 'activeKitId' => $kit_id, ] ); } public function enqueue_app_initiator( $is_preview = false ) { $dependencies = [ 'wp-i18n', 'react', 'react-dom', ]; if ( ! $is_preview ) { $dependencies[] = static::ASSETS_HANDLE; } wp_enqueue_script( static::ASSETS_HANDLE . '-app-initiator', $this->get_js_assets_url( static::ASSETS_SRC . '-app-initiator' ), $dependencies, ELEMENTOR_VERSION, true ); } public function enqueue_styles() { wp_enqueue_style( static::ASSETS_HANDLE, $this->get_css_assets_url( 'modules/' . static::ASSETS_SRC . '/' . static::ASSETS_SRC ), [], ELEMENTOR_VERSION ); } public function register_controls() { $controls_manager = Plugin::$instance->controls_manager; $controls_manager->register( new Switcher() ); } /** * Add the Enable Styleguide Preview controls to Global Colors and Global Fonts. * * @param $element * @param string $section_id * @param array $args */ public function add_styleguide_enable_controls( $element, $section_id, $args ) { if ( 'kit' !== $element->get_name() || ! in_array( $section_id, [ 'section_global_colors', 'section_text_style' ] ) ) { return; } $control_name = str_replace( 'global-', '', $args['tab'] ) . '_enable_styleguide_preview'; $element->add_control( $control_name, [ 'label' => esc_html__( 'Style Guide Preview', 'elementor' ), 'type' => Switcher::CONTROL_TYPE, 'description' => esc_html__( 'Switch between the content area and style guide to preview your changes to global colors.', 'elementor' ), 'separator' => 'after', 'label_off' => esc_html__( 'Off', 'elementor' ), 'label_on' => esc_html__( 'On', 'elementor' ), 'on_change_command' => true, ] ); } } styleguide/controls/switcher.php000064400000000762146731356320013145 0ustar00performance_lab_get_webp_src( $value['id'], 'full', $value['url'] ); } return $value; } public function __construct() { parent::__construct(); if ( $this->is_performance_lab_is_active() ) { add_filter( 'elementor/files/css/property', function( $value, $css_property, $matches ) { return $this->replace_css_with_webp( $value, $css_property, $matches ); }, 10, 3 ); } if ( is_admin() ) { add_action( 'activated_plugin', function( $plugin ) { if ( 'performance-lab/load.php' === $plugin ) { Plugin::$instance->files_manager->clear_cache(); } } ); add_action( 'deactivated_plugin', function( $plugin ) { if ( 'performance-lab/load.php' === $plugin ) { Plugin::$instance->files_manager->clear_cache(); } } ); } } } container-converter/module.php000064400000010123146731356320012540 0ustar00experiments->is_feature_active( 'container' ); } /** * Enqueue the module scripts. * * @return void */ public function enqueue_scripts() { wp_enqueue_script( 'container-converter', $this->get_js_assets_url( 'container-converter' ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); } /** * Enqueue the module styles. * * @return void */ public function enqueue_styles() { wp_enqueue_style( 'container-converter', $this->get_css_assets_url( 'modules/container-converter/editor' ), [], ELEMENTOR_VERSION ); } /** * Add a convert button to sections. * * @param \Elementor\Controls_Stack $controls_stack * * @return void */ protected function add_section_convert_button( Controls_Stack $controls_stack ) { if ( ! Plugin::$instance->editor->is_edit_mode() ) { return; } $controls_stack->start_injection( [ 'of' => '_title', ] ); $controls_stack->add_control( 'convert_to_container', [ 'type' => Controls_Manager::BUTTON, 'label' => esc_html__( 'Convert to container', 'elementor' ), 'text' => esc_html__( 'Convert', 'elementor' ), 'button_type' => 'default', 'description' => esc_html__( 'Copies all of the selected sections and columns and pastes them in a container beneath the original.', 'elementor' ), 'separator' => 'after', 'event' => static::EVENT_NAME, ] ); $controls_stack->end_injection(); } /** * Add a convert button to page settings. * * @param \Elementor\Controls_Stack $controls_stack * * @return void */ protected function add_page_convert_button( Controls_Stack $controls_stack ) { if ( ! Plugin::$instance->editor->is_edit_mode() || ! $this->page_contains_sections( $controls_stack ) || ! Plugin::$instance->role_manager->user_can( 'design' ) ) { return; } $controls_stack->start_injection( [ 'of' => 'post_title', 'at' => 'before', ] ); $controls_stack->add_control( 'convert_to_container', [ 'type' => Controls_Manager::BUTTON, 'label' => esc_html__( 'Convert to container', 'elementor' ), 'text' => esc_html__( 'Convert', 'elementor' ), 'button_type' => 'default', 'description' => esc_html__( 'Copies all of the selected sections and columns and pastes them in a container beneath the original.', 'elementor' ), 'separator' => 'after', 'event' => static::EVENT_NAME, ] ); $controls_stack->end_injection(); } /** * Checks if document has any Section elements. * * @param \Elementor\Controls_Stack $controls_stack * * @return bool */ protected function page_contains_sections( $controls_stack ) { $data = $controls_stack->get_elements_data(); if ( ! is_array( $data ) ) { return false; } foreach ( $data as $element ) { if ( isset( $element['elType'] ) && 'section' === $element['elType'] ) { return true; } } return false; } /** * Initialize the Container-Converter module. * * @return void */ public function __construct() { add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_action( 'elementor/editor/after_enqueue_styles', [ $this, 'enqueue_styles' ] ); add_action( 'elementor/element/section/section_layout/after_section_end', function ( Controls_Stack $controls_stack ) { $this->add_section_convert_button( $controls_stack ); } ); add_action( 'elementor/documents/register_controls', function ( Controls_Stack $controls_stack ) { $this->add_page_convert_button( $controls_stack ); } ); } } gutenberg/module.php000064400000014121146731356320010535 0ustar00 function( $request_value, $object ) { if ( ! User::is_current_user_can_edit( $object->ID ) ) { return false; } $document = Plugin::$instance->documents->get( $object->ID ); if ( ! $document ) { return false; } $document->set_is_built_with_elementor( false ); return true; }, ] ); } /** * @since 2.1.0 * @access public */ public function enqueue_assets() { $document = Plugin::$instance->documents->get( get_the_ID() ); if ( ! $document || ! $document->is_editable_by_current_user() ) { return; } $this->is_gutenberg_editor_active = true; $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; wp_enqueue_script( 'elementor-gutenberg', ELEMENTOR_ASSETS_URL . 'js/gutenberg' . $suffix . '.js', [ 'jquery' ], ELEMENTOR_VERSION, true ); $elementor_settings = [ 'isElementorMode' => $document->is_built_with_elementor(), 'editLink' => $document->get_edit_url(), ]; Utils::print_js_config( 'elementor-gutenberg', 'ElementorGutenbergSettings', $elementor_settings ); } /** * @since 2.1.0 * @access public */ public function print_admin_js_template() { if ( ! $this->is_gutenberg_editor_active ) { return; } ?> ID ) ) { return false; } if ( ! static::is_built_with_elementor( $post ) ) { return false; } if ( static::is_gutenberg_in_post( $post ) ) { return false; } return true; } private static function is_built_with_elementor( $post ) : bool { $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document || ! $document->is_built_with_elementor() ) { return false; } return true; } private static function is_gutenberg_in_post( $post ) : bool { if ( has_blocks( $post ) ) { return true; } if ( static::current_theme_is_fse_theme() ) { return true; } return false; } private static function current_theme_is_fse_theme() : bool { if ( function_exists( 'wp_is_block_theme' ) ) { return (bool) wp_is_block_theme(); } if ( function_exists( 'gutenberg_is_fse_theme' ) ) { return (bool) gutenberg_is_fse_theme(); } return false; } } page-templates/module.php000064400000025755146731356320011502 0ustar00documents->get_doc_for_frontend( get_the_ID() ); if ( $document && $document::get_property( 'support_wp_page_templates' ) ) { $page_template = $document->get_meta( '_wp_page_template' ); $template_path = $this->get_template_path( $page_template ); if ( self::TEMPLATE_THEME !== $page_template && ! $template_path && $document->is_built_with_elementor() ) { $kit_default_template = Plugin::$instance->kits_manager->get_current_settings( 'default_page_template' ); $template_path = $this->get_template_path( $kit_default_template ); } if ( $template_path ) { $template = $template_path; Plugin::$instance->inspector->add_log( 'Page Template', Plugin::$instance->inspector->parse_template_path( $template ), $document->get_edit_url() ); } } } return $template; } /** * Add WordPress templates. * * Adds Elementor templates to all the post types that support * Elementor. * * Fired by `init` action. * * @since 2.0.0 * @access public */ public function add_wp_templates_support() { $post_types = get_post_types_by_support( 'elementor' ); foreach ( $post_types as $post_type ) { add_filter( "theme_{$post_type}_templates", [ $this, 'add_page_templates' ], 10, 4 ); } } /** * Add page templates. * * Add the Elementor page templates to the theme templates. * * Fired by `theme_{$post_type}_templates` filter. * * @since 2.0.0 * @access public * @static * * @param array $page_templates Array of page templates. Keys are filenames, * checks are translated names. * * @param \WP_Theme $wp_theme * @param \WP_Post $post * * @return array Page templates. */ public function add_page_templates( $page_templates, $wp_theme, $post ) { if ( $post ) { // FIX ME: Gutenberg not send $post as WP_Post object, just the post ID. $post_id = ! empty( $post->ID ) ? $post->ID : $post; $document = Plugin::$instance->documents->get( $post_id ); if ( $document && ! $document::get_property( 'support_wp_page_templates' ) ) { return $page_templates; } } $page_templates = [ self::TEMPLATE_CANVAS => esc_html__( 'Elementor Canvas', 'elementor' ), self::TEMPLATE_HEADER_FOOTER => esc_html__( 'Elementor Full Width', 'elementor' ), self::TEMPLATE_THEME => esc_html__( 'Theme', 'elementor' ), ] + $page_templates; return $page_templates; } /** * Set print callback. * * Set the page template callback. * * @since 2.0.0 * @access public * * @param callable $callback */ public function set_print_callback( $callback ) { $this->print_callback = $callback; } /** * Print callback. * * Prints the page template content using WordPress loop. * * @since 2.0.0 * @access public */ public function print_callback() { while ( have_posts() ) : the_post(); the_content(); endwhile; } /** * Print content. * * Prints the page template content. * * @since 2.0.0 * @access public */ public function print_content() { if ( ! $this->print_callback ) { $this->print_callback = [ $this, 'print_callback' ]; } call_user_func( $this->print_callback ); } /** * Get page template path. * * Retrieve the path for any given page template. * * @since 2.0.0 * @access public * * @param string $page_template The page template name. * * @return string Page template path. */ public function get_template_path( $page_template ) { $template_path = ''; switch ( $page_template ) { case self::TEMPLATE_CANVAS: $template_path = __DIR__ . '/templates/canvas.php'; break; case self::TEMPLATE_HEADER_FOOTER: $template_path = __DIR__ . '/templates/header-footer.php'; break; } return $template_path; } /** * Register template control. * * Adds custom controls to any given document. * * Fired by `update_post_metadata` action. * * @since 2.0.0 * @access public * * @param Document $document The document instance. */ public function action_register_template_control( $document ) { if ( $document instanceof PageBase || $document instanceof LibraryPageDocument ) { $this->register_template_control( $document ); } } /** * Register template control. * * Adds custom controls to any given document. * * @since 2.0.0 * @access public * * @param Document $document The document instance. * @param string $control_id Optional. The control ID. Default is `template`. */ public function register_template_control( $document, $control_id = 'template' ) { if ( ! Utils::is_cpt_custom_templates_supported() ) { return; } require_once ABSPATH . '/wp-admin/includes/template.php'; $document->start_injection( [ 'of' => 'post_status', 'fallback' => [ 'of' => 'post_title', ], ] ); $control_options = [ 'options' => array_flip( get_page_templates( null, $document->get_main_post()->post_type ) ), ]; $this->add_template_controls( $document, $control_id, $control_options ); $document->end_injection(); } // The $options variable is an array of $control_options to overwrite the default public function add_template_controls( Document $document, $control_id, $control_options ) { // Default Control Options $default_control_options = [ 'label' => esc_html__( 'Page Layout', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), ], ]; $control_options = array_replace_recursive( $default_control_options, $control_options ); $document->add_control( $control_id, $control_options ); $document->add_control( $control_id . '_default_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'The default page template as defined in Elementor Panel → Hamburger Menu → Site Settings.', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => 'default', ], ] ); $document->add_control( $control_id . '_theme_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'Default Page Template from your theme.', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_THEME, ], ] ); $document->add_control( $control_id . '_canvas_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'No header, no footer, just Elementor', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_CANVAS, ], ] ); $document->add_control( $control_id . '_header_footer_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'This template includes the header, full-width content and footer', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_HEADER_FOOTER, ], ] ); if ( $document instanceof Kit ) { $document->add_control( 'reload_preview_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Changes will be reflected in the preview only after the page reloads.', 'elementor' ), 'content_classes' => 'elementor-descriptor', ] ); } } /** * Filter metadata update. * * Filters whether to update metadata of a specific type. * * Elementor don't allow WordPress to update the parent page template * during `wp_update_post`. * * Fired by `update_{$meta_type}_metadata` filter. * * @since 2.0.0 * @access public * * @param bool $check Whether to allow updating metadata for the given type. * @param int $object_id Object ID. * @param string $meta_key Meta key. * * @return bool Whether to allow updating metadata of a specific type. */ public function filter_update_meta( $check, $object_id, $meta_key ) { if ( '_wp_page_template' === $meta_key && Plugin::$instance->common ) { /** @var \Elementor\Core\Common\Modules\Ajax\Module $ajax */ $ajax = Plugin::$instance->common->get_component( 'ajax' ); $ajax_data = $ajax->get_current_action_data(); $is_autosave_action = $ajax_data && 'save_builder' === $ajax_data['action'] && Document::STATUS_AUTOSAVE === $ajax_data['data']['status']; // Don't allow WP to update the parent page template. // (during `wp_update_post` from page-settings or save_plain_text). if ( $is_autosave_action && ! wp_is_post_autosave( $object_id ) && Document::STATUS_DRAFT !== get_post_status( $object_id ) ) { $check = false; } } return $check; } /** * Support `wp_body_open` action, available since WordPress 5.2. * * @since 2.7.0 * @access public */ public static function body_open() { wp_body_open(); } /** * Page templates module constructor. * * Initializing Elementor page templates module. * * @since 2.0.0 * @access public */ public function __construct() { add_action( 'init', [ $this, 'add_wp_templates_support' ] ); add_filter( 'template_include', [ $this, 'template_include' ], 11 /* After Plugins/WooCommerce */ ); add_action( 'elementor/documents/register_controls', [ $this, 'action_register_template_control' ] ); add_filter( 'update_post_metadata', [ $this, 'filter_update_meta' ], 10, 3 ); } } page-templates/templates/header-footer.php000064400000001332146731356320014720 0ustar00frontend->add_body_class( 'elementor-template-full-width' ); get_header(); /** * Before Header-Footer page template content. * * Fires before the content of Elementor Header-Footer page template. * * @since 2.0.0 */ do_action( 'elementor/page_templates/header-footer/before_content' ); \Elementor\Plugin::$instance->modules_manager->get_modules( 'page-templates' )->print_content(); /** * After Header-Footer page template content. * * Fires after the content of Elementor Header-Footer page template. * * @since 2.0.0 */ do_action( 'elementor/page_templates/header-footer/after_content' ); get_footer(); page-templates/templates/canvas.php000064400000002517146731356320013455 0ustar00frontend->add_body_class( 'elementor-template-canvas' ); ?> > <?php echo wp_get_document_title(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> > modules_manager->get_modules( 'page-templates' )->print_content(); /** * After canvas page template content. * * Fires after the content of Elementor canvas page template. * * @since 1.0.0 */ do_action( 'elementor/page_templates/canvas/after_content' ); wp_footer(); ?> compatibility-tag/module.php000064400000003251146731356320012177 0ustar00merge( $this->get_plugins_with_plugin_title_in_their_name() ); } /** * Get all the plugins that has the name of the current plugin in their name. * * @return Collection */ private function get_plugins_with_plugin_title_in_their_name() { return Plugin::$instance->wp ->get_plugins() ->except( [ 'elementor/elementor.php', 'elementor-beta/elementor-beta.php', 'block-builder/block-builder.php', ] ) ->filter( function ( array $data ) { return false !== strpos( strtolower( $data['Name'] ), 'elementor' ); } ); } } compatibility-tag/views/plugin-update-message-compatibility.php000064400000004256146731356320021124 0ustar00
- get_plugin_label() ), $new_version->__toString() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); ?>

$plugin_data ) : ?> get_plugin_header() ] = esc_html__( 'Unknown', 'elementor' ); } ?>
get_plugin_label() ) ); ?>
get_plugin_header() ] ); ?>
compatibility-tag/base-module.php000064400000007505146731356320013115 0ustar00compatibility_tag_service ) { $this->compatibility_tag_service = new Compatibility_Tag( $this->get_plugin_header() ); } return $this->compatibility_tag_service; } /** * Add allowed headers to plugins. * * @param array $headers * @param $compatibility_tag_header * * @return array */ protected function enable_elementor_headers( array $headers, $compatibility_tag_header ) { $headers[] = $compatibility_tag_header; return $headers; } /** * @return Collection */ protected function get_plugins_to_check() { return $this->get_plugins_with_header(); } /** * Append a compatibility message to the update plugin warning. * * @param array $args * * @throws \Exception */ protected function on_plugin_update_message( array $args ) { $new_version = Version::create_from_string( $args['new_version'] ); if ( $new_version->compare( '=', $args['Version'], Version::PART_MAJOR_2 ) ) { return; } $plugins = $this->get_plugins_to_check(); $plugins_compatibility = $this->get_compatibility_tag_service()->check( $new_version, $plugins->keys() ); $plugins = $plugins->filter( function ( $data, $plugin_name ) use ( $plugins_compatibility ) { return Compatibility_Tag::COMPATIBLE !== $plugins_compatibility[ $plugin_name ]; } ); if ( $plugins->is_empty() ) { return; } include __DIR__ . '/views/plugin-update-message-compatibility.php'; } /** * Get all plugins with specific header. * * @return Collection */ private function get_plugins_with_header() { return Plugin::$instance->wp ->get_plugins() ->filter( function ( array $plugin ) { return ! empty( $plugin[ $this->get_plugin_header() ] ); } ); } /** * @return string */ abstract protected function get_plugin_header(); /** * @return string */ abstract protected function get_plugin_label(); /** * @return string */ abstract protected function get_plugin_name(); /** * @return string */ abstract protected function get_plugin_version(); /** * Base_Module constructor. * * @throws \Exception */ public function __construct() { add_filter( 'extra_plugin_headers', function ( array $headers ) { return $this->enable_elementor_headers( $headers, $this->get_plugin_header() ); } ); add_action( 'in_plugin_update_message-' . $this->get_plugin_name(), function ( array $args ) { $this->on_plugin_update_message( $args ); }, 11 /* After the warning message for backup */ ); add_action( 'elementor/system_info/get_allowed_reports', function () { $plugin_short_name = basename( $this->get_plugin_name(), '.php' ); System_Info::add_report( "{$plugin_short_name}_compatibility", [ 'file_name' => __DIR__ . '/compatibility-tag-report.php', 'class_name' => __NAMESPACE__ . '\Compatibility_Tag_Report', 'fields' => [ 'compatibility_tag_service' => $this->get_compatibility_tag_service(), 'plugin_label' => $this->get_plugin_label(), 'plugin_version' => Version::create_from_string( $this->get_plugin_version() ), 'plugins_to_check' => $this->get_plugins_to_check() ->only( get_option( 'active_plugins' ) ) ->keys(), ], ] ); } ); } } compatibility-tag/compatibility-tag.php000064400000003707146731356320014342 0ustar00header = $header; } /** * Return if plugins is compatible or not. * * @param Version $version * @param array $plugins_names * * @return array * @throws \Exception */ public function check( Version $version, array $plugins_names ) { return ( new Collection( $plugins_names ) ) ->map_with_keys( function ( $plugin_name ) use ( $version ) { return [ $plugin_name => $this->is_compatible( $version, $plugin_name ) ]; } ) ->all(); } /** * Check single plugin if is compatible or not. * * @param Version $version * @param $plugin_name * * @return string * @throws \Exception */ private function is_compatible( Version $version, $plugin_name ) { $plugins = Plugin::$instance->wp->get_plugins(); if ( ! isset( $plugins[ $plugin_name ] ) ) { return self::PLUGIN_NOT_EXISTS; } $requested_plugin = $plugins[ $plugin_name ]; if ( empty( $requested_plugin[ $this->header ] ) ) { return self::HEADER_NOT_EXISTS; } if ( ! Version::is_valid_version( $requested_plugin[ $this->header ] ) ) { return self::INVALID_VERSION; } if ( $version->compare( '>', $requested_plugin[ $this->header ], Version::PART_MAJOR_2 ) ) { return self::INCOMPATIBLE; } return self::COMPATIBLE; } } compatibility-tag/compatibility-tag-report.php000064400000010414146731356320015644 0ustar00compatibility_tag_service = $this->_properties['fields']['compatibility_tag_service']; $this->plugin_label = $this->_properties['fields']['plugin_label']; $this->plugin_version = $this->_properties['fields']['plugin_version']; $this->plugins_to_check = $this->_properties['fields']['plugins_to_check']; } /** * The title of the report * * @return string */ public function get_title() { return $this->plugin_label . ' - Compatibility Tag'; } /** * Report fields * * @return string[] */ public function get_fields() { return [ 'report_data' => '', ]; } /** * Report data. * * @return string[] * @throws \Exception */ public function get_report_data() { $compatibility_status = $this->compatibility_tag_service->check( $this->plugin_version, $this->plugins_to_check ); return [ 'value' => $compatibility_status, ]; } public function get_html_report_data() { $compatibility_status = $this->compatibility_tag_service->check( $this->plugin_version, $this->plugins_to_check ); $compatibility_status = $this->get_html_from_compatibility_status( $compatibility_status ); return [ 'value' => $compatibility_status, ]; } public function get_raw_report_data() { $compatibility_status = $this->compatibility_tag_service->check( $this->plugin_version, $this->plugins_to_check ); $compatibility_status = $this->get_raw_from_compatibility_status( $compatibility_status ); return [ 'value' => $compatibility_status, ]; } /** * Merge compatibility status with the plugins data. * * @param array $compatibility_status * * @return Collection */ private function merge_compatibility_status_with_plugins( array $compatibility_status ) { $labels = $this->get_report_labels(); $compatibility_status = ( new Collection( $compatibility_status ) ) ->map( function ( $value ) use ( $labels ) { $status = isset( $labels[ $value ] ) ? $labels[ $value ] : esc_html__( 'Unknown', 'elementor' ); return [ 'compatibility_status' => $status ]; } ); return Plugin::$instance->wp ->get_plugins() ->only( $compatibility_status->keys() ) ->merge_recursive( $compatibility_status ); } /** * Format compatibility status into HTML. * * @param array $compatibility_status * * @return string */ private function get_html_from_compatibility_status( array $compatibility_status ) { return $this->merge_compatibility_status_with_plugins( $compatibility_status ) ->map( function ( array $plugin ) { return " {$plugin['Name']} {$plugin['compatibility_status']} "; } ) ->implode( '' ); } /** * Format compatibility status into raw string. * * @param array $compatibility_status * * @return string */ private function get_raw_from_compatibility_status( array $compatibility_status ) { return PHP_EOL . $this->merge_compatibility_status_with_plugins( $compatibility_status ) ->map( function ( array $plugin ) { return "\t{$plugin['Name']}: {$plugin['compatibility_status']}"; } ) ->implode( PHP_EOL ); } /** * @return array */ private function get_report_labels() { return [ Compatibility_Tag::COMPATIBLE => esc_html__( 'Compatible', 'elementor' ), Compatibility_Tag::INCOMPATIBLE => esc_html__( 'Incompatible', 'elementor' ), Compatibility_Tag::HEADER_NOT_EXISTS => esc_html__( 'Compatibility not specified', 'elementor' ), Compatibility_Tag::INVALID_VERSION => esc_html__( 'Compatibility unknown', 'elementor' ), Compatibility_Tag::PLUGIN_NOT_EXISTS => esc_html__( 'Error', 'elementor' ), ]; } } safe-mode/module.php000064400000037730146731356320010426 0ustar00register_ajax_action( 'enable_safe_mode', [ $this, 'ajax_enable_safe_mode' ] ); $ajax->register_ajax_action( 'disable_safe_mode', [ $this, 'disable_safe_mode' ] ); } /** * @param Tools $tools_page */ public function add_admin_button( $tools_page ) { $tools_page->add_fields( Settings::TAB_GENERAL, 'tools', [ 'safe_mode' => [ 'label' => esc_html__( 'Safe Mode', 'elementor' ), 'field_args' => [ 'type' => 'select', 'std' => $this->is_enabled() ? 'global' : '', 'options' => [ '' => esc_html__( 'Disable', 'elementor' ), 'global' => esc_html__( 'Enable', 'elementor' ), ], 'desc' => esc_html__( 'Safe Mode allows you to troubleshoot issues by only loading the editor, without loading the theme or any other plugin.', 'elementor' ), ], ], ] ); } public function on_update_safe_mode( $value ) { if ( 'yes' === $value || 'global' === $value ) { $this->enable_safe_mode(); } else { $this->disable_safe_mode(); } return $value; } /** * @throws \Exception */ public function ajax_enable_safe_mode( $data ) { if ( ! current_user_can( 'install_plugins' ) ) { throw new \Exception( 'Access denied.' ); } // It will run `$this->>update_safe_mode`. update_option( 'elementor_safe_mode', 'yes' ); $document = Plugin::$instance->documents->get( $data['editor_post_id'] ); if ( $document ) { return add_query_arg( 'elementor-mode', 'safe', $document->get_edit_url() ); } return false; } public function enable_safe_mode() { if ( ! current_user_can( 'install_plugins' ) ) { return; } WP_Filesystem(); $this->update_allowed_plugins(); if ( ! is_dir( WPMU_PLUGIN_DIR ) ) { wp_mkdir_p( WPMU_PLUGIN_DIR ); add_option( 'elementor_safe_mode_created_mu_dir', true ); } if ( ! is_dir( WPMU_PLUGIN_DIR ) ) { wp_die( esc_html__( 'Cannot enable Safe Mode', 'elementor' ) ); } $results = copy_dir( __DIR__ . '/mu-plugin/', WPMU_PLUGIN_DIR ); if ( is_wp_error( $results ) ) { return; } $token = hash( 'sha256', wp_rand() ); // Only who own this key can use 'elementor-safe-mode'. update_option( self::OPTION_TOKEN, $token ); // Save for later use. setcookie( self::OPTION_TOKEN, $token, time() + HOUR_IN_SECONDS, COOKIEPATH, '', is_ssl(), true ); } public function disable_safe_mode() { if ( ! current_user_can( 'install_plugins' ) ) { return; } $file_path = WP_CONTENT_DIR . '/mu-plugins/elementor-safe-mode.php'; if ( file_exists( $file_path ) ) { unlink( $file_path ); } if ( get_option( 'elementor_safe_mode_created_mu_dir' ) ) { // It will be removed only if it's empty and don't have other mu-plugins. @rmdir( WPMU_PLUGIN_DIR ); } delete_option( 'elementor_safe_mode' ); delete_option( 'elementor_safe_mode_allowed_plugins' ); delete_option( 'theme_mods_elementor-safe' ); delete_option( 'elementor_safe_mode_created_mu_dir' ); delete_option( self::OPTION_TOKEN ); setcookie( self::OPTION_TOKEN, '', 1, '', '', is_ssl(), true ); } public function filter_preview_url( $url ) { return add_query_arg( 'elementor-mode', 'safe', $url ); } public function filter_template() { return ELEMENTOR_PATH . 'modules/page-templates/templates/canvas.php'; } public function print_safe_mode_css() { ?> print_safe_mode_css() ?>

  • ', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' ); ?>
  • ', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' ); ?>
', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' ); ?>
is_allowed_post_type() ) { return; } $this->print_safe_mode_css(); ?> ' . esc_html__( 'Disable Safe Mode', 'elementor' ) . ''; return $actions; } public function on_deactivated_plugin( $plugin ) { if ( ELEMENTOR_PLUGIN_BASE === $plugin ) { $this->disable_safe_mode(); return; } $allowed_plugins = get_option( 'elementor_safe_mode_allowed_plugins', [] ); $plugin_key = array_search( $plugin, $allowed_plugins, true ); if ( $plugin_key ) { unset( $allowed_plugins[ $plugin_key ] ); update_option( 'elementor_safe_mode_allowed_plugins', $allowed_plugins ); } } public function update_allowed_plugins() { $allowed_plugins = [ 'elementor' => ELEMENTOR_PLUGIN_BASE, ]; if ( defined( 'ELEMENTOR_PRO_PLUGIN_BASE' ) ) { $allowed_plugins['elementor_pro'] = ELEMENTOR_PRO_PLUGIN_BASE; } if ( defined( 'WC_PLUGIN_BASENAME' ) ) { $allowed_plugins['woocommerce'] = WC_PLUGIN_BASENAME; } update_option( 'elementor_safe_mode_allowed_plugins', $allowed_plugins ); } public function __construct() { if ( current_user_can( 'install_plugins' ) ) { add_action( 'elementor/admin/after_create_settings/elementor-tools', [ $this, 'add_admin_button' ] ); } add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); $plugin_file = self::MU_PLUGIN_FILE_NAME; add_filter( "plugin_action_links_{$plugin_file}", [ $this, 'plugin_action_links' ] ); // Use pre_update, in order to catch cases that $value === $old_value and it not updated. add_filter( 'pre_update_option_elementor_safe_mode', [ $this, 'on_update_safe_mode' ], 10, 2 ); add_action( 'elementor/safe_mode/init', [ $this, 'run_safe_mode' ] ); add_action( 'elementor/editor/footer', [ $this, 'print_try_safe_mode' ] ); if ( $this->is_enabled() ) { add_action( 'activated_plugin', [ $this, 'update_allowed_plugins' ] ); add_action( 'deactivated_plugin', [ $this, 'on_deactivated_plugin' ] ); } } private function is_allowed_post_type() { $allowed_post_types = [ 'post', 'page', 'product', Source_Local::CPT, ]; $current_post_type = get_post_type( Plugin::$instance->editor->get_post_id() ); return in_array( $current_post_type, $allowed_post_types ); } } safe-mode/mu-plugin/elementor-safe-mode.php000064400000007466146731356320014711 0ustar00 '' . esc_html__( 'Learn More', 'elementor' ) . '', ]; $plugin_meta = array_merge( $plugin_meta, $row_meta ); } return $plugin_meta; } public function __construct() { add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 4 ); $enabled_type = $this->is_enabled(); if ( ! $enabled_type || ! $this->is_valid_token() ) { return; } if ( ! $this->is_requested() && 'global' !== $enabled_type ) { return; } if ( ! $this->is_editor() && ! $this->is_editor_preview() && ! $this->is_editor_ajax() ) { return; } $this->add_hooks(); } } new Safe_Mode(); promotions/module.php000064400000005231146731356320010766 0ustar00handle_external_redirects(); } ); add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu ) { $this->register_menu_items( $admin_menu ); }, static::ADMIN_MENU_PRIORITY ); add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu ) { $this->register_promotion_menu_item( $admin_menu ); }, static::ADMIN_MENU_PROMOTIONS_PRIORITY ); add_action( 'elementor/widgets/register', function( Widgets_Manager $manager ) { foreach ( Api::get_promotion_widgets() as $widget_data ) { $manager->register( new Widgets\Pro_Widget_Promotion( [], [ 'widget_name' => $widget_data['name'], 'widget_title' => $widget_data['title'], ] ) ); } } ); } private function handle_external_redirects() { if ( empty( $_GET['page'] ) ) { return; } if ( 'go_elementor_pro' === $_GET['page'] ) { wp_redirect( Go_Pro_Promotion_Item::get_url() ); die; } } private function register_menu_items( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( 'e-form-submissions', new Form_Submissions_Promotion_Item() ); $admin_menu->register( 'elementor_custom_fonts', new Custom_Fonts_Promotion_Item() ); $admin_menu->register( 'elementor_custom_icons', new Custom_Icons_Promotion_Item() ); $admin_menu->register( 'elementor_custom_code', new Custom_Code_Promotion_Item() ); $admin_menu->register( 'popup_templates', new Popups_Promotion_Item() ); } private function register_promotion_menu_item( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( 'go_elementor_pro', new Go_Pro_Promotion_Item() ); } } promotions/admin-menu-items/interfaces/promotion-menu-item.php000064400000000732146731356320020722 0ustar00' ); } protected function get_content_lines(): array { return [ sprintf( esc_html__( 'Expand your icon library beyond FontAwesome and add icon %s libraries of your choice', 'elementor' ), '
' ), esc_html__( 'Add any icon, anywhere on your website', 'elementor' ), ]; } protected function get_cta_url(): string { return 'https://go.elementor.com/go-pro-custom-icons/'; } protected function get_video_url(): string { return 'https://www.youtube-nocookie.com/embed/PsowinxDWfM?si=SV9Z3TLz3_XEy5C6'; } } promotions/admin-menu-items/form-submissions-promotion-item.php000064400000002736146731356320021160 0ustar00' ); } protected function get_content_lines(): array { return [ esc_html__( 'Create single or multi-step forms to engage and convert visitors', 'elementor' ), esc_html__( 'Use any field to collect the information you need', 'elementor' ), esc_html__( 'Integrate your favorite marketing software*', 'elementor' ), esc_html__( 'Collect lead submissions directly within your WordPress Admin to manage, analyze and perform bulk actions on the submitted lead*', 'elementor' ), ]; } protected function get_cta_url():string { return 'https://go.elementor.com/go-pro-submissions/'; } protected function get_video_url():string { return 'https://www.youtube-nocookie.com/embed/LNfnwba9C-8?si=JLHk3UAexnvTfU1a'; } protected function get_side_note():string { return esc_html__( '* Requires an Advanced subscription or higher', 'elementor' ); } } promotions/admin-menu-items/base-promotion-template.php000064400000006002146731356320017416 0ustar00get_content_lines() ) ) { ?>
    get_content_lines() as $item ) { ?>
get_promotion_data(); ?> build_promotion_data_array(), 'elementor/' . $this->get_name() . '/custom_promotion', 'cta_url' ); } /** * @return array */ private function build_promotion_data_array(): array { return [ 'promotion_title' => $this->get_promotion_title(), 'cta_url' => $this->get_cta_url(), 'cta_text' => $this->get_cta_text(), 'video_url' => $this->get_video_url(), 'lines' => $this->get_lines(), 'side_note' => $this->get_side_note(), ]; } } promotions/admin-menu-items/custom-code-promotion-item.php000064400000002542146731356320020056 0ustar00 $upgrade_text ] )['upgrade_text'] ?? $upgrade_text; } public function get_page_title() { return ''; } public function get_capability() { return 'manage_options'; } public static function get_url() { $url = self::URL; $filtered_url = apply_filters( 'elementor/admin_menu/custom_promotion', [ 'upgrade_url' => $url ] )['upgrade_url'] ?? ''; $promotion_data = Filtered_Promotions_Manager::get_filtered_promotion_data( [ 'upgrade_url' => $filtered_url ], 'elementor/admin_menu/custom_promotion', 'upgrade_url' ); return $promotion_data ['upgrade_url']; } public function render() { // Redirects from the module on `admin_init`. die; } } promotions/admin-menu-items/popups-promotion-item.php000064400000003633146731356320017164 0ustar00promotion_data = [ 'title' => esc_html__( 'Get Popup Builder', 'elementor' ), 'content' => esc_html__( 'The Popup Builder lets you take advantage of all the amazing features in Elementor, so you can build beautiful & highly converting popups. Get Elementor Pro and start designing your popups today.', 'elementor' ), 'action_button' => [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => 'https://go.elementor.com/go-pro-popup-builder/', ], ]; $this->promotion_data = Filtered_Promotions_Manager::get_filtered_promotion_data( $this->promotion_data, 'elementor/templates/popup', 'action_button', 'url' ); } public function get_parent_slug() { return Source_Local::ADMIN_MENU_SLUG; } public function get_name() { return 'popups'; } public function get_label() { return esc_html__( 'Popups', 'elementor' ); } public function get_page_title() { return esc_html__( 'Popups', 'elementor' ); } public function get_promotion_title() { return $this->promotion_data['title']; } public function get_promotion_description() { return $this->promotion_data['content']; } /** * @deprecated use get_promotion_description instead * @return void */ public function render_promotion_description() { echo $this->get_promotion_description(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } public function get_cta_url() { return $this->promotion_data['action_button']['url']; } public function get_cta_text() { return $this->promotion_data['action_button']['text']; } } promotions/admin-menu-items/custom-fonts-promotion-item.php000064400000002205146731356320020271 0ustar00' ), ]; } protected function get_cta_url(): string { return 'https://go.elementor.com/go-pro-custom-fonts/'; } protected function get_video_url(): string { return 'https://www.youtube-nocookie.com/embed/j_guJkm28eY?si=cdd2TInwuGDTtCGD'; } } promotions/admin-menu-items/base-promotion-item.php000064400000003713146731356320016547 0ustar00 $this->get_promotion_title(), 'description' => $this->get_promotion_description(), 'image' => $this->get_image_url(), 'upgrade_text' => $this->get_cta_text(), 'upgrade_url' => $this->get_cta_url(), ]; $config = Filtered_Promotions_Manager::get_filtered_promotion_data( $config, 'elementor/' . $this->get_name() . '/custom_promotion', 'upgrade_url' ); $description = $config['description'] ?? $this->get_promotion_description() ?? ''; ?>

get_promotion_title() ); ?>

get_cta_text() ); ?>
widget_data['widget_name']; } public function get_title() { return $this->widget_data['widget_title']; } public function on_import( $element ) { $element['settings']['__should_import'] = true; return $element; } protected function register_controls() {} protected function render() { if ( $this->is_editor_render() ) { $this->render_promotion(); } else { $this->render_empty_content(); } } private function is_editor_render(): bool { return \Elementor\Plugin::$instance->editor->is_edit_mode(); } private function render_promotion() { ?>

Go Pro widget_data['widget_title'] ) ); ?>

widget_data = [ 'widget_name' => $args['widget_name'], 'widget_title' => $args['widget_title'], ]; parent::__construct( $data, $args ); } public function render_plain_content( $instance = [] ) {} } landing-pages/module.php000064400000042570146731356320011275 0ustar00 'landing-pages', 'title' => esc_html__( 'Landing Pages', 'elementor' ), 'description' => esc_html__( 'Adds a new Elementor content type that allows creating beautiful landing pages instantly in a streamlined workflow.', 'elementor' ), 'release_status' => Experiments_Manager::RELEASE_STATUS_BETA, 'default' => Experiments_Manager::STATE_ACTIVE, 'new_site' => [ 'default_active' => false, 'minimum_installation_version' => '3.1.0', ], ]; } /** * Get Trashed Landing Pages Posts * * Returns the posts property of a WP_Query run for Landing Pages with post_status of 'trash'. * * @since 3.1.0 * * @return array trashed posts */ private function get_trashed_landing_page_posts() { if ( $this->trashed_posts ) { return $this->trashed_posts; } // `'posts_per_page' => 1` is because this is only used as an indicator to whether there are any trashed landing pages. $trashed_posts_query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => self::CPT, 'post_status' => 'trash', 'posts_per_page' => 1, 'meta_key' => '_elementor_template_type', 'meta_value' => self::DOCUMENT_TYPE, ] ); $this->trashed_posts = $trashed_posts_query->posts; return $this->trashed_posts; } private function has_landing_pages() { if ( null !== $this->has_pages ) { return $this->has_pages; } $posts_query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => self::CPT, 'post_status' => 'any', 'posts_per_page' => 1, 'meta_key' => '_elementor_template_type', 'meta_value' => self::DOCUMENT_TYPE, ] ); $this->has_pages = $posts_query->post_count > 0; return $this->has_pages; } /** * Is Elementor Landing Page. * * Check whether the post is an Elementor Landing Page. * * @since 3.1.0 * @access public * * @param \WP_Post $post Post Object * * @return bool Whether the post was built with Elementor. */ public function is_elementor_landing_page( $post ) { return self::CPT === $post->post_type; } private function get_menu_args() { if ( $this->has_landing_pages() ) { $menu_slug = self::ADMIN_PAGE_SLUG; $function = null; } else { $menu_slug = self::CPT; $function = [ $this, 'print_empty_landing_pages_page' ]; } return [ 'menu_slug' => $menu_slug, 'function' => $function, ]; } private function register_admin_menu( MainMenu $menu ) { $landing_pages_title = esc_html__( 'Landing Pages', 'elementor' ); $menu_args = array_merge( $this->get_menu_args(), [ 'page_title' => $landing_pages_title, 'menu_title' => $landing_pages_title, 'index' => 20, ] ); $menu->add_submenu( $menu_args ); } /** * Add Submenu Page * * Adds the 'Landing Pages' submenu item to the 'Templates' menu item. * * @since 3.1.0 */ private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) { $menu_args = $this->get_menu_args(); $slug = $menu_args['menu_slug']; $function = $menu_args['function']; if ( is_callable( $function ) ) { $admin_menu->register( $slug, new Landing_Pages_Empty_View_Menu_Item( $function ) ); } else { $admin_menu->register( $slug, new Landing_Pages_Menu_Item() ); } } /** * Get 'Add New' Landing Page URL * * Retrieves the custom URL for the admin dashboard's 'Add New' button in the Landing Pages admin screen. This URL * creates a new Landing Pages and directly opens the Elementor Editor with the Template Library modal open on the * Landing Pages tab. * * @since 3.1.0 * * @return string */ private function get_add_new_landing_page_url() { if ( ! $this->new_lp_url ) { $this->new_lp_url = Plugin::$instance->documents->get_create_new_post_url( self::CPT, self::DOCUMENT_TYPE ) . '#library'; } return $this->new_lp_url; } /** * Get Empty Landing Pages Page * * Prints the HTML content of the page that is displayed when there are no existing landing pages in the DB. * Added as the callback to add_submenu_page. * * @since 3.1.0 */ public function print_empty_landing_pages_page() { $template_sources = Plugin::$instance->templates_manager->get_registered_sources(); $source_local = $template_sources['local']; $trashed_posts = $this->get_trashed_landing_page_posts(); ?>
print_blank_state_template( esc_html__( 'Landing Page', 'elementor' ), $this->get_add_new_landing_page_url(), esc_html__( 'Build Effective Landing Pages for your business\' marketing campaigns.', 'elementor' ) ); if ( ! empty( $trashed_posts ) ) : ?>
', '' ); ?>
base ) { return $this->is_elementor_landing_page( get_post() ); } return false; } /** * Admin Localize Settings * * Enables adding properties to the globally available elementorAdmin.config JS object in the Admin Dashboard. * Runs on the 'elementor/admin/localize_settings' filter. * * @since 3.1.0 * * @param $settings * @return array|null */ private function admin_localize_settings( $settings ) { $additional_settings = [ 'urls' => [ 'addNewLandingPageUrl' => $this->get_add_new_landing_page_url(), ], 'landingPages' => [ 'landingPagesHasPages' => $this->has_landing_pages(), 'isLandingPageAdminEdit' => $this->is_landing_page_admin_edit(), ], ]; return array_replace_recursive( $settings, $additional_settings ); } /** * Register Landing Pages CPT * * @since 3.1.0 */ private function register_landing_page_cpt() { $labels = [ 'name' => esc_html__( 'Landing Pages', 'elementor' ), 'singular_name' => esc_html__( 'Landing Page', 'elementor' ), 'add_new' => esc_html__( 'Add New', 'elementor' ), 'add_new_item' => esc_html__( 'Add New Landing Page', 'elementor' ), 'edit_item' => esc_html__( 'Edit Landing Page', 'elementor' ), 'new_item' => esc_html__( 'New Landing Page', 'elementor' ), 'all_items' => esc_html__( 'All Landing Pages', 'elementor' ), 'view_item' => esc_html__( 'View Landing Page', 'elementor' ), 'search_items' => esc_html__( 'Search Landing Pages', 'elementor' ), 'not_found' => esc_html__( 'No landing pages found', 'elementor' ), 'not_found_in_trash' => esc_html__( 'No landing pages found in trash', 'elementor' ), 'parent_item_colon' => '', 'menu_name' => esc_html__( 'Landing Pages', 'elementor' ), ]; $args = [ 'labels' => $labels, 'public' => true, 'show_in_menu' => 'edit.php?post_type=elementor_library&tabs_group=library', 'capability_type' => 'page', 'taxonomies' => [ Source_Local::TAXONOMY_TYPE_SLUG ], 'supports' => [ 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', 'page-attributes', 'thumbnail', 'custom-fields', 'post-formats', 'elementor' ], ]; register_post_type( self::CPT, $args ); } /** * Remove Post Type Slug * * Landing Pages are supposed to act exactly like pages. This includes their URLs being directly under the site's * domain name. Since "Landing Pages" is a CPT, WordPress automatically adds the landing page slug as a prefix to * it's posts' permalinks. This method checks if the post's post type is Landing Pages, and if it is, it removes * the CPT slug from the requested post URL. * * Runs on the 'post_type_link' filter. * * @since 3.1.0 * * @param $post_link * @param $post * @param $leavename * @return string|string[] */ private function remove_post_type_slug( $post_link, $post, $leavename ) { // Only try to modify the permalink if the post is a Landing Page. if ( self::CPT !== $post->post_type || 'publish' !== $post->post_status ) { return $post_link; } // Any slug prefixes need to be removed from the post link. return get_home_url() . '/' . $post->post_name . '/'; } /** * Adjust Landing Page Query * * Since Landing Pages are a CPT but should act like pages, the WP_Query that is used to fetch the page from the * database needs to be adjusted. This method adds the Landing Pages CPT to the list of queried post types, to * make sure the database query finds the correct Landing Page to display. * Runs on the 'pre_get_posts' action. * * @since 3.1.0 * * @param \WP_Query $query */ private function adjust_landing_page_query( \WP_Query $query ) { // Only handle actual pages. if ( ! $query->is_main_query() // If the query is not for a page. || ! isset( $query->query['page'] ) // If the query is for a static home/blog page. || is_home() // If the post type comes already set, the main query is probably a custom one made by another plugin. // In this case we do not want to intervene in order to not cause a conflict. || isset( $query->query['post_type'] ) ) { return; } // Create the post types property as an array and include the landing pages CPT in it. $query_post_types = [ 'post', 'page', self::CPT ]; // Since WordPress determined this is supposed to be a page, we'll pre-set the post_type query arg to make sure // it includes the Landing Page CPT, so when the query is parsed, our CPT will be a legitimate match to the // Landing Page's permalink (that is directly under the domain, without a CPT slug prefix). In some cases, // The 'name' property will be set, and in others it is the 'pagename', so we have to cover both cases. if ( ! empty( $query->query['name'] ) ) { $query->set( 'post_type', $query_post_types ); } elseif ( ! empty( $query->query['pagename'] ) && false === strpos( $query->query['pagename'], '/' ) ) { $query->set( 'post_type', $query_post_types ); // We also need to set the name query var since redirect_guess_404_permalink() relies on it. add_filter( 'pre_redirect_guess_404_permalink', function( $value ) use ( $query ) { set_query_var( 'name', $query->query['pagename'] ); return $value; } ); } } /** * Handle 404 * * This method runs after a page is not found in the database, but before a page is returned as a 404. * These cases are handled in this filter callback, that runs on the 'pre_handle_404' filter. * * In some cases (such as when a site uses custom permalink structures), WordPress's WP_Query does not identify a * Landing Page's URL as a post belonging to the Landing Page CPT. Some cases are handled successfully by the * adjust_landing_page_query() method, but some are not and still trigger a 404 process. This method handles such * cases by overriding the $wp_query global to fetch the correct landing page post entry. * * For example, since Landing Pages slugs come directly after the site domain name, WP_Query might parse the post * as a category page. Since there is no category matching the slug, it triggers a 404 process. In this case, we * run a query for a Landing Page post with the passed slug ($query->query['category_name']. If a Landing Page * with the passed slug is found, we override the global $wp_query with the new, correct query. * * @param $current_value * @param $query * @return false */ private function handle_404( $current_value, $query ) { global $wp_query; // If another plugin/theme already used this filter, exit here to avoid conflicts. if ( $current_value ) { return $current_value; } if ( // Make sure we only intervene in the main query. ! $query->is_main_query() // If a post was found, this is not a 404 case, so do not intervene. || ! empty( $query->posts ) // This filter is only meant to deal with wrong queries where the only query var is 'category_name'. // If there is no 'category_name' query var, do not intervene. || empty( $query->query['category_name'] ) // If the query is for a real taxonomy (determined by it including a table to search in, such as the // wp_term_relationships table), do not intervene. || ! empty( $query->tax_query->table_aliases ) ) { return false; } // Search for a Landing Page with the same name passed as the 'category name'. $possible_new_query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => self::CPT, 'name' => $query->query['category_name'], ] ); // Only if such a Landing Page is found, override the query to fetch the correct page. if ( ! empty( $possible_new_query->posts ) ) { $wp_query = $possible_new_query; //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited } return false; } public function __construct() { $this->permalink_structure = get_option( 'permalink_structure' ); $this->register_landing_page_cpt(); // If there is a permalink structure set to the site, run the hooks that modify the Landing Pages permalinks to // match WordPress' native 'Pages' post type. if ( '' !== $this->permalink_structure ) { // Landing Pages' post link needs to be modified to be identical to the pages permalink structure. This // needs to happen in both the admin and the front end, since post links are also used in the admin pages. add_filter( 'post_type_link', function( $post_link, $post, $leavename ) { return $this->remove_post_type_slug( $post_link, $post, $leavename ); }, 10, 3 ); // The query itself only has to be manipulated when pages are viewed in the front end. if ( ! is_admin() || wp_doing_ajax() ) { add_action( 'pre_get_posts', function ( $query ) { $this->adjust_landing_page_query( $query ); } ); // Handle cases where visiting a Landing Page's URL returns 404. add_filter( 'pre_handle_404', function ( $value, $query ) { return $this->handle_404( $value, $query ); }, 10, 2 ); } } add_action( 'elementor/documents/register', function( Documents_Manager $documents_manager ) { $documents_manager->register_document_type( self::DOCUMENT_TYPE, Landing_Page::get_class_full_name() ); } ); if ( Plugin::$instance->experiments->is_feature_active( 'admin_menu_rearrangement' ) ) { add_action( 'elementor/admin/menu_registered/elementor', function( MainMenu $menu ) { $this->register_admin_menu( $menu ); } ); } else { add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) { $this->register_admin_menu_legacy( $admin_menu ); }, Source_Local::ADMIN_MENU_PRIORITY + 20 ); } // Add the custom 'Add New' link for Landing Pages into Elementor's admin config. add_action( 'elementor/admin/localize_settings', function( array $settings ) { return $this->admin_localize_settings( $settings ); } ); add_filter( 'elementor/template_library/sources/local/register_taxonomy_cpts', function( array $cpts ) { $cpts[] = self::CPT; return $cpts; } ); // In the Landing Pages Admin Table page - Overwrite Template type column header title. add_action( 'manage_' . Landing_Pages_Module::CPT . '_posts_columns', function( $posts_columns ) { /** @var Source_Local $source_local */ $source_local = Plugin::$instance->templates_manager->get_source( 'local' ); return $source_local->admin_columns_headers( $posts_columns ); } ); // In the Landing Pages Admin Table page - Overwrite Template type column row values. add_action( 'manage_' . Landing_Pages_Module::CPT . '_posts_custom_column', function( $column_name, $post_id ) { /** @var Landing_Page $document */ $document = Plugin::$instance->documents->get( $post_id ); $document->admin_columns_content( $column_name ); }, 10, 2 ); // Overwrite the Admin Bar's 'New +' Landing Page URL with the link that creates the new LP in Elementor // with the Template Library modal open. add_action( 'admin_bar_menu', function( $admin_bar ) { // Get the Landing Page menu node. $new_landing_page_node = $admin_bar->get_node( 'new-e-landing-page' ); if ( $new_landing_page_node ) { $new_landing_page_node->href = $this->get_add_new_landing_page_url(); $admin_bar->add_node( $new_landing_page_node ); } }, 100 ); } } landing-pages/admin-menu-items/landing-pages-menu-item.php000064400000001250146731356320017556 0ustar00render_callback = $render_callback; } public function render() { ( $this->render_callback )(); } } landing-pages/documents/landing-page.php000064400000004437146731356320014337 0ustar00print_admin_column_type(); } } protected function get_remote_library_config() { $config = [ 'type' => 'lp', 'default_route' => 'templates/landing-pages', 'autoImportSettings' => true, ]; return array_replace_recursive( parent::get_remote_library_config(), $config ); } } library/module.php000064400000002611146731356320010220 0ustar00documents ->register_document_type( 'not-supported', Documents\Not_Supported::get_class_full_name() ) ->register_document_type( 'page', Documents\Page::get_class_full_name() ) ->register_document_type( 'section', Documents\Section::get_class_full_name() ); $experiments_manager = Plugin::$instance->experiments; // Register `Container` document type only if the experiment is active. if ( $experiments_manager->is_feature_active( 'container' ) ) { Plugin::$instance->documents ->register_document_type( 'container', Documents\Container::get_class_full_name() ); } } } library/documents/not-supported.php000064400000002673146731356320013567 0ustar00 true, ]; return $config; } public function get_content( $with_css = false ) { return do_shortcode( parent::get_content( $with_css ) ); } } library/documents/section.php000064400000001665146731356320012410 0ustar00get_main_id(); } /** * @since 3.1.0 * @access protected */ protected function register_controls() { parent::register_controls(); Post::register_hide_title_control( $this ); Post::register_style_controls( $this ); } protected function get_remote_library_config() { $config = parent::get_remote_library_config(); $config['type'] = 'page'; $config['default_route'] = 'templates/pages'; return $config; } } library/traits/library.php000064400000002021146731356320011700 0ustar00get_name() ); //PHPCS - Not a user input printf( '%s', $admin_filter_url, $this->get_title() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Save document type. * * Set new/updated document type. * * @since 3.1.0 * @access public */ public function save_template_type() { parent::save_template_type(); wp_set_object_terms( $this->post->ID, $this->get_name(), Source_Local::TAXONOMY_TYPE_SLUG ); } } library/user-favorites.php000064400000005265146731356320011721 0ustar00user_id = $user_id; } /** * @param null $vendor * @param null $resource * @param false $ignore_cache * * @return array */ public function get( $vendor = null, $resource = null, $ignore_cache = false ) { if ( $ignore_cache || empty( $this->cache ) ) { $this->cache = get_user_meta( $this->user_id, self::USER_META_KEY, true ); } if ( ! $this->cache || ! is_array( $this->cache ) ) { return []; } if ( $vendor && $resource ) { $key = $this->get_key( $vendor, $resource ); return isset( $this->cache[ $key ] ) ? $this->cache[ $key ] : []; } return $this->cache; } /** * @param $vendor * @param $resource * @param $id * * @return bool */ public function exists( $vendor, $resource, $id ) { return in_array( $id, $this->get( $vendor, $resource ), true ); } /** * @param $vendor * @param $resource * @param array $value * * @return $this * @throws \Exception */ public function save( $vendor, $resource, $value = [] ) { $all_favorites = $this->get(); $all_favorites[ $this->get_key( $vendor, $resource ) ] = $value; $result = update_user_meta( $this->user_id, self::USER_META_KEY, $all_favorites ); if ( false === $result ) { throw new \Exception( 'Failed to save user favorites.' ); } $this->cache = $all_favorites; return $this; } /** * @param $vendor * @param $resource * @param $id * * @return $this * @throws \Exception */ public function add( $vendor, $resource, $id ) { $favorites = $this->get( $vendor, $resource ); if ( in_array( $id, $favorites, true ) ) { return $this; } $favorites[] = $id; $this->save( $vendor, $resource, $favorites ); return $this; } /** * @param $vendor * @param $resource * @param $id * * @return $this * @throws \Exception */ public function remove( $vendor, $resource, $id ) { $favorites = $this->get( $vendor, $resource ); if ( ! in_array( $id, $favorites, true ) ) { return $this; } $favorites = array_filter( $favorites, function ( $item ) use ( $id ) { return $item !== $id; } ); $this->save( $vendor, $resource, $favorites ); return $this; } /** * @param $vendor * @param $resource * * @return string */ private function get_key( $vendor, $resource ) { return "{$vendor}/{$resource}"; } } generator-tag/module.php000064400000004460146731356320011317 0ustar00get_generator_content(); echo '' . PHP_EOL; } private function get_generator_content(): string { $active_features = $this->get_active_features(); $settings = $this->get_generator_tag_settings(); $tags = [ 'Elementor ' . ELEMENTOR_VERSION, ]; if ( ! empty( $active_features ) ) { $tags[] = 'features: ' . implode( ', ', $active_features ); } if ( ! empty( $settings ) ) { $tags[] = 'settings: ' . implode( ', ', $settings ); } return implode( '; ', $tags ); } private function get_active_features(): array { $active_features = []; foreach ( Plugin::$instance->experiments->get_active_features() as $feature_slug => $feature ) { if ( isset( $feature['generator_tag'] ) && $feature['generator_tag'] ) { $active_features[] = $feature_slug; } } return $active_features; } private function get_generator_tag_settings(): array { return apply_filters( 'elementor/generator_tag/settings', [] ); } public function register_admin_settings( Settings $settings ) { $settings->add_field( Settings::TAB_ADVANCED, Settings::TAB_ADVANCED, 'meta_generator_tag', [ 'label' => esc_html__( 'Generator Tag', 'elementor' ), 'field_args' => [ 'type' => 'select', 'std' => '', 'options' => [ '' => esc_html__( 'Enable', 'elementor' ), '1' => esc_html__( 'Disable', 'elementor' ), ], 'desc' => esc_html__( 'A generator tag is a meta element that indicates the attributes used to create a webpage. It is used for analytical purposes.', 'elementor' ), ], ] ); } } web-cli/module.php000064400000002125146731356320010076 0ustar00get_js_assets_url( 'web-cli' ), [ 'jquery', ], ELEMENTOR_VERSION, true ); $this->print_config( 'elementor-web-cli' ); } protected function get_init_settings() { return [ 'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ), 'urls' => [ 'rest' => get_rest_url(), 'assets' => ELEMENTOR_ASSETS_URL, ], 'nonce' => wp_create_nonce( 'wp_rest' ), 'version' => ELEMENTOR_VERSION, ]; } } site-navigation/rest-fields/page-user-can.php000064400000001647146731356320015250 0ustar00 [ $this, 'get_callback' ], 'schema' => [ 'description' => __( 'Whether the current user can edit or delete this post', 'elementor' ), 'type' => 'array', ], ] ); } public function get_callback( $post ) { $can_edit = current_user_can( 'edit_post', $post['id'] ); $can_delete = current_user_can( 'delete_post', $post['id'] ); return [ 'edit' => $can_edit, 'delete' => $can_delete, ]; } } site-navigation/module.php000064400000003774146731356320011670 0ustar00data_manager_v2->register_controller( new Controller() ); $is_tests = Utils::is_elementor_tests(); $is_v2_experiment_on = Plugin::$instance->experiments->is_feature_active( 'editor_v2' ); if ( ! $is_v2_experiment_on && ! $is_tests ) { return; } $this->register_pages_panel_experiment(); if ( Plugin::$instance->experiments->is_feature_active( self::PAGES_PANEL_EXPERIMENT_NAME ) ) { add_filter( 'elementor/editor/v2/scripts/env', function( $env ) { $env['@elementor/editor-site-navigation'] = [ 'is_pages_panel_active' => true, ]; return $env; } ); $this->register_rest_fields(); } } /** * Retrieve the module name. * * @return string */ public function get_name() { return 'site-navigation'; } /** * Register Experiment * * @since 3.16.0 * * @return void * @throws \Exception */ private function register_pages_panel_experiment() { Plugin::$instance->experiments->add_feature( [ 'name' => self::PAGES_PANEL_EXPERIMENT_NAME, 'title' => esc_html__( 'Pages Panel', 'elementor' ), 'release_status' => Experiments_Manager::RELEASE_STATUS_ALPHA, 'default' => Experiments_Manager::STATE_INACTIVE, 'hidden' => true, 'dependencies' => [ 'editor_v2', ], ] ); } private function register_rest_fields() { add_action( 'rest_api_init', function() { ( new Page_User_Can() )->register_rest_field(); } ); } } site-navigation/data/controller.php000064400000002761146731356320013472 0ustar00get_items_permissions_check( $request ); } public function create_item_permissions_check( $request ): bool { return $this->create_items_permissions_check( $request ); } public function register_endpoints() { $this->register_endpoint( new Recent_Posts( $this ) ); $this->register_endpoint( new Add_New_Post( $this ) ); if ( Plugin::$instance->experiments->is_feature_active( 'pages_panel' ) ) { $this->register_endpoint( new Duplicate_Post( $this ) ); $this->register_endpoint( new Homepage( $this ) ); } } protected function register_index_endpoint() { // Bypass, currently does not required. } } site-navigation/data/endpoints/add-new-post.php000064400000004174146731356320015614 0ustar00 [ 'description' => 'Post type to create', 'type' => 'string', 'required' => false, 'default' => 'post', 'sanitize_callback' => function ( $value ) { return sanitize_text_field( $value ); }, 'validate_callback' => 'rest_validate_request_arg', ], ]; $this->register_items_route( \WP_REST_Server::CREATABLE, $args ); } public function get_name() { return 'add-new-post'; } public function get_format() { return 'site-navigation/add-new-post'; } public function create_items( $request ) { $post_type = $request->get_param( 'post_type' ); if ( ! $this->validate_post_type( $post_type ) ) { return new \WP_Error( 400, sprintf( 'Post type %s does not exist.', $post_type ), [ 'status' => 400 ] ); } if ( ! User::is_current_user_can_edit_post_type( $post_type ) ) { return new \WP_Error( 401, sprintf( 'User dont have capability to create page of type - %s.', $post_type ), [ 'status' => 401 ] ); } // Temporary solution for the fact that documents creation not using the actual registered post types. $post_type = $this->map_post_type( $post_type ); $document = Plugin::$instance->documents->create( $post_type ); if ( is_wp_error( $document ) ) { return new \WP_Error( 500, sprintf( 'Error while creating %s.', $post_type ) ); } return [ 'id' => $document->get_main_id(), 'edit_url' => $document->get_edit_url(), ]; } private function validate_post_type( $post_type ): bool { $post_types = get_post_types(); return in_array( $post_type, $post_types ); } /** * Map post type to Elementor document type. * * @param $post_type * * @return string */ private function map_post_type( $post_type ): string { $post_type_map = [ 'page' => 'wp-page', 'post' => 'wp-post', ]; return $post_type_map[ $post_type ] ?? $post_type; } } site-navigation/data/endpoints/duplicate-post.php000064400000007361146731356320016250 0ustar00 [ 'description' => 'Post id to duplicate', 'type' => 'integer', 'required' => true, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ], 'title' => [ 'description' => 'Post title', 'type' => 'string', 'required' => false, 'sanitize_callback' => function ( $value ) { return sanitize_text_field( $value ); }, 'validate_callback' => 'rest_validate_request_arg', ], ]; $this->register_items_route( \WP_REST_Server::CREATABLE, $args ); } public function get_name() { return 'duplicate-post'; } public function get_format() { return 'site-navigation/duplicate-post'; } public function create_items( $request ) { $post_id = $request->get_param( 'post_id' ); $post_title = $request->get_param( 'title' ); $post = get_post( $post_id ); if ( ! User::is_current_user_can_edit_post_type( $post->post_type ) ) { return new \WP_Error( 401, sprintf( 'User dont have capability to create page of type - %s.', $post->post_type ), [ 'status' => 401 ] ); } if ( ! $post ) { return new \WP_Error( 500, 'Post not found' ); } $new_post_id = $this->duplicate_post( $post, $post_title ); if ( is_wp_error( $new_post_id ) ) { return new \WP_Error( 500, 'Error while duplicating post.' ); } //Duplicate all post meta $this->duplicate_post_meta( $post_id, $new_post_id ); //Duplicate all taxonomies $this->duplicate_post_taxonomies( $post_id, $new_post_id ); return [ 'post_id' => $new_post_id, ]; } /** * Duplicate post * * @param $post * * @return int|\WP_Error */ private function duplicate_post( $post, $post_title ) { $post_status = 'draft'; $current_user = wp_get_current_user(); $new_post_author = $current_user->ID; $args = [ 'comment_status' => $post->comment_status, 'ping_status' => $post->ping_status, 'post_author' => $new_post_author, 'post_content' => $post->post_content, 'post_excerpt' => $post->post_excerpt, 'post_parent' => $post->post_parent, 'post_password' => $post->post_password, 'post_status' => $post_status, 'post_title' => $post_title, 'post_type' => $post->post_type, 'to_ping' => $post->to_ping, 'menu_order' => $post->menu_order, ]; return wp_insert_post( $args ); } /** * Duplicate the associated post meta to the new post ID. * * @param int $post_id * @param int $new_post_id */ private function duplicate_post_meta( int $post_id, int $new_post_id ) { $post_meta = get_post_meta( $post_id ); if ( empty( $post_meta ) || ! is_array( $post_meta ) ) { return; } foreach ( $post_meta as $key => $values ) { if ( '_wp_old_slug' === $key ) { // Ignore this meta key continue; } foreach ( $values as $value ) { $value = maybe_unserialize( $value ); add_post_meta( $new_post_id, $key, wp_slash( $value ) ); } } } /** * duplicate_post_taxonomies * * @param int $post_id * @param int $new_post_id */ private function duplicate_post_taxonomies( $post_id, $new_post_id ) { $taxonomies = array_map( 'sanitize_text_field', get_object_taxonomies( get_post_type( $post_id ) ) ); if ( empty( $taxonomies ) || ! is_array( $taxonomies ) ) { return; } foreach ( $taxonomies as $taxonomy ) { $post_terms = wp_get_object_terms( $post_id, $taxonomy, [ 'fields' => 'slugs' ] ); if ( ! is_wp_error( $post_terms ) ) { wp_set_object_terms( $new_post_id, $post_terms, $taxonomy, false ); } } } } site-navigation/data/endpoints/recent-posts.php000064400000004762146731356320015743 0ustar00 [ 'description' => 'Number of posts to return', 'type' => 'integer', 'required' => true, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ], 'post_type' => [ 'description' => 'Post types to retrieve', 'type' => 'array', 'required' => false, 'default' => [ 'page', 'post', Source_Local::CPT ], 'sanitize_callback' => 'rest_sanitize_array', 'validate_callback' => 'rest_validate_request_arg', ], 'post__not_in' => [ 'description' => 'Post id`s to exclude', 'type' => 'array', 'required' => [], 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ], ]; parent::register_items_route( $methods, $args ); } public function get_name() { return 'recent-posts'; } public function get_format() { return 'site-navigation/recent-posts'; } public function get_items( $request ) { $args = [ 'posts_per_page' => $request->get_param( 'posts_per_page' ), 'post_type' => $request->get_param( 'post_type' ), 'fields' => 'ids', 'meta_query' => [ [ 'key' => Document::TYPE_META_KEY, 'value' => Kit::get_type(), // Exclude kits. 'compare' => '!=', ], ], ]; $exclude = $request->get_param( 'post__not_in' ); if ( ! empty( $exclude ) ) { $args['post__not_in'] = $exclude; } $recently_edited_query = Utils::get_recently_edited_posts_query( $args ); $recent = []; foreach ( $recently_edited_query->posts as $id ) { $document = Plugin::$instance->documents->get( $id ); $recent[] = [ 'id' => $id, 'title' => get_the_title( $id ), 'edit_url' => $document->get_edit_url(), 'date_modified' => get_post_timestamp( $id, 'modified' ), 'type' => [ 'post_type' => get_post_type( $id ), 'doc_type' => $document->get_name(), 'label' => $document->get_title(), ], 'user_can' => [ 'edit' => current_user_can( 'edit_post', $id ), ], ]; } return $recent; } } site-navigation/data/endpoints/homepage.php000064400000001214146731356320015067 0ustar00
get_css_assets_url( 'admin-top-bar', null, 'default', true ), [], ELEMENTOR_VERSION ); /** * Before admin top bar enqueue scripts. * * Fires before Elementor admin top bar scripts are enqueued. * * @since 3.19.0 */ do_action( 'elementor/admin_top_bar/before_enqueue_scripts', $this ); wp_enqueue_script( 'elementor-admin-top-bar', $this->get_js_assets_url( 'admin-top-bar' ), [ 'elementor-common', 'react', 'react-dom', 'tipsy', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-admin-top-bar', 'elementor' ); $min_suffix = Utils::is_script_debug() ? '' : '.min'; wp_enqueue_script( 'tipsy', ELEMENTOR_ASSETS_URL . 'lib/tipsy/tipsy' . $min_suffix . '.js', [ 'jquery', ], '1.0.0', true ); $this->print_config(); } private function add_frontend_settings() { $settings = []; $settings['is_administrator'] = current_user_can( 'manage_options' ); // TODO: Find a better way to add apps page url to the admin top bar. $settings['apps_url'] = admin_url( 'admin.php?page=elementor-apps' ); $settings['promotion'] = [ 'text' => __( 'Upgrade Now', 'elementor' ), 'url' => 'https://go.elementor.com/wp-dash-admin-top-bar-upgrade/', ]; $settings['promotion'] = Filtered_Promotions_Manager::get_filtered_promotion_data( $settings['promotion'], 'elementor/admin_top_bar/go_pro_promotion', 'url' ); $current_screen = get_current_screen(); /** @var \Elementor\Core\Common\Modules\Connect\Apps\Library $library */ $library = Plugin::$instance->common->get_component( 'connect' )->get_app( 'library' ); if ( $library ) { $settings = array_merge( $settings, [ 'is_user_connected' => $library->is_connected(), 'connect_url' => $library->get_admin_url( 'authorize', [ 'utm_source' => 'top-bar', 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-account', 'utm_content' => $current_screen->id, 'source' => 'generic', ] ), ] ); } $this->set_settings( $settings ); do_action( 'elementor/admin-top-bar/init', $this ); } private function is_top_bar_active() { $current_screen = get_current_screen(); if ( ! $current_screen ) { return false; } $is_elementor_page = strpos( $current_screen->id ?? '', 'elementor' ) !== false; $is_elementor_post_type_page = strpos( $current_screen->post_type ?? '', 'elementor' ) !== false; return apply_filters( 'elementor/admin-top-bar/is-active', $is_elementor_page || $is_elementor_post_type_page, $current_screen ); } /** * Module constructor. */ public function __construct() { parent::__construct(); add_action( 'current_screen', function () { if ( ! $this->is_top_bar_active() ) { return; } $this->add_frontend_settings(); add_action( 'in_admin_header', function () { $this->render_admin_top_bar(); } ); add_action( 'admin_enqueue_scripts', function () { $this->enqueue_scripts(); } ); } ); } } announcements/module.php000064400000010717146731356320011437 0ustar00
get_js_assets_url( 'announcements-app' ), [ 'wp-i18n', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'announcements-app', 'elementor' ); $this->print_config( 'announcements-app' ); } /** * Get initialization settings to use in frontend. * * @return array[] */ protected function get_init_settings(): array { $active_announcements = $this->get_active_announcements(); $additional_settings = []; foreach ( $active_announcements as $announcement ) { $additional_settings[] = $announcement->get_prepared_data(); //@TODO - replace with ajax request from the front after actually triggered $announcement->after_triggered(); } return [ 'announcements' => $additional_settings, ]; } /** * Enqueue the module styles. */ public function enqueue_styles() { wp_enqueue_style( 'announcements-app', $this->get_css_assets_url( 'modules/announcements/announcements' ), [], ELEMENTOR_VERSION ); } /** * Retrieve all announcement in raw format ( array ). * * @return array[] */ private function get_raw_announcements(): array { $raw_announcements = [ [ 'title' => 'Unlock the Power of Elementor AI ', 'description' => '

Design a website true to your brand with natively integrated AI tools.

  • Generate containers using text or any website you reference from the web and get a wireframe layout to start with. Use the container variations capability to bring the wireframe to life with design and content.
  • Let AI write or edit your text in the context of your brand, tone of voice and optimal length. Also generate custom code or CSS that seamlessly integrates into your website.
  • Create one-of-a-kind images, add, or erase content from existing images or expand them beyond their original size and aspect ratio.
  • Use Elementor’s AI History Panel to efficiently access previously-generated text, code or image prompts, and ensure consistency across your site.
', 'media' => [ 'type' => 'image', 'src' => ELEMENTOR_ASSETS_URL . 'images/announcement.png?' . ELEMENTOR_VERSION, ], 'cta' => [ [ 'label' => 'Continue', 'variant' => 'primary', 'target' => '_blank', ], [ 'label' => 'Learn More', 'target' => '_blank', 'url' => 'https://go.elementor.com/whats-new-popup-learn-elementor-ai/', ], ], 'triggers' => [ [ 'action' => 'aiStared', ], ], ], ]; // DO NOT USE THIS FILTER return apply_filters( 'elementor/announcements/raw_announcements', $raw_announcements ); } /** * Retrieve all announcement objects. * * @return array */ private function get_announcements(): array { $announcements = []; foreach ( $this->get_raw_announcements() as $announcement_data ) { $announcements[] = new Announcement( $announcement_data ); } return $announcements; } /** * Retrieve all active announcement objects. * * @return array */ private function get_active_announcements(): array { $active_announcements = []; foreach ( $this->get_announcements() as $announcement ) { if ( $announcement->is_active() ) { $active_announcements[] = $announcement; } } return $active_announcements; } public function __construct() { parent::__construct(); add_action( 'elementor/init', [ $this, 'on_elementor_init' ] ); } public function on_elementor_init() { if ( empty( $this->get_active_announcements() ) ) { return; } add_action( 'elementor/editor/footer', function () { $this->render_app_wrapper(); } ); add_action( 'elementor/editor/after_enqueue_scripts', function () { $this->enqueue_scripts(); $this->enqueue_styles(); } ); } } announcements/classes/announcement.php000064400000002362146731356320014276 0ustar00raw_data = $data; $this->set_triggers(); } /** * @return array */ protected function get_triggers(): array { return $this->triggers; } protected function set_triggers() { $triggers = $this->raw_data['triggers'] ?? []; foreach ( $triggers as $trigger ) { $this->triggers[] = Utils::get_trigger_object( $trigger ); } } /** * is_active * @return bool */ public function is_active(): bool { $triggers = $this->get_triggers(); if ( empty( $triggers ) ) { return true; } foreach ( $triggers as $trigger ) { if ( ! $trigger->is_active() ) { return false; } } return true; } public function after_triggered() { foreach ( $this->get_triggers() as $trigger ) { if ( $trigger->is_active() ) { $trigger->after_triggered(); } } } /** * @return array */ public function get_prepared_data(): array { $raw_data = $this->raw_data; unset( $raw_data['triggers'] ); return $raw_data; } } announcements/classes/utils.php000064400000001435146731356320012744 0ustar00name; } /** * @return bool */ public function is_active(): bool { return true; } public function after_triggered() { } } announcements/triggers/ai-started.php000064400000001152146731356320014026 0ustar00 $this->name ] ); } /** * @return bool */ public function is_active(): bool { return ! User::get_introduction_meta( 'ai_get_started' ) && ! User::get_introduction_meta( $this->name ); } } announcements/triggers/is-flex-container-inactive.php000064400000002241146731356320017120 0ustar00get_user_announcement_count(); return ! empty( $user_counter ) ? (int) $user_counter : 0; } public function after_triggered() { $new_counter = $this->get_view_count() + 1; update_user_meta( get_current_user_id(), self::USER_META_KEY, $new_counter ); } /** * @return bool */ public function is_active(): bool { $is_feature_active = Plugin::$instance->experiments->is_feature_active( 'container' ); $counter = $this->get_user_announcement_count(); return ! $is_feature_active && (int) $counter < 1; } /** * @return string */ private function get_user_announcement_count(): string { return get_user_meta( get_current_user_id(), self::USER_META_KEY, true ); } } editor-events/module.php000064400000002160146731356320011343 0ustar00 $can_send_events, 'elementor_version' => ELEMENTOR_VERSION, 'site_url' => hash( 'sha256', get_site_url() ), 'wp_version' => get_bloginfo( 'version' ), 'user_agent' => esc_html( Utils::get_super_global_value( $_SERVER, 'HTTP_USER_AGENT' ) ), 'site_language' => get_locale(), 'site_key' => get_option( Base_App::OPTION_CONNECT_SITE_KEY ), 'subscription_id' => null, ]; if ( $can_send_events ) { $settings['data_system_url'] = self::ELEMENTOR_EDITOR_EVENTS_DATA_SYSTEM_URL; } return $settings; } } dynamic-tags/module.php000064400000005725146731356320011145 0ustar00register_groups(); add_action( 'elementor/dynamic_tags/register', [ $this, 'register_tags' ] ); } /** * Get module name. * * Retrieve the dynamic tags module name. * * @since 2.0.0 * @access public * * @return string Module name. */ public function get_name() { return 'dynamic_tags'; } /** * Get classes names. * * Retrieve the dynamic tag classes names. * * @since 2.0.0 * @access public * * @return array Tag dynamic tag classes names. */ public function get_tag_classes_names() { return []; } /** * Get groups. * * Retrieve the dynamic tag groups. * * @since 2.0.0 * @access public * * @return array Tag dynamic tag groups. */ public function get_groups() { return [ self::BASE_GROUP => [ 'title' => 'Base Tags', ], ]; } /** * Register groups. * * Add all the available tag groups. * * @since 2.0.0 * @access private */ private function register_groups() { foreach ( $this->get_groups() as $group_name => $group_settings ) { Plugin::$instance->dynamic_tags->register_group( $group_name, $group_settings ); } } /** * Register tags. * * Add all the available dynamic tags. * * @since 2.0.0 * @access public * * @param Manager $dynamic_tags */ public function register_tags( $dynamic_tags ) { foreach ( $this->get_tag_classes_names() as $tag_class ) { /** @var Base_Tag $class_name */ $class_name = $this->get_reflection()->getNamespaceName() . '\Tags\\' . $tag_class; $dynamic_tags->register( new $class_name() ); } } } lazyload/module.php000064400000006502146731356320010376 0ustar00 static::EXPERIMENT_NAME, 'title' => esc_html__( 'Lazy Load Background Images', 'elementor' ), 'tag' => esc_html__( 'Performance', 'elementor' ), 'description' => esc_html__( 'Lazy loading images that are not in the viewport improves initial page load performance and user experience. By activating this experiment all background images except the first one on your page will be lazy loaded to improve your LCP score', 'elementor' ), 'release_status' => Experiments_Manager::RELEASE_STATUS_BETA, 'default' => Experiments_Manager::STATE_INACTIVE, 'new_site' => [ 'default_active' => true, 'minimum_installation_version' => '3.21.0', ], 'generator_tag' => true, ]; } public function __construct() { parent::__construct(); add_action( 'init', [ $this, 'init' ] ); } public function init() { add_action( 'wp_head', function() { if ( ! $this->should_lazyload() ) { return; } ?> should_lazyload() ) { return; } ?> preview->is_preview_mode() && ! Plugin::$instance->editor->is_edit_mode(); } } nested-tabs/module.php000064400000001357146731356320010773 0ustar00experiments->is_feature_active( NestedElementsModule::EXPERIMENT_NAME ); } public function get_name() { return 'nested-tabs'; } public function __construct() { parent::__construct(); add_action( 'elementor/editor/before_enqueue_scripts', function () { wp_enqueue_script( $this->get_name(), $this->get_js_assets_url( $this->get_name() ), [ 'nested-elements', ], ELEMENTOR_VERSION, true ); } ); } } nested-tabs/widgets/nested-tabs.php000064400000122213146731356320013360 0ustar00 'container', 'settings' => [ '_title' => sprintf( __( 'Tab #%s', 'elementor' ), $index ), 'content_width' => 'full', ], ]; } protected function get_default_children_elements() { return [ $this->tab_content_container( 1 ), $this->tab_content_container( 2 ), $this->tab_content_container( 3 ), ]; } protected function get_default_repeater_title_setting_key() { return 'tab_title'; } protected function get_default_children_title() { return esc_html__( 'Tab #%d', 'elementor' ); } protected function get_default_children_placeholder_selector() { return '.e-n-tabs-content'; } protected function get_html_wrapper_class() { return 'elementor-widget-n-tabs'; } protected function register_controls() { $start = is_rtl() ? 'right' : 'left'; $end = is_rtl() ? 'left' : 'right'; $start_logical = is_rtl() ? 'end' : 'start'; $end_logical = is_rtl() ? 'start' : 'end'; $heading_selector_non_touch_device = '{{WRAPPER}} > .elementor-widget-container > .e-n-tabs[data-touch-mode="false"] > .e-n-tabs-heading'; $heading_selector_touch_device = '{{WRAPPER}} > .elementor-widget-container > .e-n-tabs[data-touch-mode="true"] > .e-n-tabs-heading'; $heading_selector = '{{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-heading'; $content_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-content ) > .e-con'; $this->start_controls_section( 'section_tabs', [ 'label' => esc_html__( 'Tabs', 'elementor' ), ] ); $repeater = new Repeater(); $repeater->add_control( 'tab_title', [ 'label' => esc_html__( 'Title', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Tab Title', 'elementor' ), 'placeholder' => esc_html__( 'Tab Title', 'elementor' ), 'label_block' => true, 'dynamic' => [ 'active' => true, ], ] ); $repeater->add_control( 'tab_icon', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'skin' => 'inline', 'label_block' => false, ] ); $repeater->add_control( 'tab_icon_active', [ 'label' => esc_html__( 'Active Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'skin' => 'inline', 'label_block' => false, 'condition' => [ 'tab_icon[value]!' => '', ], ] ); $repeater->add_control( 'element_id', [ 'label' => esc_html__( 'CSS ID', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'ai' => [ 'active' => false, ], 'dynamic' => [ 'active' => true, ], 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ), 'style_transfer' => false, 'classes' => 'elementor-control-direction-ltr', ] ); $this->add_control( 'tabs', [ 'label' => esc_html__( 'Tabs Items', 'elementor' ), 'type' => Control_Nested_Repeater::CONTROL_TYPE, 'fields' => $repeater->get_controls(), 'default' => [ [ 'tab_title' => esc_html__( 'Tab #1', 'elementor' ), ], [ 'tab_title' => esc_html__( 'Tab #2', 'elementor' ), ], [ 'tab_title' => esc_html__( 'Tab #3', 'elementor' ), ], ], 'title_field' => '{{{ tab_title }}}', 'button_text' => 'Add Tab', ] ); $styling_block_start = '--n-tabs-direction: column; --n-tabs-heading-direction: row; --n-tabs-heading-width: initial; --n-tabs-title-flex-basis: content; --n-tabs-title-flex-shrink: 0;'; $styling_block_end = '--n-tabs-direction: column-reverse; --n-tabs-heading-direction: row; --n-tabs-heading-width: initial; --n-tabs-title-flex-basis: content; --n-tabs-title-flex-shrink: 0'; $styling_inline_end = '--n-tabs-direction: row-reverse; --n-tabs-heading-direction: column; --n-tabs-heading-width: 240px; --n-tabs-title-flex-basis: initial; --n-tabs-title-flex-shrink: initial;'; $styling_inline_start = '--n-tabs-direction: row; --n-tabs-heading-direction: column; --n-tabs-heading-width: 240px; --n-tabs-title-flex-basis: initial; --n-tabs-title-flex-shrink: initial;'; $this->add_responsive_control( 'tabs_direction', [ 'label' => esc_html__( 'Direction', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'block-start' => [ 'title' => esc_html__( 'Above', 'elementor' ), 'icon' => 'eicon-v-align-top', ], 'block-end' => [ 'title' => esc_html__( 'Below', 'elementor' ), 'icon' => 'eicon-v-align-bottom', ], 'inline-end' => [ 'title' => esc_html__( 'After', 'elementor' ), 'icon' => 'eicon-h-align-' . $end, ], 'inline-start' => [ 'title' => esc_html__( 'Before', 'elementor' ), 'icon' => 'eicon-h-align-' . $start, ], ], 'separator' => 'before', 'selectors_dictionary' => [ 'block-start' => $styling_block_start, 'block-end' => $styling_block_end, 'inline-end' => $styling_inline_end, 'inline-start' => $styling_inline_start, // Styling duplication for BC reasons. 'top' => $styling_block_start, 'bottom' => $styling_block_end, 'end' => $styling_inline_end, 'start' => $styling_inline_start, ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->add_responsive_control( 'tabs_justify_horizontal', [ 'label' => esc_html__( 'Justify', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => "eicon-align-$start_logical-h", ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-align-center-h', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => "eicon-align-$end_logical-h", ], 'stretch' => [ 'title' => esc_html__( 'Stretch', 'elementor' ), 'icon' => 'eicon-align-stretch-h', ], ], 'selectors_dictionary' => [ 'start' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;', 'center' => '--n-tabs-heading-justify-content: center; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;', 'end' => '--n-tabs-heading-justify-content: flex-end; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;', 'stretch' => '--n-tabs-heading-justify-content: initial; --n-tabs-title-width: 100%; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 1;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'condition' => [ 'tabs_direction' => [ '', 'block-start', 'block-end', 'top', 'bottom', ], ], 'frontend_available' => true, ] ); $this->add_responsive_control( 'tabs_justify_vertical', [ 'label' => esc_html__( 'Justify', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-align-start-v', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-align-center-v', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-align-end-v', ], 'stretch' => [ 'title' => esc_html__( 'Stretch', 'elementor' ), 'icon' => 'eicon-align-stretch-v', ], ], 'selectors_dictionary' => [ 'start' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial; --n-tabs-heading-wrap: wrap; --n-tabs-title-flex-basis: content', 'center' => '--n-tabs-heading-justify-content: center; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial; --n-tabs-heading-wrap: wrap; --n-tabs-title-flex-basis: content', 'end' => '--n-tabs-heading-justify-content: flex-end; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial; --n-tabs-heading-wrap: wrap; --n-tabs-title-flex-basis: content', 'stretch' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: 100%; --n-tabs-title-align-items: center; --n-tabs-heading-wrap: nowrap; --n-tabs-title-flex-basis: auto', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'condition' => [ 'tabs_direction' => [ 'inline-start', 'inline-end', 'start', 'end', ], ], ] ); $this->add_responsive_control( 'tabs_width', [ 'label' => esc_html__( 'Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 50, ], 'px' => [ 'min' => 20, 'max' => 600, ], ], 'default' => [ 'unit' => '%', ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-heading-width: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'tabs_direction' => [ 'inline-start', 'inline-end', 'start', 'end', ], ], ] ); $this->add_responsive_control( 'title_alignment', [ 'label' => esc_html__( 'Align Title', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-text-align-left', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-text-align-center', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-text-align-right', ], ], 'selectors_dictionary' => [ 'start' => '--n-tabs-title-justify-content: flex-start; --n-tabs-title-align-items: flex-start; --n-tabs-title-text-align: start;', 'center' => '--n-tabs-title-justify-content: center; --n-tabs-title-align-items: center; --n-tabs-title-text-align: center;', 'end' => '--n-tabs-title-justify-content: flex-end; --n-tabs-title-align-items: flex-end; --n-tabs-title-text-align: end;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_tabs_responsive', [ 'label' => esc_html__( 'Additional Settings', 'elementor' ), ] ); $this->add_responsive_control( 'horizontal_scroll', [ 'label' => esc_html__( 'Horizontal Scroll', 'elementor' ), 'type' => Controls_Manager::SELECT, 'description' => esc_html__( 'Note: Scroll tabs if they don’t fit into their parent container.', 'elementor' ), 'options' => [ 'disable' => esc_html__( 'Disable', 'elementor' ), 'enable' => esc_html__( 'Enable', 'elementor' ), ], 'default' => 'disable', 'selectors_dictionary' => [ 'disable' => '--n-tabs-heading-wrap: wrap; --n-tabs-heading-overflow-x: initial; --n-tabs-title-white-space: initial;', 'enable' => '--n-tabs-heading-wrap: nowrap; --n-tabs-heading-overflow-x: scroll; --n-tabs-title-white-space: nowrap;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'frontend_available' => true, 'condition' => [ 'tabs_direction' => [ '', 'block-start', 'block-end', 'top', 'bottom', ], ], ] ); $dropdown_options = [ 'none' => esc_html__( 'None', 'elementor' ), ]; $excluded_breakpoints = [ 'laptop', 'tablet_extra', 'widescreen', ]; foreach ( Plugin::$instance->breakpoints->get_active_breakpoints() as $breakpoint_key => $breakpoint_instance ) { // Exclude the larger breakpoints from the dropdown selector. if ( in_array( $breakpoint_key, $excluded_breakpoints, true ) ) { continue; } $dropdown_options[ $breakpoint_key ] = sprintf( /* translators: 1: Breakpoint label, 2: `>` character, 3: Breakpoint value. */ esc_html__( '%1$s (%2$s %3$dpx)', 'elementor' ), $breakpoint_instance->get_label(), '>', $breakpoint_instance->get_value() ); } $this->add_control( 'breakpoint_selector', [ 'label' => esc_html__( 'Breakpoint', 'elementor' ), 'type' => Controls_Manager::SELECT, 'description' => esc_html__( 'Note: Choose at which breakpoint tabs will automatically switch to a vertical (“accordion”) layout.', 'elementor' ), 'options' => $dropdown_options, 'default' => 'mobile', 'prefix_class' => 'e-n-tabs-', ] ); $this->end_controls_section(); $this->start_controls_section( 'section_tabs_style', [ 'label' => esc_html__( 'Tabs', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'tabs_title_space_between', [ 'label' => esc_html__( 'Gap between tabs', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 400, ], 'em' => [ 'max' => 40, ], 'rem' => [ 'max' => 40, ], ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'tabs_title_spacing', [ 'label' => esc_html__( 'Distance from content', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 400, ], 'em' => [ 'max' => 40, ], 'rem' => [ 'max' => 40, ], ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->start_controls_tabs( 'tabs_title_style' ); $this->start_controls_tab( 'tabs_title_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'tabs_title_background_color', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => '{{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-heading > .e-n-tab-title[aria-selected="false"]:not( :hover )', 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'selectors' => [ '{{SELECTOR}}' => 'background: {{VALUE}}', ], ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'tabs_title_border', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover )", 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'tabs_title_box_shadow', 'label' => esc_html__( 'Shadow', 'elementor' ), 'separator' => 'after', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover )", ] ); $this->end_controls_tab(); $this->start_controls_tab( 'tabs_title_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'tabs_title_background_color_hover', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'background' => [ 'default' => 'classic', ], 'color' => [ 'global' => [ 'default' => Global_Colors::COLOR_ACCENT, ], 'label' => esc_html__( 'Background Color', 'elementor' ), 'selectors' => [ '{{SELECTOR}}' => 'background: {{VALUE}};', ], ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'tabs_title_border_hover', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'tabs_title_box_shadow_hover', 'label' => esc_html__( 'Shadow', 'elementor' ), 'separator' => 'after', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", ] ); $this->add_control( 'hover_animation', [ 'label' => esc_html__( 'Hover Animation', 'elementor' ), 'type' => Controls_Manager::HOVER_ANIMATION, ] ); $this->add_control( 'tabs_title_transition_duration', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)', 'type' => Controls_Manager::SLIDER, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-transition: {{SIZE}}s', ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 3, 'step' => 0.1, ], ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'tabs_title_active', [ 'label' => esc_html__( 'Active', 'elementor' ), ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'tabs_title_background_color_active', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'background' => [ 'default' => 'classic', ], 'color' => [ 'global' => [ 'default' => Global_Colors::COLOR_ACCENT, ], 'label' => esc_html__( 'Background Color', 'elementor' ), 'selectors' => [ '{{SELECTOR}}' => 'background: {{VALUE}};', ], ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'tabs_title_border_active', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'tabs_title_box_shadow_active', 'label' => esc_html__( 'Shadow', 'elementor' ), 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_responsive_control( 'tabs_title_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'separator' => 'before', 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->add_responsive_control( 'padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-padding-top: {{TOP}}{{UNIT}}; --n-tabs-title-padding-right: {{RIGHT}}{{UNIT}}; --n-tabs-title-padding-bottom: {{BOTTOM}}{{UNIT}}; --n-tabs-title-padding-left: {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_title_style', [ 'label' => esc_html__( 'Titles', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'title_typography', 'global' => [ 'default' => Global_Typography::TYPOGRAPHY_ACCENT, ], 'selector' => "{$heading_selector} > :is( .e-n-tab-title > .e-n-tab-title-text, .e-n-tab-title )", 'fields_options' => [ 'font_size' => [ 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-font-size: {{SIZE}}{{UNIT}}', ], ], ], ] ); $this->start_controls_tabs( 'title_style' ); $this->start_controls_tab( 'title_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'title_text_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => 'title_text_shadow', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover )", 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'title_text_stroke', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover ) :is( span, a, i )", 'fields_options' => [ 'text_stroke_type' => [ 'label' => esc_html__( 'Stroke', 'elementor' ), ], ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'title_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'title_text_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} [data-touch-mode="false"] .e-n-tab-title[aria-selected="false"]:hover' => '--n-tabs-title-color-hover: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => 'title_text_shadow_hover', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'title_text_stroke_hover', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover :is( span, a, i )", 'fields_options' => [ 'text_stroke_type' => [ 'label' => esc_html__( 'Stroke', 'elementor' ), ], ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'title_active', [ 'label' => esc_html__( 'Active', 'elementor' ), ] ); $this->add_control( 'title_text_color_active', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-color-active: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => 'title_text_shadow_active', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'title_text_stroke_active', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"] :is( span, a, i ), {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover :is( span, a, i )", 'fields_options' => [ 'text_stroke_type' => [ 'label' => esc_html__( 'Stroke', 'elementor' ), ], ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); $this->start_controls_section( 'icon_section_style', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $styling_block_start = '--n-tabs-title-direction: column; --n-tabs-icon-order: initial; --n-tabs-title-justify-content-toggle: center; --n-tabs-title-align-items-toggle: initial;'; $styling_block_end = '--n-tabs-title-direction: column; --n-tabs-icon-order: 1; --n-tabs-title-justify-content-toggle: center; --n-tabs-title-align-items-toggle: initial;'; $styling_inline_start = '--n-tabs-title-direction: row; --n-tabs-icon-order: initial; --n-tabs-title-justify-content-toggle: initial; --n-tabs-title-align-items-toggle: center;'; $styling_inline_end = '--n-tabs-title-direction: row; --n-tabs-icon-order: 1; --n-tabs-title-justify-content-toggle: initial; --n-tabs-title-align-items-toggle: center;'; $this->add_responsive_control( 'icon_position', [ 'label' => esc_html__( 'Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'block-start' => [ 'title' => esc_html__( 'Above', 'elementor' ), 'icon' => 'eicon-v-align-top', ], 'inline-end' => [ 'title' => esc_html__( 'After', 'elementor' ), 'icon' => 'eicon-h-align-' . $end, ], 'block-end' => [ 'title' => esc_html__( 'Below', 'elementor' ), 'icon' => 'eicon-v-align-bottom', ], 'inline-start' => [ 'title' => esc_html__( 'Before', 'elementor' ), 'icon' => 'eicon-h-align-' . $start, ], ], 'selectors_dictionary' => [ // The toggle variables for 'align items' and 'justify content' have been added to separate the styling of the two 'flex direction' modes. 'block-start' => $styling_block_start, 'inline-end' => $styling_inline_end, 'block-end' => $styling_block_end, 'inline-start' => $styling_inline_start, // Styling duplication for BC reasons. 'top' => $styling_block_start, 'bottom' => $styling_block_end, 'start' => $styling_inline_start, 'end' => $styling_inline_end, ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->add_responsive_control( 'icon_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'max' => 100, ], 'em' => [ 'max' => 10, ], 'rem' => [ 'max' => 10, ], ], 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-size: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'icon_spacing', [ 'label' => esc_html__( 'Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'max' => 400, ], 'vw' => [ 'max' => 50, 'step' => 0.1, ], ], 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->start_controls_tabs( 'icon_style_states' ); $this->start_controls_tab( 'icon_section_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'icon_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-color: {{VALUE}};', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'icon_section_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'icon_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} [data-touch-mode="false"] .e-n-tab-title[aria-selected="false"]:hover' => '--n-tabs-icon-color-hover: {{VALUE}};', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'icon_section_active', [ 'label' => esc_html__( 'Active', 'elementor' ), ] ); $this->add_control( 'icon_color_active', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-color-active: {{VALUE}};', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); $this->start_controls_section( 'section_box_style', [ 'label' => esc_html__( 'Content', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'box_background_color', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => $content_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Background Color', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'box_border', 'selector' => $content_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_responsive_control( 'box_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ $content_selector => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'box_shadow_box_shadow', 'selector' => $content_selector, 'condition' => [ 'box_height!' => 'height', ], ] ); $this->add_responsive_control( 'box_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ $content_selector => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); } protected function render_tab_titles_html( $item_settings ): string { $setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $item_settings['index'] ); $title = $item_settings['item']['tab_title']; $css_classes = [ 'e-n-tab-title' ]; if ( $item_settings['settings']['hover_animation'] ) { $css_classes[] = 'elementor-animation-' . $item_settings['settings']['hover_animation']; } $this->add_render_attribute( $setting_key, [ 'id' => $item_settings['tab_id'], 'class' => $css_classes, 'aria-selected' => 1 === $item_settings['tab_count'] ? 'true' : 'false', 'data-tab-index' => $item_settings['tab_count'], 'role' => 'tab', 'tabindex' => 1 === $item_settings['tab_count'] ? '0' : '-1', 'aria-controls' => $item_settings['container_id'], 'style' => '--n-tabs-title-order: ' . $item_settings['tab_count'] . ';', ] ); $render_attributes = $this->get_render_attribute_string( $setting_key ); $text_class = $this->get_render_attribute_string( 'tab-title-text' ); $icon_class = $this->get_render_attribute_string( 'tab-icon' ); $icon_html = Icons_Manager::try_get_icon_html( $item_settings['item']['tab_icon'], [ 'aria-hidden' => 'true' ] ); $icon_active_html = $icon_html; if ( $this->is_active_icon_exist( $item_settings['item'] ) ) { $icon_active_html = Icons_Manager::try_get_icon_html( $item_settings['item']['tab_icon_active'], [ 'aria-hidden' => 'true' ] ); } ob_start(); ?> print_child( $item_settings['index'], $item_settings ); return ob_get_clean(); } /** * Print the content area. * * @param int $index * @param array $item_settings */ public function print_child( $index, $item_settings = [] ) { $children = $this->get_children(); $child_ids = []; foreach ( $children as $child ) { $child_ids[] = $child->get_id(); } // Add data-tab-index attribute to the content area. $add_attribute_to_container = function ( $should_render, $container ) use ( $item_settings, $child_ids ) { if ( in_array( $container->get_id(), $child_ids ) ) { $this->add_attributes_to_container( $container, $item_settings ); } return $should_render; }; add_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container, 10, 3 ); $children[ $index ]->print_element(); remove_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container ); } protected function add_attributes_to_container( $container, $item_settings ) { $container->add_render_attribute( '_wrapper', [ 'id' => $item_settings['container_id'], 'role' => 'tabpanel', 'aria-labelledby' => $item_settings['tab_id'], 'data-tab-index' => $item_settings['tab_count'], 'style' => '--n-tabs-title-order: ' . $item_settings['tab_count'] . ';', 'class' => 0 === $item_settings['index'] ? 'e-active' : '', ] ); } protected function render() { $settings = $this->get_settings_for_display(); $widget_number = substr( $this->get_id_int(), 0, 3 ); if ( ! empty( $settings['link'] ) ) { $this->add_link_attributes( 'elementor-tabs', $settings['link'] ); } $this->add_render_attribute( 'elementor-tabs', [ 'class' => 'e-n-tabs', 'data-widget-number' => $widget_number, 'aria-label' => esc_html__( 'Tabs. Open items with Enter or Space, close with Escape and navigate using the Arrow keys.', 'elementor' ), ] ); $this->add_render_attribute( 'tab-title-text', 'class', 'e-n-tab-title-text' ); $this->add_render_attribute( 'tab-icon', 'class', 'e-n-tab-icon' ); $this->add_render_attribute( 'tab-icon-active', 'class', [ 'e-n-tab-icon' ] ); $tab_titles_html = ''; $tab_containers_html = ''; foreach ( $settings['tabs'] as $index => $item ) { $tab_count = $index + 1; $tab_id = empty( $item['element_id'] ) ? 'e-n-tabs-title-' . $widget_number . $tab_count : $item['element_id']; $item_settings = [ 'index' => $index, 'tab_count' => $tab_count, 'tab_id' => $tab_id, 'container_id' => 'e-n-tab-content-' . $widget_number . $tab_count, 'widget_number' => $widget_number, 'item' => $item, 'settings' => $settings, ]; $tab_titles_html .= $this->render_tab_titles_html( $item_settings ); $tab_containers_html .= $this->render_tab_containers_html( $item_settings ); } ?>
print_render_attribute_string( 'elementor-tabs' ); ?>>
experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) { return array_merge( parent::get_initial_config(), [ 'support_improved_repeaters' => true, 'target_container' => [ '.e-n-tabs-heading' ], 'node' => 'button', ] ); } return parent::get_initial_config(); } protected function content_template_single_repeater_item() { ?> <# const tabCount = view.collection.length + 1, elementUid = view.getIDInt().toString().substring( 0, 3 ) + tabCount, tabIcon = elementor.helpers.renderIcon( view, data.tab_icon, { 'aria-hidden': true }, 'i' , 'object' ); let tabActiveIcon = tabIcon, tabId = 'e-n-tab-title-' + elementUid; if ( '' !== data.tab_icon_active.value ) { tabActiveIcon = elementor.helpers.renderIcon( view, data.tab_icon_active, { 'aria-hidden': true }, 'i' , 'object' ); } const tabWrapperKey = { 'id': 'e-n-tab-title-' + elementUid, 'class': [ 'e-n-tab-title' ], 'data-tab-index': tabCount, 'role': 'tab', 'aria-selected': 1 === tabCount ? 'true' : 'false', 'tabindex': 1 === tabCount ? '0' : '-1', 'aria-controls': 'e-n-tab-content-' + elementUid, 'style': '--n-tabs-title-order: ' + tabCount + ';', }; const tabIconKey = { 'class': [ 'e-n-tab-icon' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'tabs', 'data-binding-setting': [ data.tab_icon.value, data.tab_icon_active.value ], 'data-binding-index': tabCount, }; const tabTitleKey = { 'class': [ 'e-n-tab-title-text' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'tabs', 'data-binding-setting': [ 'tab_title' ], 'data-binding-index': tabCount, }; view.addRenderAttribute( 'button-container', tabWrapperKey, null, true ); view.addRenderAttribute( 'tab-title-container', tabTitleKey, null, true ); view.addRenderAttribute( 'tab-icon-key-container', tabIconKey, null, true ); #> <# const elementUid = view.getIDInt().toString().substr( 0, 3 ); #>
<# if ( settings['tabs'] ) { #>
<# _.each( settings['tabs'], function( item, index ) { const tabCount = index + 1, tabUid = elementUid + tabCount, tabWrapperKey = tabUid, tabTitleKey = 'tab-title-' + tabUid, tabIconKey = 'tab-icon-' + tabUid, tabIcon = elementor.helpers.renderIcon( view, item.tab_icon, { 'aria-hidden': true }, 'i' , 'object' ), hoverAnimationClass = settings['hover_animation'] ? `elementor-animation-${ settings['hover_animation'] }` : ''; let tabActiveIcon = tabIcon, tabId = 'e-n-tab-title-' + tabUid; if ( '' !== item.tab_icon_active.value ) { tabActiveIcon = elementor.helpers.renderIcon( view, item.tab_icon_active, { 'aria-hidden': true }, 'i' , 'object' ); } if ( '' !== item.element_id ) { tabId = item.element_id; } view.addRenderAttribute( tabWrapperKey, { 'id': tabId, 'class': [ 'e-n-tab-title',hoverAnimationClass ], 'data-tab-index': tabCount, 'role': 'tab', 'aria-selected': 1 === tabCount ? 'true' : 'false', 'tabindex': 1 === tabCount ? '0' : '-1', 'aria-controls': 'e-n-tab-content-' + tabUid, 'style': '--n-tabs-title-order: ' + tabCount + ';', } ); view.addRenderAttribute( tabTitleKey, { 'class': [ 'e-n-tab-title-text' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'tabs', 'data-binding-setting': [ 'tab_title' ], 'data-binding-index': tabCount, } ); view.addRenderAttribute( tabIconKey, { 'class': [ 'e-n-tab-icon' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'tabs', 'data-binding-setting': [ 'tab_icon.value', 'tab_icon_active.value' ], 'data-binding-index': tabCount, } ); #> <# } ); #>
<# } #>
get_js_assets_url( 'kit-elements-defaults-editor' ), [ 'elementor-common', 'elementor-editor-modules', 'elementor-editor-document', 'wp-i18n', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-kit-elements-defaults-editor', 'elementor' ); } public function __construct() { parent::__construct(); add_action( 'elementor/editor/before_enqueue_scripts', function () { $this->enqueue_scripts(); } ); Plugin::$instance->data_manager_v2->register_controller( new Controller() ); ( new Usage() )->register(); if ( is_admin() ) { ( new Import_Export() )->register(); } } } kit-elements-defaults/import-export/import-export.php000064400000001553146731356320017163 0ustar00register( new Export_Runner() ); } ); add_action( 'elementor/import-export/import-kit', function ( Import $import ) { $import->register( new Import_Runner() ); } ); } } kit-elements-defaults/import-export/runners/export.php000064400000003252146731356320017345 0ustar00kits_manager->get_active_kit(); if ( ! $kit ) { return [ 'manifest' => [], 'files' => [], ]; } $default_values = $kit->get_json_meta( Module::META_KEY ); if ( ! $default_values ) { return [ 'manifest' => [], 'files' => [], ]; } $sanitizer = new Settings_Sanitizer( Plugin::$instance->elements_manager, array_keys( Plugin::$instance->widgets_manager->get_widget_types() ) ); $default_values = ( new Collection( $default_values ) ) ->map( function ( $settings, $type ) use ( $sanitizer, $kit ) { return $sanitizer ->for( $type ) ->using( $settings ) ->remove_invalid_settings() ->kses_deep() ->prepare_for_export( $kit ) ->get(); } ) ->all(); return [ 'files' => [ 'path' => Import_Export::FILE_NAME, 'data' => $default_values, ], ]; } } kit-elements-defaults/import-export/runners/import.php000064400000004146146731356320017341 0ustar00kits_manager->get_active_kit(); $file_name = Import_Export::FILE_NAME; $default_values = ImportExportUtils::read_json_file( "{$data['extracted_directory_path']}/{$file_name}.json" ); if ( ! $kit || ! $default_values ) { return []; } $element_types = array_keys( Plugin::$instance->elements_manager->get_element_types() ); $widget_types = array_keys( Plugin::$instance->widgets_manager->get_widget_types() ); $types = array_merge( $element_types, $widget_types ); $sanitizer = new Settings_Sanitizer( Plugin::$instance->elements_manager, $widget_types ); $default_values = ( new Collection( $default_values ) ) ->filter( function ( $settings, $type ) use ( $types ) { return in_array( $type, $types, true ); } ) ->map( function ( $settings, $type ) use ( $sanitizer, $kit ) { return $sanitizer ->for( $type ) ->using( $settings ) ->remove_invalid_settings() ->kses_deep() ->prepare_for_import( $kit ) ->get(); } ) ->all(); $kit->update_json_meta( Module::META_KEY, $default_values ); return $default_values; } } kit-elements-defaults/data/controller.php000064400000007453146731356320014602 0ustar00index_endpoint->register_item_route(\WP_REST_Server::EDITABLE, [ 'id_arg_name' => 'type', 'id_arg_type_regex' => '[\w\-\_]+', 'type' => [ 'type' => 'string', 'description' => 'The type of the element.', 'required' => true, 'validate_callback' => function( $type ) { return $this->validate_type( $type ); }, ], 'settings' => [ 'description' => 'All the default values for the requested type', 'required' => true, 'type' => 'object', 'validate_callback' => function( $settings ) { return is_array( $settings ); }, 'sanitize_callback' => function( $settings, \WP_REST_Request $request ) { $sanitizer = new Settings_Sanitizer( Plugin::$instance->elements_manager, array_keys( Plugin::$instance->widgets_manager->get_widget_types() ) ); return $sanitizer ->for( $request->get_param( 'type' ) ) ->using( $settings ) ->remove_invalid_settings() ->kses_deep() ->get(); }, ], ] ); $this->index_endpoint->register_item_route(\WP_REST_Server::DELETABLE, [ 'id_arg_name' => 'type', 'id_arg_type_regex' => '[\w\-\_]+', 'type' => [ 'type' => 'string', 'description' => 'The type of the element.', 'required' => true, 'validate_callback' => function( $type ) { return $this->validate_type( $type ); }, ], ] ); } public function get_collection_params() { return []; } public function get_items( $request ) { $this->validate_kit(); $kit = Plugin::$instance->kits_manager->get_active_kit(); return (object) $kit->get_json_meta( Module::META_KEY ); } public function update_item( $request ) { $this->validate_kit(); $kit = Plugin::$instance->kits_manager->get_active_kit(); $data = $kit->get_json_meta( Module::META_KEY ); $data[ $request->get_param( 'type' ) ] = $request->get_param( 'settings' ); $kit->update_json_meta( Module::META_KEY, $data ); return (object) []; } public function delete_item( $request ) { $this->validate_kit(); $kit = Plugin::$instance->kits_manager->get_active_kit(); $data = $kit->get_json_meta( Module::META_KEY ); unset( $data[ $request->get_param( 'type' ) ] ); $kit->update_json_meta( Module::META_KEY, $data ); return (object) []; } private function validate_kit() { $kit = Plugin::$instance->kits_manager->get_active_kit(); $is_valid_kit = $kit && $kit->get_main_id(); if ( ! $is_valid_kit ) { throw new Error_404( 'Kit doesn\'t exist.' ); } } private function validate_type( $param ) { $element_types = array_keys( Plugin::$instance->elements_manager->get_element_types() ); $widget_types = array_keys( Plugin::$instance->widgets_manager->get_widget_types() ); return in_array( $param, array_merge( $element_types, $widget_types ), true ); } public function get_items_permissions_check( $request ) { return current_user_can( 'edit_posts' ); } // TODO: Should be removed once the infra will support it. public function get_item_permissions_check( $request ) { return $this->get_items_permissions_check( $request ); } public function update_item_permissions_check( $request ) { return current_user_can( 'manage_options' ); } public function delete_item_permissions_check( $request ) { return current_user_can( 'manage_options' ); } } kit-elements-defaults/utils/settings-sanitizer.php000064400000013105146731356320016503 0ustar00elements_manager = $elements_manager; $this->widget_types = $widget_types; } /** * @param $type * * @return $this */ public function for( $type ) { $this->pending_element = $this->create_element( $type ); return $this; } /** * @param $settings * * @return $this */ public function using( $settings ) { $this->pending_settings = $settings; return $this; } /** * @return $this */ public function reset() { $this->pending_element = null; $this->pending_settings = null; return $this; } /** * @return bool */ public function is_prepared() { return $this->pending_element && is_array( $this->pending_settings ); } /** * @return $this */ public function remove_invalid_settings() { if ( ! $this->is_prepared() ) { return $this; } $valid_settings_keys = $this->get_valid_settings_keys( $this->pending_element->get_controls() ); $this->pending_settings = $this->filter_invalid_settings( $this->pending_settings, array_merge( $valid_settings_keys, self::SPECIAL_SETTINGS ) ); foreach ( self::SPECIAL_SETTINGS as $special_setting ) { if ( ! isset( $this->pending_settings[ $special_setting ] ) ) { continue; } $this->pending_settings[ $special_setting ] = $this->filter_invalid_settings( $this->pending_settings[ $special_setting ], $valid_settings_keys ); } return $this; } public function kses_deep() { if ( ! $this->is_prepared() ) { return $this; } $this->pending_settings = map_deep( $this->pending_settings, function( $value ) { if ( ! is_string( $value ) ) { return $value; } return wp_kses_post( $value ); } ); return $this; } /** * @param Document $document * * @return $this */ public function prepare_for_export( Document $document ) { return $this->run_import_export_sanitize_process( $document, 'on_export' ); } /** * @param Document $document * * @return $this */ public function prepare_for_import( Document $document ) { return $this->run_import_export_sanitize_process( $document, 'on_import' ); } /** * @return array */ public function get() { if ( ! $this->is_prepared() ) { return []; } $settings = $this->pending_settings; $this->reset(); return $settings; } /** * @param string $type * @param array $settings * * @return Element_Base|null */ private function create_element( $type ) { $is_widget = in_array( $type, $this->widget_types, true ); $is_inner_section = 'inner-section' === $type; if ( $is_inner_section ) { return $this->elements_manager->create_element_instance( [ 'elType' => 'section', 'isInner' => true, 'id' => '0', ] ); } if ( $is_widget ) { return $this->elements_manager->create_element_instance( [ 'elType' => 'widget', 'widgetType' => $type, 'id' => '0', ] ); } return $this->elements_manager->create_element_instance( [ 'elType' => $type, 'id' => '0', ] ); } /** * @param Document $document * @param $process_type * * @return $this */ private function run_import_export_sanitize_process( Document $document, $process_type ) { if ( ! $this->is_prepared() ) { return $this; } $result = $document->process_element_import_export( $this->pending_element, $process_type, [ 'settings' => $this->pending_settings ] ); if ( empty( $result['settings'] ) ) { return $this; } $this->pending_settings = $result['settings']; return $this; } /** * Get all the available settings of a specific element, including responsive settings. * * @param array $controls * * @return array */ private function get_valid_settings_keys( $controls ) { if ( ! $controls ) { return []; } $control_keys = array_keys( $controls ); $optional_responsive_keys = [ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE, Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA, Breakpoints_Manager::BREAKPOINT_KEY_TABLET, Breakpoints_Manager::BREAKPOINT_KEY_TABLET_EXTRA, Breakpoints_Manager::BREAKPOINT_KEY_LAPTOP, Breakpoints_Manager::BREAKPOINT_KEY_WIDESCREEN, ]; $settings = []; foreach ( $control_keys as $control_key ) { // Add the responsive settings. foreach ( $optional_responsive_keys as $responsive_key ) { $settings[] = "{$control_key}_{$responsive_key}"; } // Add the setting itself (not responsive). $settings[] = $control_key; } return $settings; } /** * Remove invalid settings. * * @param $settings * @param $valid_settings_keys * * @return array */ private function filter_invalid_settings( $settings, $valid_settings_keys ) { return array_filter( $settings, function ( $setting_key ) use ( $valid_settings_keys ) { return in_array( $setting_key, $valid_settings_keys, true ); }, ARRAY_FILTER_USE_KEY ); } } kit-elements-defaults/usage.php000064400000001364146731356320012605 0ustar00get_usage_data(); return $params; } ); } private function get_usage_data() { $elements_defaults = $this->get_elements_defaults() ?? []; return [ 'count' => count( $elements_defaults ), 'elements' => array_keys( $elements_defaults ), ]; } private function get_elements_defaults() { $kit = Plugin::$instance->kits_manager->get_active_kit(); return $kit->get_json_meta( Module::META_KEY ); } } admin-bar/module.php000064400000006323146731356320010412 0ustar00is_editable_by_current_user() ) { return; } $this->documents[ $document->get_main_id() ] = $document; } /** * Scripts for module. */ public function enqueue_scripts() { if ( empty( $this->documents ) ) { return; } // Should load 'elementor-admin-bar' before 'admin-bar' wp_dequeue_script( 'admin-bar' ); wp_enqueue_script( 'elementor-admin-bar', $this->get_js_assets_url( 'elementor-admin-bar' ), [ 'elementor-frontend-modules' ], ELEMENTOR_VERSION, true ); // This is a core script of WordPress, it is not required to pass the 'ver' argument. wp_enqueue_script( // phpcs:ignore WordPress.WP.EnqueuedResourceParameters 'admin-bar', null, [ 'elementor-admin-bar' ], false, true ); $this->print_config( 'elementor-admin-bar' ); } /** * Creates admin bar menu items config. * * @return array */ public function get_init_settings() { $settings = []; if ( ! empty( $this->documents ) ) { $settings['elementor_edit_page'] = $this->get_edit_button_config(); } /** * Admin bar settings in the frontend. * * Register admin_bar config to parse later in the frontend and add to the admin bar with JS. * * @since 3.0.0 * * @param array $settings the admin_bar config */ $settings = apply_filters( 'elementor/frontend/admin_bar/settings', $settings ); return $settings; } /** * Creates the config for 'Edit with elementor' menu item. * * @return array */ private function get_edit_button_config() { $queried_object_id = get_queried_object_id(); $href = null; if ( is_singular() && isset( $this->documents[ $queried_object_id ] ) ) { $href = $this->documents[ $queried_object_id ]->get_edit_url(); unset( $this->documents[ $queried_object_id ] ); } return [ 'id' => 'elementor_edit_page', 'title' => esc_html__( 'Edit with Elementor', 'elementor' ), 'href' => $href, 'children' => array_map( function ( $document ) { return [ 'id' => "elementor_edit_doc_{$document->get_main_id()}", 'title' => $document->get_post()->post_title, 'sub_title' => $document::get_title(), 'href' => $document->get_edit_url(), ]; }, $this->documents ), ]; } /** * Module constructor. */ public function __construct() { add_action( 'elementor/frontend/before_get_builder_content', [ $this, 'add_document_to_admin_bar' ], 10, 2 ); add_action( 'wp_footer', [ $this, 'enqueue_scripts' ], 11 /* after third party scripts */ ); } } apps/module.php000064400000007506146731356320007527 0ustar00register( static::PAGE_ID, new Admin_Menu_Apps() ); }, 115 ); add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) { if ( ! empty( $hooks[ static::PAGE_ID ] ) ) { add_action( "admin_print_scripts-{$hooks[ static::PAGE_ID ]}", [ $this, 'enqueue_assets' ] ); } }, 10, 2 ); add_filter( 'elementor/finder/categories', function( array $categories ) { $categories['site']['items']['apps'] = [ 'title' => esc_html__( 'Add-ons', 'elementor' ), 'url' => admin_url( 'admin.php?page=' . static::PAGE_ID ), 'icon' => 'apps', 'keywords' => [ 'apps', 'addon', 'plugin', 'extension', 'integration' ], ]; return $categories; } ); // Add the Elementor Apps link to the plugin install action links. add_filter( 'install_plugins_tabs', [ $this, 'add_elementor_plugin_install_action_link' ] ); add_action( 'install_plugins_pre_elementor', [ $this, 'maybe_open_elementor_tab' ] ); add_action( 'admin_print_styles-plugin-install.php', [ $this, 'add_plugins_page_styles' ] ); } public function enqueue_assets() { add_filter( 'admin_body_class', [ $this, 'body_status_classes' ] ); wp_enqueue_style( 'elementor-apps', $this->get_css_assets_url( 'modules/apps/admin' ), [], ELEMENTOR_VERSION ); } public function body_status_classes( $admin_body_classes ) { $admin_body_classes .= ' elementor-apps-page'; return $admin_body_classes; } public function add_elementor_plugin_install_action_link( $tabs ) { $tabs['elementor'] = esc_html__( 'For Elementor', 'elementor' ); return $tabs; } public function maybe_open_elementor_tab() { if ( ! isset( $_GET['tab'] ) || 'elementor' !== $_GET['tab'] ) { return; } $elementor_url = add_query_arg( [ 'page' => static::PAGE_ID, 'tab' => 'elementor', 'ref' => 'plugins', ], admin_url( 'admin.php' ) ); wp_safe_redirect( $elementor_url ); exit; } public function add_plugins_page_styles() { ?> ' . esc_html__( 'New! Popular Add-ons', 'elementor' ) . ''; $pointer_content .= '

' . esc_html__( 'Discover our collection of plugins and add-ons carefully selected to enhance your Elementor website and unleash your creativity.', 'elementor' ) . '

'; $pointer_content .= sprintf( '

%s

', admin_url( 'admin.php?page=' . Module::PAGE_ID ), esc_html__( 'Explore Add-ons', 'elementor' ) ) ?> =' ); } } apps/admin-menu-apps.php000064400000001256146731356320011231 0ustar00

*

is_plugin_active( $app['file_path'] ) ) { return null; } if ( self::$plugin_status_adapter->is_plugin_installed( $app['file_path'] ) ) { if ( current_user_can( 'activate_plugins' ) ) { $app['action_label'] = 'Activate'; $app['action_url'] = self::$plugin_status_adapter->get_activate_plugin_url( $app['file_path'] ); } else { $app['action_label'] = 'Cannot Activate'; $app['action_url'] = '#'; } } else { if ( current_user_can( 'install_plugins' ) ) { $app['action_label'] = 'Install'; $app['action_url'] = self::$plugin_status_adapter->get_install_plugin_url( $app['file_path'] ); } else { $app['action_label'] = 'Cannot Install'; $app['action_url'] = '#'; } } return $app; } private static function is_ecom_app( $app ) { return isset( $app['type'] ) && 'ecom' === $app['type']; } private static function filter_ecom_app( $app ) { if ( self::$wordpress_adapter->is_plugin_active( $app['file_path'] ) ) { return null; } if ( ! self::$plugin_status_adapter->is_plugin_installed( $app['file_path'] ) ) { return $app; } if ( current_user_can( 'activate_plugins' ) ) { $app['action_label'] = 'Activate'; $app['action_url'] = self::$plugin_status_adapter->get_activate_plugin_url( $app['file_path'] ); } else { $app['action_label'] = 'Cannot Activate'; $app['action_url'] = '#'; } $app['target'] = '_self'; return $app; } private static function get_images_url() { return ELEMENTOR_URL . 'modules/apps/images/'; } private static function is_elementor_pro_installed() { return defined( 'ELEMENTOR_PRO_VERSION' ); } private static function render_plugin_item( $plugin ) { ?>
<?php echo esc_attr( $plugin['name'] ); ?>

verify_permission(); $this->force_enabled_all_elements(); $widgets = []; $plugins = []; foreach ( Plugin::$instance->widgets_manager->get_widget_types() as $widget ) { $widget_title = sanitize_user( $widget->get_title() ); if ( empty( $widget_title ) || ! $widget->show_in_panel() ) { continue; } $plugin_name = $this->get_plugin_name_from_widget_instance( $widget ); if ( ! in_array( $plugin_name, $plugins ) ) { $plugins[] = $plugin_name; } $widgets[] = [ 'name' => $widget->get_name(), 'plugin' => $plugin_name, 'title' => $widget_title, 'icon' => $widget->get_icon(), ]; } $notice_id = 'e-element-manager-intro-1'; $data = [ 'disabled_elements' => Options::get_disabled_elements(), 'promotion_widgets' => [], 'widgets' => $widgets, 'plugins' => $plugins, 'notice_data' => [ 'notice_id' => $notice_id, 'is_viewed' => User::is_user_notice_viewed( $notice_id ), ], 'promotion_data' => [ 'manager_permissions' => [ 'pro' => $this->get_element_manager_promotion( [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => self::FREE_TO_PRO_PERMISSIONS_PROMOTION_URL, ], 'pro_permissions' ), 'advanced' => $this->get_element_manager_promotion( [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => self::PRO_TO_ADVANCED_PERMISSIONS_PROMOTION_URL, ], 'advanced_permissions' ), ], 'element_manager' => $this->get_element_manager_promotion( [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => self::ELEMENT_MANAGER_PROMOTION_URL, ], 'element_manager' ), ], ]; if ( ! Utils::has_pro() ) { $data['promotion_widgets'] = Api::get_promotion_widgets(); } $data['additional_data'] = apply_filters( 'elementor/element_manager/admin_app_data/additional_data', [] ); wp_send_json_success( $data ); } private function get_element_manager_promotion( $promotion_data, $filter_id ): array { return Filtered_Promotions_Manager::get_filtered_promotion_data( $promotion_data, 'elementor/element_manager/admin_app_data/promotion_data/' . $filter_id, 'url' ); } private function verify_permission() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( esc_html__( 'You do not have permission to edit these settings.', 'elementor' ) ); } $nonce = Utils::get_super_global_value( $_POST, 'nonce' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'e-element-manager-app' ) ) { wp_send_json_error( esc_html__( 'Invalid nonce.', 'elementor' ) ); } } private function force_enabled_all_elements() { remove_all_filters( 'elementor/widgets/is_widget_enabled' ); } private function get_plugin_name_from_widget_instance( $widget ) { if ( in_array( 'wordpress', $widget->get_categories() ) ) { return esc_html__( 'WordPress Widgets', 'elementor' ); } $class_reflection = new \ReflectionClass( $widget ); $plugin_basename = plugin_basename( $class_reflection->getFileName() ); $plugin_directory = strtok( $plugin_basename, '/' ); $plugins_data = get_plugins( '/' . $plugin_directory ); $plugin_data = array_shift( $plugins_data ); return $plugin_data['Name'] ?? esc_html__( 'Unknown', 'elementor' ); } public function ajax_save_disabled_elements() { $this->verify_permission(); $elements = Utils::get_super_global_value( $_POST, 'widgets' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( empty( $elements ) ) { wp_send_json_error( esc_html__( 'No elements to save.', 'elementor' ) ); } $disabled_elements = json_decode( $elements ); if ( ! is_array( $disabled_elements ) ) { wp_send_json_error( esc_html__( 'Unexpected elements data.', 'elementor' ) ); } Options::update_disabled_elements( $disabled_elements ); do_action( 'elementor/element_manager/save_disabled_elements' ); wp_send_json_success(); } public function ajax_get_widgets_usage() { $this->verify_permission(); /** @var Usage_Module $usage_module */ $usage_module = Usage_Module::instance(); $usage_module->recalc_usage(); $widgets_usage = []; foreach ( $usage_module->get_formatted_usage( 'raw' ) as $data ) { foreach ( $data['elements'] as $element => $count ) { if ( ! isset( $widgets_usage[ $element ] ) ) { $widgets_usage[ $element ] = 0; } $widgets_usage[ $element ] += $count; } } wp_send_json_success( $widgets_usage ); } } element-manager/module.php000064400000005023146731356320011615 0ustar00register_endpoints(); add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( static::PAGE_ID, new Admin_Menu_App() ); }, 25 ); add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) { if ( ! empty( $hooks[ static::PAGE_ID ] ) ) { add_action( "admin_print_scripts-{$hooks[ static::PAGE_ID ]}", [ $this, 'enqueue_assets' ] ); add_action( "admin_footer-{$hooks[ static::PAGE_ID ]}", [ $this, 'print_styles' ], 1000 ); } }, 10, 2 ); add_filter( 'elementor/widgets/is_widget_enabled', function( $should_register, Widget_Base $widget_instance ) { return ! Options::is_element_disabled( $widget_instance->get_name() ); }, 10, 2 ); add_filter( 'elementor/system-info/usage/settings', function( $usage ) { $disabled_elements = Options::get_disabled_elements(); if ( ! empty( $disabled_elements ) ) { $usage['disabled_elements'] = implode( ', ', $disabled_elements ); } return $usage; } ); add_filter( 'elementor/tracker/send_tracking_data_params', function( $params ) { $disabled_elements = Options::get_disabled_elements(); if ( ! empty( $disabled_elements ) ) { $params['usages']['disabled_elements'] = $disabled_elements; } return $params; } ); } public function enqueue_assets() { wp_enqueue_script( 'e-element-manager-app', $this->get_js_assets_url( 'element-manager-admin' ), [ 'wp-element', 'wp-components', 'wp-dom-ready', 'wp-i18n', ], ELEMENTOR_VERSION ); wp_localize_script( 'e-element-manager-app', 'eElementManagerConfig', [ 'nonce' => wp_create_nonce( 'e-element-manager-app' ), 'ajaxurl' => admin_url( 'admin-ajax.php' ), ] ); wp_set_script_translations( 'e-element-manager-app', 'elementor' ); wp_enqueue_style( 'wp-components' ); wp_enqueue_style( 'wp-format-library' ); } public function print_styles() { ?> '; echo '

' . esc_html__( 'Element Manager', 'elementor' ) . '

'; echo '
'; echo ''; } } element-manager/options.php000064400000001024146731356320012020 0ustar00get_js_assets_url( 'notes' ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-notes', 'elementor' ); } /** * Enqueue the module styles. * * @return void */ public function enqueue_styles() { wp_enqueue_style( 'elementor-notes', $this->get_css_assets_url( 'modules/notes/editor' ), [ 'elementor-editor' ], ELEMENTOR_VERSION ); } /** * @return bool */ public static function is_active() { return ! Utils::has_pro(); } /** * Initialize the Notes module. * * @return void */ public function __construct() { parent::__construct(); add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_action( 'elementor/editor/after_enqueue_styles', [ $this, 'enqueue_styles' ] ); } } nested-accordion/module.php000064400000001276146731356320012003 0ustar00experiments->is_feature_active( 'nested-elements' ); } public function get_name() { return 'nested-accordion'; } public function __construct() { parent::__construct(); add_action( 'elementor/editor/before_enqueue_scripts', function () { wp_enqueue_script( $this->get_name(), $this->get_js_assets_url( $this->get_name() ), [ 'nested-elements', ], ELEMENTOR_VERSION, true ); } ); } } nested-accordion/widgets/nested-accordion.php000064400000072440146731356320015406 0ustar00experiments->is_feature_active( 'nested-elements' ); } protected function item_content_container( int $index ) { return [ 'elType' => 'container', 'settings' => [ '_title' => sprintf( __( 'item #%s', 'elementor' ), $index ), 'content_width' => 'full', ], ]; } protected function get_default_children_elements() { return [ $this->item_content_container( 1 ), $this->item_content_container( 2 ), $this->item_content_container( 3 ), ]; } protected function get_default_repeater_title_setting_key() { return 'item_title'; } protected function get_default_children_title() { return esc_html__( 'Item #%d', 'elementor' ); } protected function get_default_children_placeholder_selector() { return '.e-n-accordion'; } protected function get_default_children_container_placeholder_selector() { return '.e-n-accordion-item'; } protected function get_html_wrapper_class() { return 'elementor-widget-n-accordion'; } protected function register_controls() { $this->start_controls_section( 'section_items', [ 'label' => esc_html__( 'Layout', 'elementor' ), ] ); $repeater = new Repeater(); $repeater->add_control( 'item_title', [ 'label' => esc_html__( 'Title', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Item Title', 'elementor' ), 'placeholder' => esc_html__( 'Item Title', 'elementor' ), 'label_block' => true, 'dynamic' => [ 'active' => true, ], ] ); $repeater->add_control( 'element_css_id', [ 'label' => esc_html__( 'CSS ID', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ), 'style_transfer' => false, ] ); $this->add_control( 'items', [ 'label' => esc_html__( 'Items', 'elementor' ), 'type' => Control_Nested_Repeater::CONTROL_TYPE, 'fields' => $repeater->get_controls(), 'default' => [ [ 'item_title' => esc_html__( 'Item #1', 'elementor' ), ], [ 'item_title' => esc_html__( 'Item #2', 'elementor' ), ], [ 'item_title' => esc_html__( 'Item #3', 'elementor' ), ], ], 'title_field' => '{{{ item_title }}}', 'button_text' => 'Add Item', ] ); $this->add_responsive_control( 'accordion_item_title_position_horizontal', [ 'label' => esc_html__( 'Item Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'separator' => 'before', 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-flex eicon-align-start-h', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-h-align-center', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-flex eicon-align-end-h', ], 'stretch' => [ 'title' => esc_html__( 'Stretch', 'elementor' ), 'icon' => 'eicon-h-align-stretch', ], ], 'selectors_dictionary' => [ 'start' => '--n-accordion-title-justify-content: initial; --n-accordion-title-flex-grow: initial;', 'center' => '--n-accordion-title-justify-content: center; --n-accordion-title-flex-grow: initial;', 'end' => '--n-accordion-title-justify-content: flex-end; --n-accordion-title-flex-grow: initial;', 'stretch' => '--n-accordion-title-justify-content: space-between; --n-accordion-title-flex-grow: 1;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->add_control( 'heading_accordion_item_title_icon', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Icon', 'elementor' ), 'separator' => 'before', ] ); $this->add_responsive_control( 'accordion_item_title_icon_position', [ 'label' => esc_html__( 'Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'selectors_dictionary' => [ 'start' => '--n-accordion-title-icon-order: -1;', 'end' => '--n-accordion-title-icon-order: initial;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->add_control( 'accordion_item_title_icon', [ 'label' => esc_html__( 'Expand', 'elementor' ), 'type' => Controls_Manager::ICONS, 'default' => [ 'value' => 'fas fa-plus', 'library' => 'fa-solid', ], 'skin' => 'inline', 'label_block' => false, ] ); $this->add_control( 'accordion_item_title_icon_active', [ 'label' => esc_html__( 'Collapse', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon_active', 'default' => [ 'value' => 'fas fa-minus', 'library' => 'fa-solid', ], 'condition' => [ 'accordion_item_title_icon[value]!' => '', ], 'skin' => 'inline', 'label_block' => false, ] ); $this->add_control( 'title_tag', [ 'label' => esc_html__( 'Title HTML Tag', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'h1' => 'H1', 'h2' => 'H2', 'h3' => 'H3', 'h4' => 'H4', 'h5' => 'H5', 'h6' => 'H6', 'div' => 'div', 'span' => 'span', 'p' => 'p', ], 'selectors_dictionary' => [ 'h1' => '--n-accordion-title-font-size: 2.5rem;', 'h2' => '--n-accordion-title-font-size: 2rem;', 'h3' => '--n-accordion-title-font-size: 1,75rem;', 'h4' => '--n-accordion-title-font-size: 1.5rem;', 'h5' => '--n-accordion-title-font-size: 1rem;', 'h6' => '--n-accordion-title-font-size: 1rem; ', 'div' => '--n-accordion-title-font-size: 1rem;', 'span' => '--n-accordion-title-font-size: 1rem; ', 'p' => '--n-accordion-title-font-size: 1rem;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'default' => 'div', 'separator' => 'before', 'render_type' => 'template', ] ); $this->add_control( 'faq_schema', [ 'label' => esc_html__( 'FAQ Schema', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Yes', 'elementor' ), 'label_off' => esc_html__( 'No', 'elementor' ), 'default' => 'no', ] ); $this->add_control( 'faq_schema_message', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => esc_html__( 'Let Google know that this section contains an FAQ. Make sure to only use it only once per page', 'elementor' ), 'condition' => [ 'faq_schema[value]' => 'yes', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_interactions', [ 'label' => esc_html__( 'Interactions', 'elementor' ), ] ); $this->add_control( 'default_state', [ 'label' => esc_html__( 'Default State', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'expanded' => esc_html__( 'First expanded', 'elementor' ), 'all_collapsed' => esc_html__( 'All collapsed', 'elementor' ), ], 'default' => 'expanded', 'frontend_available' => true, ] ); $this->add_control( 'max_items_expended', [ 'label' => esc_html__( 'Max Items Expanded', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'one' => esc_html__( 'One', 'elementor' ), 'multiple' => esc_html__( 'Multiple', 'elementor' ), ], 'default' => 'one', 'frontend_available' => true, ] ); $this->add_control( 'n_accordion_animation_duration', [ 'label' => esc_html__( 'Animation Duration', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 's', 'ms' ], 'default' => [ 'unit' => 'ms', 'size' => 400, ], 'frontend_available' => true, ] ); $this->end_controls_section(); $this->add_style_tab(); } private function add_style_tab() { $this->add_accordion_style_section(); $this->add_header_style_section(); $this->add_content_style_section(); } private function add_accordion_style_section() { $this->start_controls_section( 'section_accordion_style', [ 'label' => esc_html__( 'Accordion', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'accordion_item_title_space_between', [ 'label' => esc_html__( 'Space between Items', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 200, ], 'em' => [ 'max' => 20, ], 'rem' => [ 'max' => 20, ], ], 'default' => [ 'size' => 0, ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-item-title-space-between: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'accordion_item_title_distance_from_content', [ 'label' => esc_html__( 'Distance from content', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 200, ], 'em' => [ 'max' => 20, ], 'rem' => [ 'max' => 20, ], ], 'default' => [ 'size' => 0, ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-item-title-distance-from-content: {{SIZE}}{{UNIT}}', ], ] ); $this->start_controls_tabs( 'accordion_border_and_background' ); foreach ( [ 'normal', 'hover', 'active' ] as $state ) { $this->add_border_and_radius_style( $state ); } $this->end_controls_tabs(); $this->add_responsive_control( 'accordion_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], 'separator' => 'before', ] ); $this->add_responsive_control( 'accordion_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} ' => '--n-accordion-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); } private function add_content_style_section() { $low_specificity_accordion_item_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item ) > .e-con'; $this->start_controls_section( 'section_content_style', [ 'label' => esc_html__( 'Content', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'content_background', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => $low_specificity_accordion_item_selector, ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'content_border', 'selector' => $low_specificity_accordion_item_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_responsive_control( 'content_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ $low_specificity_accordion_item_selector => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->add_responsive_control( 'content_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ $low_specificity_accordion_item_selector => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); } private function add_header_style_section() { $this->start_controls_section( 'section_header_style', [ 'label' => esc_html__( 'Header', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'heading_header_style_title', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Title', 'elementor' ), ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'title_typography', 'selector' => ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item > .e-n-accordion-item-title > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text', 'fields_options' => [ 'font_size' => [ 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-title-font-size: {{SIZE}}{{UNIT}}', ], ], ], ] ); $this->start_controls_tabs( 'header_title_color_style' ); foreach ( [ 'normal', 'hover', 'active' ] as $state ) { $this->add_header_style( $state, 'title' ); } $this->end_controls_tabs(); $this->add_control( 'heading_icon_style_title', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Icon', 'elementor' ), 'separator' => 'before', ] ); $this->add_responsive_control( 'icon_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'em' => [ 'max' => 10, ], 'rem' => [ 'max' => 10, ], ], 'default' => [ 'unit' => 'px', 'size' => 15, ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-icon-size: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'icon_spacing', [ 'label' => esc_html__( 'Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'max' => 400, ], 'vw' => [ 'max' => 50, 'step' => 0.1, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-icon-gap: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'accordion_item_title_position_horizontal!' => 'stretch', ], ] ); $this->start_controls_tabs( 'header_icon_color_style' ); foreach ( [ 'normal', 'hover', 'active' ] as $state ) { $this->add_header_style( $state, 'icon' ); } $this->end_controls_tabs(); $this->end_controls_section(); } private function add_header_style( $state, $context ) { $variable = '--n-accordion-' . $context . '-' . $state . '-color'; switch ( $state ) { case 'hover': $translated_tab_text = esc_html__( 'Hover', 'elementor' ); $translated_tab_css_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item:not([open]) > .e-n-accordion-item-title:hover > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text'; break; case 'active': $translated_tab_text = esc_html__( 'Active', 'elementor' ); $translated_tab_css_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item[open] > .e-n-accordion-item-title > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text'; break; default: $translated_tab_text = esc_html__( 'Normal', 'elementor' ); $translated_tab_css_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item:not([open]) > .e-n-accordion-item-title:not(hover) > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text'; break; } $this->start_controls_tab( 'header_' . $state . '_' . $context, [ 'label' => $translated_tab_text, ] ); $this->add_control( $state . '_' . $context . '_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => $variable . ': {{VALUE}};', ], ] ); if ( 'title' === $context ) { $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => $context . '_' . $state . '_text_shadow', 'selector' => '{{WRAPPER}} ' . $translated_tab_css_selector, 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => $context . '_' . $state . '_stroke', 'selector' => '{{WRAPPER}} ' . $translated_tab_css_selector, ] ); } $this->end_controls_tab(); } /** * @string $state */ private function add_border_and_radius_style( $state ) { $selector = '{{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item > .e-n-accordion-item-title'; $translated_tab_text = esc_html__( 'Normal', 'elementor' ); switch ( $state ) { case 'hover': $selector .= ':hover'; $translated_tab_text = esc_html__( 'Hover', 'elementor' ); break; case 'active': $selector = '{{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item[open] > .e-n-accordion-item-title'; $translated_tab_text = esc_html__( 'Active', 'elementor' ); break; } $this->start_controls_tab( 'accordion_' . $state . '_border_and_background', [ 'label' => $translated_tab_text, ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'accordion_background_' . $state, 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Color', 'elementor' ), ], ], 'selector' => $selector, ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'accordion_border_' . $state, 'selector' => $selector, ] ); $this->end_controls_tab(); } private function is_active_icon_exist( $settings ):bool { return array_key_exists( 'accordion_item_title_icon_active', $settings ) && ! empty( $settings['accordion_item_title_icon_active'] ) && ! empty( $settings['accordion_item_title_icon_active']['value'] ); } private function render_accordion_icons( $settings ) { $icon_html = Icons_Manager::try_get_icon_html( $settings['accordion_item_title_icon'], [ 'aria-hidden' => 'true' ] ); $icon_active_html = $this->is_active_icon_exist( $settings ) ? Icons_Manager::try_get_icon_html( $settings['accordion_item_title_icon_active'], [ 'aria-hidden' => 'true' ] ) : $icon_html; ob_start(); ?> get_settings_for_display(); $items = $settings['items']; $id_int = substr( $this->get_id_int(), 0, 3 ); $items_title_html = ''; $icons_content = $this->render_accordion_icons( $settings ); $this->add_render_attribute( 'elementor-accordion', 'class', 'e-n-accordion' ); $this->add_render_attribute( 'elementor-accordion', 'aria-label', 'Accordion. Open links with Enter or Space, close with Escape, and navigate with Arrow Keys' ); $default_state = $settings['default_state']; $title_html_tag = Utils::validate_html_tag( $settings['title_tag'] ); $faq_schema = []; foreach ( $items as $index => $item ) { $accordion_count = $index + 1; $item_setting_key = $this->get_repeater_setting_key( 'item_title', 'items', $index ); $item_summary_key = $this->get_repeater_setting_key( 'item_summary', 'items', $index ); $item_classes = [ 'e-n-accordion-item' ]; $item_id = empty( $item['element_css_id'] ) ? 'e-n-accordion-item-' . $id_int . $index : $item['element_css_id']; $item_title = $item['item_title']; $is_open = 'expanded' === $default_state && 0 === $index ? 'open' : ''; $aria_expanded = 'expanded' === $default_state && 0 === $index; $this->add_render_attribute( $item_setting_key, [ 'id' => $item_id, 'class' => $item_classes, ] ); $this->add_render_attribute( $item_summary_key, [ 'class' => [ 'e-n-accordion-item-title' ], 'data-accordion-index' => $accordion_count, 'tabindex' => 0 === $index ? 0 : -1, 'aria-expanded' => $aria_expanded ? 'true' : 'false', 'aria-controls' => $item_id, ] ); $title_render_attributes = $this->get_render_attribute_string( $item_setting_key ); $title_render_attributes = $title_render_attributes . ' ' . $is_open; $summary_render_attributes = $this->get_render_attribute_string( $item_summary_key ); // items content. ob_start(); $this->print_child( $index, $item_id ); $item_content = ob_get_clean(); $faq_schema[ $item_title ] = $item_content; ob_start(); ?>
> > $item_title " ); ?>
print_render_attribute_string( 'elementor-accordion' ); ?>>
'https://schema.org', '@type' => 'FAQPage', 'mainEntity' => [], ]; foreach ( $faq_schema as $name => $text ) { $json['mainEntity'][] = [ '@type' => 'Question', 'name' => wp_strip_all_tags( $name ), 'acceptedAnswer' => [ '@type' => 'Answer', 'text' => wp_strip_all_tags( $text ), ], ]; } ?> get_children(); if ( ! empty( $children[ $index ] ) ) { // Add data-tab-index attribute to the content area. $add_attribute_to_container = function ( $should_render, $container ) use ( $item_id ) { $this->add_attributes_to_container( $container, $item_id ); return $should_render; }; add_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container, 10, 3 ); $children[ $index ]->print_element(); remove_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container ); } } protected function add_attributes_to_container( $container, $item_id ) { $container->add_render_attribute( '_wrapper', [ 'role' => 'region', 'aria-labelledby' => $item_id, ] ); } protected function get_initial_config(): array { if ( Plugin::$instance->experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) { return array_merge( parent::get_initial_config(), [ 'support_improved_repeaters' => true, 'target_container' => [ '.e-n-accordion' ], 'node' => 'details', ] ); } return parent::get_initial_config(); } protected function content_template_single_repeater_item() { ?> <# const elementUid = view.getIDInt().toString().substring( 0, 3 ) + view.collection.length; const itemWrapperAttributes = { 'id': 'e-n-accordion-item-' + elementUid, 'class': [ 'e-n-accordion-item', 'e-normal' ], }; const itemTitleAttributes = { 'class': [ 'e-n-accordion-item-title' ], 'data-accordion-index': view.collection.length + 1, 'tabindex': -1, 'aria-expanded': 'false', 'aria-controls': 'e-n-accordion-item-' + elementUid, }; const itemTitleTextAttributes = { 'class': [ 'e-n-accordion-item-title-text' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'items', 'data-binding-setting': ['item_title'], 'data-binding-index': view.collection.length + 1, }; view.addRenderAttribute( 'details-container', itemWrapperAttributes, null, true ); view.addRenderAttribute( 'summary-container', itemTitleAttributes, null, true ); view.addRenderAttribute( 'text-container', itemTitleTextAttributes, null, true ); #>
{{{ data.item_title }}}
<# if ( settings['items'] ) { const elementUid = view.getIDInt().toString().substring( 0, 3 ), titleHTMLTag = elementor.helpers.validateHTMLTag( settings.title_tag ), defaultState = settings.default_state, itemTitleIcon = elementor.helpers.renderIcon( view, settings['accordion_item_title_icon'], { 'aria-hidden': true }, 'i', 'object' ) ?? '', itemTitleIconActive = '' === settings.accordion_item_title_icon_active.value ? itemTitleIcon : elementor.helpers.renderIcon( view, settings['accordion_item_title_icon_active'], { 'aria-hidden': true }, 'i', 'object' ); #> <# _.each( settings['items'], function( item, index ) { const itemCount = index + 1, itemUid = elementUid + index, itemTitleTextKey = 'item-title-text-' + itemUid, itemWrapperKey = itemUid, itemTitleKey = 'item-' + itemUid, ariaExpanded = 'expanded' === defaultState && 0 === index ? 'true' : 'false'; if ( '' !== item.element_css_id ) { itemId = item.element_css_id; } else { itemId = 'e-n-accordion-item-' + itemUid; } const itemWrapperAttributes = { 'id': itemId, 'class': [ 'e-n-accordion-item', 'e-normal' ], }; if ( defaultState === 'expanded' && index === 0) { itemWrapperAttributes['open'] = true; } view.addRenderAttribute( itemWrapperKey, itemWrapperAttributes ); view.addRenderAttribute( itemTitleKey, { 'class': ['e-n-accordion-item-title'], 'data-accordion-index': itemCount, 'tabindex': 0 === index ? 0 : -1, 'aria-expanded': ariaExpanded, 'aria-controls': itemId, }); view.addRenderAttribute( itemTitleTextKey, { 'class': ['e-n-accordion-item-title-text'], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'items', 'data-binding-setting': ['item_title'], 'data-binding-index': itemCount, }); #>
<{{{ titleHTMLTag }}} {{{ view.getRenderAttributeString( itemTitleTextKey ) }}}> {{{ item.item_title }}} <# if (settings.accordion_item_title_icon.value) { #> {{{ itemTitleIconActive.value }}} {{{ itemTitleIcon.value }}} <# } #>
<# } ); #> <# } #>
true, ) ); } function wpcf7_date_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); $class .= ' wpcf7-validates-as-date'; if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['min'] = $tag->get_date_option( 'min' ); $atts['max'] = $tag->get_date_option( 'max' ); $atts['step'] = $tag->get_option( 'step', 'int', true ); $atts['readonly'] = $tag->has_option( 'readonly' ); $atts['autocomplete'] = $tag->get_option( 'autocomplete', '[-0-9a-zA-Z]+', true ); if ( $tag->is_required() ) { $atts['aria-required'] = 'true'; } if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $value = (string) reset( $tag->values ); if ( $tag->has_option( 'placeholder' ) or $tag->has_option( 'watermark' ) ) { $atts['p