Done !
| Server IP : 46.105.57.169 / Your IP : 216.73.217.35 Web Server : Apache System : Linux webm002.cluster120.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64 User : verseaumee ( 152031) PHP Version : 8.5.7 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/verseaumee/123click/assets/ |
Upload File : |
modules/image-loading-optimization/module.php 0000644 00000027313 15076057101 0015443 0 ustar 00 <?php
namespace Elementor\Modules\ImageLoadingOptimization;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Core\Experiments\Manager as Experiments_Manager;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Module extends BaseModule {
/**
* @var string The experiment name.
*/
const EXPERIMENT_NAME = 'e_image_loading_optimization';
/**
* @var int Minimum square-pixels threshold.
*/
private $min_priority_img_pixels = 50000;
/**
* @var int The number of content media elements to not lazy-load.
*/
private $omit_threshold = 3;
/**
* @var array Keep a track of images for which loading optimization strategy were computed.
*/
private static $image_visited = [];
/**
* Get Module name.
*/
public function get_name() {
return 'image-loading-optimization';
}
/**
* Get experimental data.
*
* @return array Experimental settings.
*/
public static function get_experimental_data() {
return [
'name' => static::EXPERIMENT_NAME,
'title' => esc_html__( 'Optimize Image Loading', 'elementor' ),
'tag' => esc_html__( 'Performance', 'elementor' ),
'description' => sprintf(
/* translators: 1: fetchpriority attribute, 2: lazy loading attribute. */
esc_html__( 'Applying %1$s on LCP image and %2$s on images below the fold to improve performance scores.', 'elementor' ),
'<code>fetchpriority="high"</code>',
'<code>loading="lazy"</code>'
),
'new_site' => [
'default_active' => true,
'minimum_installation_version' => '3.17.0',
],
'generator_tag' => true,
'release_status' => Experiments_Manager::RELEASE_STATUS_BETA,
'default' => Experiments_Manager::STATE_INACTIVE,
];
}
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
// Stop wp core logic.
add_action( 'init', [ $this, 'stop_core_fetchpriority_high_logic' ] );
add_filter( 'wp_lazy_loading_enabled', '__return_false' );
// Run optimization logic on header.
add_action( 'get_header', [ $this, 'set_buffer' ] );
// Ensure buffer is flushed (if any) before the content logic.
add_filter( 'the_content', [ $this, 'flush_header_buffer' ], 0 );
// Run optimization logic on content.
add_filter( 'wp_content_img_tag', [ $this, 'loading_optimization_image' ] );
// Run optimization logic on footer. Flushing of footer buffer will be handled by PHP script end default logic.
add_action( 'get_footer', [ $this, 'set_buffer' ] );
}
/**
* Stop WordPress core fetchpriority logic by setting the wp_high_priority_element_flag flag to false.
*/
public function stop_core_fetchpriority_high_logic() {
// wp_high_priority_element_flag was only introduced in 6.3.0
if ( function_exists( 'wp_high_priority_element_flag' ) ) {
wp_high_priority_element_flag( false );
}
}
/**
* Set buffer to handle header and footer content.
*/
public function set_buffer() {
ob_start( [ $this, 'handle_buffer_content' ] );
}
/**
* This function ensure that buffer if any is flushed before the content is called.
* This function behaves more like an action than a filter.
*
* @param string $content the content.
* @return string We simply return the content from parameter.
*/
public function flush_header_buffer( $content ) {
$buffer_status = ob_get_status();
if ( ! empty( $buffer_status ) &&
1 === $buffer_status['type'] &&
get_class( $this ) . '::handle_buffer_content' === $buffer_status['name'] ) {
ob_end_flush();
}
return $content;
}
/**
* Callback to handle image optimization logic on buffered content.
*
* @param string $buffer Buffered content.
* @return string Content with optimized images.
*/
public function handle_buffer_content( $buffer ) {
return $this->filter_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(
'/<img\s[^>]+>/',
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( '<img', '<img fetchpriority="' . esc_attr( $optimization_attrs['fetchpriority'] ) . '"', $image );
}
if ( ! empty( $optimization_attrs['loading'] ) ) {
$image = str_replace( '<img', '<img loading="' . esc_attr( $optimization_attrs['loading'] ) . '"', $image );
}
return $image;
}
/**
* Return loading Loading optimization attributes for a image with give attribute.
*
* @param array $attr Existing image attributes.
* @return array Loading optimization attributes.
*/
private function get_loading_optimization_attributes( $attr ) {
$loading_attrs = [];
// For any resources, width and height must be provided, to avoid layout shifts.
if ( ! isset( $attr['width'], $attr['height'] ) ) {
return $loading_attrs;
}
/*
* The key function logic starts here.
*/
$maybe_in_viewport = null;
$increase_count = false;
$maybe_increase_count = false;
/*
* Logic to handle a `loading` attribute that is already provided.
*
* Copied from `wp_get_loading_optimization_attributes()`.
*/
if ( isset( $attr['loading'] ) ) {
/*
* Interpret "lazy" as not in viewport. Any other value can be
* interpreted as in viewport (realistically only "eager" or `false`
* to force-omit the attribute are other potential values).
*/
if ( 'lazy' === $attr['loading'] ) {
$maybe_in_viewport = false;
} else {
$maybe_in_viewport = true;
}
}
// Logic to handle a `fetchpriority` attribute that is already provided.
$has_fetchpriority_high_attr = ( isset( $attr['fetchpriority'] ) && 'high' === $attr['fetchpriority'] );
/*
* Handle cases where a `fetchpriority="high"` has already been set.
*
* Copied from `wp_get_loading_optimization_attributes()`.
*/
if ( $has_fetchpriority_high_attr ) {
/*
* If the image was already determined to not be in the viewport (e.g.
* from an already provided `loading` attribute), trigger a warning.
* Otherwise, the value can be interpreted as in viewport, since only
* the most important in-viewport image should have `fetchpriority` set
* to "high".
*/
if ( false === $maybe_in_viewport ) {
_doing_it_wrong(
__FUNCTION__,
esc_html__( 'An image should not be lazy-loaded and marked as high priority at the same time.', 'elementor' ),
''
);
/*
* Set `fetchpriority` here for backward-compatibility as we should
* not override what a developer decided, even though it seems
* incorrect.
*/
$loading_attrs['fetchpriority'] = 'high';
} else {
$maybe_in_viewport = true;
}
}
if ( null === $maybe_in_viewport && ! is_admin() ) {
$content_media_count = $this->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;
}
}
modules/history/module.php 0000644 00000003007 15076057101 0011715 0 ustar 00 <?php
namespace Elementor\Modules\History;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Plugin;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Elementor history module.
*
* Elementor history module handler class is responsible for registering and
* managing Elementor history modules.
*
* @since 1.7.0
*/
class Module extends BaseModule {
/**
* Get module name.
*
* Retrieve the history module name.
*
* @since 1.7.0
* @access public
*
* @return string Module name.
*/
public function get_name() {
return 'history';
}
/**
* Localize settings.
*
* Add new localized settings for the history module.
*
* Fired by `elementor/editor/localize_settings` filter.
*
* @since 1.7.0
* @deprecated 3.1.0
* @access public
*
* @return array Localized settings.
*/
public function localize_settings() {
Plugin::$instance->modules_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' ] );
}
}
modules/history/views/revisions-panel-template.php 0000644 00000006642 15076057101 0016524 0 ustar 00 <?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
?>
<script type="text/template" id="tmpl-elementor-panel-revisions">
<div class="elementor-panel-box">
<div class="elementor-panel-revisions-buttons">
<button class="elementor-button e-btn-txt e-revision-discard" disabled>
<?php echo esc_html__( 'Discard', 'elementor' ); ?>
</button>
<button class="elementor-button e-revision-save" disabled>
<?php echo esc_html__( 'Apply', 'elementor' ); ?>
</button>
</div>
</div>
<div class="elementor-panel-box">
<div id="elementor-revisions-list" class="elementor-panel-box-content"></div>
</div>
</script>
<script type="text/template" id="tmpl-elementor-panel-revisions-no-revisions">
<#
var no_revisions_1 = '<?php echo esc_html__( 'Revision history lets you save your previous versions of your work, and restore them any time.', 'elementor' ); ?>',
no_revisions_2 = '<?php echo esc_html__( 'Start designing your page and you will be able to see the entire revision history here.', 'elementor' ); ?>',
revisions_disabled_1 = '<?php echo esc_html__( 'It looks like the post revision feature is unavailable in your website.', 'elementor' ); ?>',
revisions_disabled_2 = '<?php printf(
/* translators: %1$s Link open tag, %2$s: Link close tag. */
esc_html__( 'Learn more about %1$sWordPress revisions%2$s', 'elementor' ),
'<a target="_blank" href="https://go.elementor.com/wordpress-revisions/">',
'</a>'
); ?>';
#>
<img class="elementor-nerd-box-icon" src="<?php
// PHPCS - Safe Elementor SVG
echo ELEMENTOR_ASSETS_URL . 'images/information.svg' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" loading="lazy" />
<div class="elementor-nerd-box-title"><?php echo esc_html__( 'No Revisions Saved Yet', 'elementor' ); ?></div>
<div class="elementor-nerd-box-message">{{{ elementor.config.document.revisions.enabled ? no_revisions_1 : revisions_disabled_1 }}}</div>
<div class="elementor-nerd-box-message">{{{ elementor.config.document.revisions.enabled ? no_revisions_2 : revisions_disabled_2 }}}</div>
</script>
<script type="text/template" id="tmpl-elementor-panel-revisions-loading">
<i class="eicon-loading eicon-animation-spin" aria-hidden="true"></i>
</script>
<script type="text/template" id="tmpl-elementor-panel-revisions-revision-item">
<div class="elementor-revision-item__wrapper {{ type }}">
<div class="elementor-revision-item__gravatar">{{{ gravatar }}}</div>
<div class="elementor-revision-item__details">
<div class="elementor-revision-date" title="{{{ new Date( timestamp * 1000 ) }}}">{{{ date }}}</div>
<div class="elementor-revision-meta">
<span>{{{ typeLabel }}}</span>
<?php echo esc_html__( 'By', 'elementor' ); ?> {{{ author }}}
<span>(#{{{ id }}})</span>
</div>
</div>
<div class="elementor-revision-item__tools">
<i class="elementor-revision-item__tools-spinner eicon-loading eicon-animation-spin" aria-hidden="true"></i>
<# if ( 'current' === type ) { #>
<i class="elementor-revision-item__tools-current eicon-check" aria-hidden="true"></i>
<span class="elementor-screen-only"><?php echo esc_html__( 'Published', 'elementor' ); ?></span>
<# } #>
<!-- <# if ( 'revision' === type ) { #>-->
<!-- <i class="eicon-undo" aria-hidden="true"></i>-->
<!-- <span class="elementor-screen-only">--><?php //echo esc_html__( 'Restore', 'elementor' ); ?><!--</span>-->
<!-- <# } #>-->
</div>
</div>
</script>
modules/history/views/history-panel-template.php 0000644 00000003570 15076057101 0016201 0 ustar 00 <?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
?>
<script type="text/template" id="tmpl-elementor-panel-history-page">
<div id="elementor-panel-elements-navigation" class="elementor-panel-navigation">
<button class="elementor-component-tab elementor-panel-navigation-tab" data-tab="actions"><?php echo esc_html__( 'Actions', 'elementor' ); ?></button>
<button class="elementor-component-tab elementor-panel-navigation-tab" data-tab="revisions"><?php echo esc_html__( 'Revisions', 'elementor' ); ?></button>
</div>
<div id="elementor-panel-history-content"></div>
</script>
<script type="text/template" id="tmpl-elementor-panel-history-tab">
<div id="elementor-history-list"></div>
<div class="elementor-history-revisions-message"><?php echo esc_html__( 'Switch to Revisions tab for older versions', 'elementor' ); ?></div>
</script>
<script type="text/template" id="tmpl-elementor-panel-history-no-items">
<img class="elementor-nerd-box-icon" src="<?php
// PHPCS - Safe Elementor SVG
echo ELEMENTOR_ASSETS_URL . 'images/information.svg'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" loading="lazy" />
<div class="elementor-nerd-box-title"><?php echo esc_html__( 'No History Yet', 'elementor' ); ?></div>
<div class="elementor-nerd-box-message"><?php echo esc_html__( 'Once you start working, you\'ll be able to redo / undo any action you make in the editor.', 'elementor' ); ?></div>
</script>
<script type="text/template" id="tmpl-elementor-panel-history-item">
<div class="elementor-history-item__details">
<span class="elementor-history-item__title">{{{ title }}}</span>
<span class="elementor-history-item__subtitle">{{{ subTitle }}}</span>
<span class="elementor-history-item__action">{{{ action }}}</span>
</div>
<div class="elementor-history-item__icon">
<span class="eicon" aria-hidden="true"></span>
</div>
</script>
modules/history/revisions-manager.php 0000644 00000023576 15076057101 0014076 0 ustar 00 <?php
namespace Elementor\Modules\History;
use Elementor\Core\Base\Document;
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
use Elementor\Core\Files\CSS\Post as Post_CSS;
use Elementor\Plugin;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Elementor history revisions manager.
*
* Elementor history revisions manager handler class is responsible for
* registering and managing Elementor revisions manager.
*
* @since 1.7.0
*/
class Revisions_Manager {
/**
* Maximum number of revisions to display.
*/
const MAX_REVISIONS_TO_DISPLAY = 50;
/**
* Authors list.
*
* Holds all the authors.
*
* @access private
*
* @var array
*/
private static $authors = [];
/**
* History revisions manager constructor.
*
* Initializing Elementor history revisions manager.
*
* @since 1.7.0
* @access public
*/
public function __construct() {
self::register_actions();
}
/**
* @since 1.7.0
* @access public
* @static
*/
public static function handle_revision() {
add_filter( 'wp_save_post_revision_check_for_changes', '__return_false' );
}
/**
* @since 2.0.0
* @access public
* @static
*
* @param $post_content
* @param $post_id
*
* @return string
*/
public static function avoid_delete_auto_save( $post_content, $post_id ) {
// Add a temporary string in order the $post will not be equal to the $autosave
// in edit-form-advanced.php:210
$document = Plugin::$instance->documents->get( $post_id );
if ( $document && $document->is_built_with_elementor() ) {
$post_content .= '<!-- Created with Elementor -->';
}
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( '<!-- Created with Elementor -->', '', $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. */
__( '%1$s ago (%2$s)', 'elementor' ),
$human_time,
$date
),
'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;
}
}
modules/elements-color-picker/module.php 0000644 00000001723 15076057101 0014422 0 ustar 00 <?php
namespace Elementor\Modules\ElementsColorPicker;
use Elementor\Core\Experiments\Manager;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Core\Experiments\Manager as Experiments_Manager;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Module extends BaseModule {
/**
* Retrieve the module name.
*
* @return string
*/
public function get_name() {
return 'elements-color-picker';
}
/**
* Enqueue the `Color-Thief` library to pick colors from images.
*
* @return void
*/
public function enqueue_scripts() {
wp_enqueue_script(
'color-thief',
$this->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' ] );
}
}
modules/container-converter/module.php 0000644 00000010123 15076057101 0014200 0 ustar 00 <?php
namespace Elementor\Modules\ContainerConverter;
use Elementor\Controls_Manager;
use Elementor\Controls_Stack;
use Elementor\Plugin;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Module extends \Elementor\Core\Base\Module {
// Event name dispatched by the buttons.
const EVENT_NAME = 'elementorContainerConverter:convert';
/**
* Retrieve the module name.
*
* @return string
*/
public function get_name() {
return 'container-converter';
}
/**
* Determine whether the module is active.
*
* @return bool
*/
public static function is_active() {
return Plugin::$instance->experiments->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 );
} );
}
}
modules/nested-elements/base/widget-nested-base.php 0000644 00000005654 15076057101 0016422 0 ustar 00 <?php
namespace Elementor\Modules\NestedElements\Base;
use Elementor\Plugin;
use Elementor\Widget_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Used to create a new widget that can be nested inside other widgets.
*/
abstract class Widget_Nested_Base extends Widget_Base {
/**
* Get default children elements structure.
*
* @return array
*/
abstract protected function get_default_children_elements();
/**
* Get repeater title setting key name.
*
* @return string
*/
abstract protected function get_default_repeater_title_setting_key();
/**
* Get default children title for the navigator, using `%d` as index in the format.
*
* @note The title in this method is used to set the default title for each created child in nested element.
* for handling the children title for new created widget(s), use `get_default_children_elements()` method,
* eg:
* [
* 'elType' => '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 '';
}
/**
* @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(),
'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();
}
}
}
modules/nested-elements/controls/control-nested-repeater.php 0000644 00000000743 15076057101 0020437 0 ustar 00 <?php
namespace Elementor\Modules\NestedElements\Controls;
use Elementor\Control_Repeater;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Changing the default repeater control behavior for custom item title defaults.
* For custom management of nested repeater controls.
*/
class Control_Nested_Repeater extends Control_Repeater {
const CONTROL_TYPE = 'nested-elements-repeater';
public function get_type() {
return static::CONTROL_TYPE;
}
}
modules/nested-elements/module.php 0000644 00000003101 15076057101 0013303 0 ustar 00 <?php
namespace Elementor\Modules\NestedElements;
use Elementor\Core\Experiments\Manager as Experiments_Manager;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Module extends \Elementor\Core\Base\Module {
const EXPERIMENT_NAME = 'nested-elements';
public static function get_experimental_data() {
return [
'name' => self::EXPERIMENT_NAME,
'title' => esc_html__( 'Nested Elements', 'elementor' ),
'description' => sprintf(
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. %1$sLearn More%2$s', 'elementor' ),
'<a href="https://go.elementor.com/wp-dash-nested-elements/" target="_blank">',
'</a>'
),
'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 );
} );
}
}
modules/safe-mode/mu-plugin/elementor-safe-mode.php 0000644 00000007577 15076057101 0016354 0 ustar 00 <?php
/**
* Plugin Name: Elementor Safe Mode
* Description: Safe Mode allows you to troubleshoot issues by only loading the editor, without loading the theme or any other plugin.
* Plugin URI: https://elementor.com/?utm_source=safe-mode&utm_campaign=plugin-uri&utm_medium=wp-dash
* Author: Elementor.com
* Version: 1.0.0
* Author URI: https://elementor.com/?utm_source=safe-mode&utm_campaign=author-uri&utm_medium=wp-dash
*
* Text Domain: elementor
*
* @package Elementor
* @category Safe Mode
*
* Elementor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Elementor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Safe_Mode {
const OPTION_ENABLED = 'elementor_safe_mode';
const OPTION_TOKEN = self::OPTION_ENABLED . '_token';
public function is_enabled() {
return get_option( self::OPTION_ENABLED );
}
public function is_valid_token() {
$token = isset( $_COOKIE[ self::OPTION_TOKEN ] )
? wp_kses_post( wp_unslash( $_COOKIE[ self::OPTION_TOKEN ] ) )
: null;
if ( $token && get_option( self::OPTION_TOKEN ) === $token ) {
return true;
}
return false;
}
public function is_requested() {
return ! empty( $_REQUEST['elementor-mode'] ) && 'safe' === $_REQUEST['elementor-mode'];
}
public function is_editor() {
return is_admin() && isset( $_GET['action'] ) && 'elementor' === $_GET['action'];
}
public function is_editor_preview() {
return isset( $_GET['elementor-preview'] );
}
public function is_editor_ajax() {
// PHPCS - There is already nonce verification in the Ajax Manager
return is_admin() && isset( $_POST['action'] ) && 'elementor_ajax' === $_POST['action']; // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
public function add_hooks() {
add_filter( 'pre_option_active_plugins', function () {
return get_option( 'elementor_safe_mode_allowed_plugins' );
} );
add_filter( 'pre_option_stylesheet', function () {
return 'elementor-safe';
} );
add_filter( 'pre_option_template', function () {
return 'elementor-safe';
} );
add_action( 'elementor/init', function () {
do_action( 'elementor/safe_mode/init' );
} );
}
/**
* Plugin row meta.
*
* Adds row meta links to the plugin list table
*
* Fired by `plugin_row_meta` filter.
*
* @access public
*
* @param array $plugin_meta An array of the plugin's metadata, including
* the version, author, author URI, and plugin URI.
* @param string $plugin_file Path to the plugin file, relative to the plugins
* directory.
*
* @return array An array of plugin row meta links.
*/
public function plugin_row_meta( $plugin_meta, $plugin_file, $plugin_data, $status ) {
if ( basename( __FILE__ ) === $plugin_file ) {
$row_meta = [
'docs' => '<a href="https://go.elementor.com/safe-mode/" aria-label="' . esc_attr( esc_html__( 'Learn More', 'elementor' ) ) . '" target="_blank">' . esc_html__( 'Learn More', 'elementor' ) . '</a>',
];
$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();
modules/safe-mode/module.php 0000644 00000037730 15076057101 0012066 0 ustar 00 <?php
namespace Elementor\Modules\SafeMode;
use Elementor\Plugin;
use Elementor\Settings;
use Elementor\Tools;
use Elementor\TemplateLibrary\Source_Local;
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Module extends \Elementor\Core\Base\Module {
const OPTION_ENABLED = 'elementor_safe_mode';
const OPTION_TOKEN = self::OPTION_ENABLED . '_token';
const MU_PLUGIN_FILE_NAME = 'elementor-safe-mode.php';
const DOCS_HELPED_URL = 'https://go.elementor.com/safe-mode-helped/';
const DOCS_DIDNT_HELP_URL = 'https://go.elementor.com/safe-mode-didnt-helped/';
const DOCS_MU_PLUGINS_URL = 'https://go.elementor.com/safe-mode-mu-plugins/';
const DOCS_TRY_SAFE_MODE_URL = 'https://go.elementor.com/safe-mode/';
const EDITOR_NOTICE_TIMEOUT = 30000; /* ms */
public function get_name() {
return 'safe-mode';
}
public function register_ajax_actions( Ajax $ajax ) {
$ajax->register_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() {
?>
<style>
.elementor-safe-mode-toast {
position: absolute;
z-index: 10000; /* Over the loading layer */
bottom: 10px;
width: 400px;
line-height: 30px;
color: var(--e-a-color-txt);
background: var(--e-a-bg-default);
padding: 20px 25px 25px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
border-radius: 5px;
font-family: var(--e-a-font-family);
}
body.rtl .elementor-safe-mode-toast {
left: 10px;
}
body:not(.rtl) .elementor-safe-mode-toast {
right: 10px;
}
#elementor-try-safe-mode {
display: none;
}
.elementor-safe-mode-toast .elementor-toast-content {
font-size: 13px;
line-height: 22px;
}
.elementor-safe-mode-toast .elementor-toast-content a {
color: var(--e-a-color-info);
}
.elementor-safe-mode-toast .elementor-toast-content hr {
margin: 15px auto;
border: 0 none;
border-block-start: var(--e-a-border);
}
.elementor-safe-mode-toast header {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
margin-block-end: 20px;
}
.elementor-safe-mode-toast header > * {
margin-block-start: 10px;
}
.elementor-safe-mode-toast header i {
font-size: 25px;
color: var(--e-a-color-warning);
}
.elementor-safe-mode-toast header i {
margin-inline-end: 10px;
}
.elementor-safe-mode-toast header h2 {
flex-grow: 1;
font-size: 18px;
}
.elementor-safe-mode-list-item {
margin-block-start: 10px;
list-style: outside;
}
.elementor-safe-mode-list-item {
margin-inline-start: 15px;
}
.elementor-safe-mode-list-item b {
font-size: 14px;
}
.elementor-safe-mode-list-item-content {
font-style: italic;
color: var(--e-a-color-txt);
}
.elementor-safe-mode-list-item-title {
font-weight: 500;
}
.elementor-safe-mode-mu-plugins {
background-color: var(--e-a-bg-hover);
color: var(--e-a-color-txt-hover);
margin-block-start: 20px;
padding: 10px 15px;
}
</style>
<?php
}
public function print_safe_mode_notice() {
$this->print_safe_mode_css()
?>
<div class="elementor-safe-mode-toast" id="elementor-safe-mode-message">
<header>
<i class="eicon-warning"></i>
<h2><?php echo esc_html__( 'Safe Mode ON', 'elementor' ); ?></h2>
<a class="elementor-button elementor-safe-mode-button elementor-disable-safe-mode" target="_blank" href="<?php echo esc_url( $this->get_admin_page_url() ); ?>">
<?php echo esc_html__( 'Disable Safe Mode', 'elementor' ); ?>
</a>
</header>
<div class="elementor-toast-content">
<ul class="elementor-safe-mode-list">
<li class="elementor-safe-mode-list-item">
<div class="elementor-safe-mode-list-item-title"><?php echo esc_html__( 'Editor successfully loaded?', 'elementor' ); ?></div>
<div class="elementor-safe-mode-list-item-content">
<?php
echo esc_html__( 'The issue was probably caused by one of your plugins or theme.', 'elementor' );
echo ' ';
printf(
/* translators: %1$s Link open tag, %2$s: Link close tag. */
esc_html__( '%1$sClick here%2$s to troubleshoot', 'elementor' ),
'<a href="' . self::DOCS_HELPED_URL . '" target="_blank">', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'</a>'
);
?>
</div>
</li>
<li class="elementor-safe-mode-list-item">
<div class="elementor-safe-mode-list-item-title"><?php echo esc_html__( 'Still experiencing issues?', 'elementor' ); ?></div>
<div class="elementor-safe-mode-list-item-content">
<?php
printf(
/* translators: %1$s Link open tag, %2$s: Link close tag. */
esc_html__( '%1$sClick here%2$s to troubleshoot', 'elementor' ),
'<a href="' . self::DOCS_DIDNT_HELP_URL . '" target="_blank">', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'</a>'
);
?>
</div>
</li>
</ul>
<?php
$mu_plugins = wp_get_mu_plugins();
if ( 1 < count( $mu_plugins ) ) : ?>
<div class="elementor-safe-mode-mu-plugins">
<?php
printf(
/* translators: %1$s Link open tag, %2$s: Link close tag. */
esc_html__( 'Please note! We couldn\'t deactivate all of your plugins on Safe Mode. Please %1$sread more%2$s about this issue', 'elementor' ),
'<a href="' . self::DOCS_MU_PLUGINS_URL . '" target="_blank">', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'</a>'
);
?>
</div>
<?php endif; ?>
</div>
</div>
<script>
var ElementorSafeMode = function() {
var attachEvents = function() {
jQuery( '.elementor-disable-safe-mode' ).on( 'click', function( e ) {
if ( ! elementorCommon || ! elementorCommon.ajax ) {
return;
}
e.preventDefault();
elementorCommon.ajax.addRequest(
'disable_safe_mode', {
success: function() {
if ( -1 === location.href.indexOf( 'elementor-mode=safe' ) ) {
location.reload();
} else {
// Need to remove the URL from browser history.
location.replace( location.href.replace( '&elementor-mode=safe', '' ) );
}
},
error: function() {
alert( 'An error occurred.' );
},
},
true
);
} );
};
var init = function() {
attachEvents();
};
init();
};
new ElementorSafeMode();
</script>
<?php
}
public function print_try_safe_mode() {
if ( ! $this->is_allowed_post_type() ) {
return;
}
$this->print_safe_mode_css();
?>
<div class="elementor-safe-mode-toast" id="elementor-try-safe-mode">
<?php if ( current_user_can( 'install_plugins' ) ) : ?>
<header>
<i class="eicon-warning"></i>
<h2><?php echo esc_html__( 'Can\'t Edit?', 'elementor' ); ?></h2>
<a class="elementor-button e-primary elementor-safe-mode-button elementor-enable-safe-mode" target="_blank" href="<?php echo esc_url( $this->get_admin_page_url() ); ?>">
<?php echo esc_html__( 'Enable Safe Mode', 'elementor' ); ?>
</a>
</header>
<div class="elementor-toast-content">
<?php echo esc_html__( 'Having problems loading Elementor? Please enable Safe Mode to troubleshoot.', 'elementor' ); ?>
<a href="<?php Utils::print_unescaped_internal_string( self::DOCS_TRY_SAFE_MODE_URL ); ?>" target="_blank"><?php echo esc_html__( 'Learn More', 'elementor' ); ?></a>
</div>
<?php else : ?>
<header>
<i class="eicon-warning"></i>
<h2><?php echo esc_html__( 'Can\'t Edit?', 'elementor' ); ?></h2>
</header>
<div class="elementor-toast-content">
<?php echo esc_html__( 'If you are experiencing a loading issue, contact your site administrator to troubleshoot the problem using Safe Mode.', 'elementor' ); ?>
<a href="<?php Utils::print_unescaped_internal_string( self::DOCS_TRY_SAFE_MODE_URL ); ?>" target="_blank"><?php echo esc_html__( 'Learn More', 'elementor' ); ?></a>
</div>
<?php endif; ?>
</div>
<script>
var ElementorTrySafeMode = function() {
var attachEvents = function() {
jQuery( '.elementor-enable-safe-mode' ).on( 'click', function( e ) {
if ( ! elementorCommon || ! elementorCommon.ajax ) {
return;
}
e.preventDefault();
elementorCommon.ajax.addRequest(
'enable_safe_mode', {
data: {
editor_post_id: '<?php
// PHPCS - the method get_post_id is safe.
echo Plugin::$instance->editor->get_post_id(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>',
},
success: function( url ) {
location.assign( url );
},
error: function() {
alert( 'An error occurred.' );
},
},
true
);
} );
};
var isElementorLoaded = function() {
if ( 'undefined' === typeof elementor ) {
return false;
}
if ( ! elementor.loaded ) {
return false;
}
if ( jQuery( '#elementor-loading' ).is( ':visible' ) ) {
return false;
}
return true;
};
var handleTrySafeModeNotice = function() {
var $notice = jQuery( '#elementor-try-safe-mode' );
if ( isElementorLoaded() ) {
$notice.remove();
return;
}
if ( ! $notice.data( 'visible' ) ) {
$notice.show().data( 'visible', true );
}
// Re-check after 500ms.
setTimeout( handleTrySafeModeNotice, 500 );
};
var init = function() {
setTimeout( handleTrySafeModeNotice, <?php Utils::print_unescaped_internal_string( self::EDITOR_NOTICE_TIMEOUT ); ?> );
attachEvents();
};
init();
};
new ElementorTrySafeMode();
</script>
<?php
}
public function run_safe_mode() {
remove_action( 'elementor/editor/footer', [ $this, 'print_try_safe_mode' ] );
// Avoid notices like for comment.php.
add_filter( 'deprecated_file_trigger_error', '__return_false' );
add_filter( 'template_include', [ $this, 'filter_template' ], 999 );
add_filter( 'elementor/document/urls/preview', [ $this, 'filter_preview_url' ] );
add_action( 'elementor/editor/footer', [ $this, 'print_safe_mode_notice' ] );
add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'register_scripts' ], 11 /* After Common Scripts */ );
}
public function register_scripts() {
wp_add_inline_script( 'elementor-common', 'elementorCommon.ajax.addRequestConstant( "elementor-mode", "safe" );' );
}
private function is_enabled() {
return get_option( self::OPTION_ENABLED, '' );
}
private function get_admin_page_url() {
// A fallback URL if the Js doesn't work.
return Tools::get_url();
}
public function plugin_action_links( $actions ) {
$actions['disable'] = '<a href="' . self::get_admin_page_url() . '">' . esc_html__( 'Disable Safe Mode', 'elementor' ) . '</a>';
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 );
}
}
modules/ai/module.php 0000644 00000056440 15076057101 0010616 0 ustar 00 <?php
namespace Elementor\Modules\Ai;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Core\Common\Modules\Connect\Module as ConnectModule;
use Elementor\Core\Experiments\Manager as Experiments_Manager;
use Elementor\Core\Utils\Collection;
use Elementor\Modules\Ai\Connect\Ai;
use Elementor\Plugin;
use Elementor\User;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Module extends BaseModule {
const HISTORY_TYPE_ALL = 'all';
const HISTORY_TYPE_TEXT = 'text';
const HISTORY_TYPE_CODE = 'code';
const HISTORY_TYPE_IMAGE = 'images';
const HISTORY_TYPE_BLOCK = 'blocks';
const VALID_HISTORY_TYPES = [
self::HISTORY_TYPE_ALL,
self::HISTORY_TYPE_TEXT,
self::HISTORY_TYPE_CODE,
self::HISTORY_TYPE_IMAGE,
self::HISTORY_TYPE_BLOCK,
];
const LAYOUT_EXPERIMENT = 'ai-layout';
public function get_name() {
return 'ai';
}
public function __construct() {
parent::__construct();
$this->register_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_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' ),
'default' => Experiments_Manager::STATE_INACTIVE,
'status' => Experiments_Manager::RELEASE_STATUS_ALPHA,
'hidden' => true,
'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
);
wp_localize_script(
'elementor-ai',
'ElementorAiConfig',
[
'is_get_started' => User::get_introduction_meta( 'ai_get_started' ),
'connect_url' => $this->get_ai_connect_url(),
]
);
wp_set_script_translations( 'elementor-ai', 'elementor' );
}
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,
];
}
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' );
}
$result = $app->get_image_prompt_enhanced( $data['prompt'] );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['prompt'] ) ) {
throw new \Exception( 'Missing prompt' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_completion_text( $data['prompt'], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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'];
}
public function ajax_ai_get_edit_text( $data ) {
$this->verify_permissions( $data['editor_post_id'] );
$app = $this->get_ai_app();
if ( empty( $data['input'] ) ) {
throw new \Exception( 'Missing input' );
}
if ( empty( $data['instruction'] ) ) {
throw new \Exception( 'Missing instruction' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_edit_text( $data['input'], $data['instruction'], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['prompt'] ) ) {
throw new \Exception( 'Missing prompt' );
}
if ( empty( $data['language'] ) ) {
throw new \Exception( 'Missing language' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_custom_code( $data['prompt'], $data['language'], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['prompt'] ) ) {
throw new \Exception( 'Missing prompt' );
}
if ( empty( $data['html_markup'] ) ) {
$data['html_markup'] = '';
}
if ( empty( $data['element_id'] ) ) {
throw new \Exception( 'Missing element_id' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_custom_css( $data['prompt'], $data['html_markup'], $data['element_id'], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['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 );
$result = $app->get_text_to_image( $data['prompt'], $data['promptSettings'], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['prompt'] ) ) {
throw new \Exception( 'Missing prompt' );
}
if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
throw new \Exception( 'Missing Image' );
}
if ( empty( $data['promptSettings'] ) ) {
throw new \Exception( 'Missing prompt settings' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_image_to_image( [
'prompt' => $data['prompt'],
'promptSettings' => $data['promptSettings'],
'attachment_id' => $data['image']['id'],
], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['image'] ) || empty( $data['image']['id'] ) ) {
throw new \Exception( 'Missing Image' );
}
if ( empty( $data['promptSettings'] ) ) {
throw new \Exception( 'Missing prompt settings' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_image_to_image_upscale( [
'promptSettings' => $data['promptSettings'],
'attachment_id' => $data['image']['id'],
], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['image'] ) || empty( $data['image']['id'] ) ) {
throw new \Exception( 'Missing Image' );
}
if ( empty( $data['prompt'] ) ) {
throw new \Exception( 'Prompt Missing' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_image_to_image_replace_background( [
'attachment_id' => $data['image']['id'],
'prompt' => $data['prompt'],
], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['image'] ) || empty( $data['image']['id'] ) ) {
throw new \Exception( 'Missing Image' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$context = $this->get_request_context( $data );
$result = $app->get_image_to_image_remove_background( [
'attachment_id' => $data['image']['id'],
], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['prompt'] ) ) {
throw new \Exception( 'Missing prompt' );
}
if ( empty( $data['image'] ) || empty( $data['image']['id'] ) ) {
throw new \Exception( 'Missing Image' );
}
if ( empty( $data['promptSettings'] ) ) {
throw new \Exception( 'Missing prompt settings' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
if ( empty( $data['mask'] ) ) {
throw new \Exception( 'Missing Mask' );
}
$context = $this->get_request_context( $data );
$result = $app->get_image_to_image_mask( [
'prompt' => $data['prompt'],
'promptSettings' => $data['promptSettings'],
'attachment_id' => $data['image']['id'],
'mask' => $data['mask'],
], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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['prompt'] ) ) {
throw new \Exception( 'Missing prompt' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
if ( empty( $data['mask'] ) ) {
throw new \Exception( 'Missing Expended Image' );
}
$context = $this->get_request_context( $data );
$result = $app->get_image_to_image_out_painting( [
'prompt' => $data['prompt'],
'mask' => $data['mask'],
], $context );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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'] ) ) {
throw new \Exception( 'Missing prompt' );
}
if ( ! $app->is_connected() ) {
throw new \Exception( 'not_connected' );
}
$result = $app->generate_layout(
$data['prompt'],
$this->prepare_generate_layout_context(),
$data['variationType']
);
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
$template = $result['text']['elements'][0] ?? null;
if ( empty( $template ) || ! is_array( $template ) ) {
throw new \Exception( 'unknown_error' );
}
return [
'all' => [],
'text' => $template,
'response_id' => $result['responseId'],
'usage' => $result['usage'],
];
}
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'] );
if ( is_wp_error( $result ) ) {
throw new \Exception( $result->get_error_message() );
}
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 [];
}
}
modules/ai/connect/ai.php 0000644 00000032635 15076057101 0011353 0 ustar 00 <?php
namespace Elementor\Modules\Ai\Connect;
use Elementor\Core\Common\Modules\Connect\Apps\Library;
use Elementor\Modules\Ai\Module;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Ai extends Library {
const API_URL = 'https://my.elementor.com/api/v2/ai/';
const STYLE_PRESET = 'style_preset';
const IMAGE_TYPE = 'image_type';
const IMAGE_STRENGTH = 'image_strength';
const ASPECT_RATIO = 'ratio';
const IMAGE_RESOLUTION = 'image_resolution';
const PROMPT = 'prompt';
public function get_title() {
return esc_html__( 'AI', 'elementor' );
}
protected function get_api_url() {
return static::API_URL . '/';
}
public function get_usage() {
return $this->ai_request(
'POST',
'status/check',
[
'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 = '' ) {
$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;
}
return $this->http_request(
$method,
$endpoint,
[
'timeout' => 100,
'headers' => $headers,
'body' => $body,
],
[
'return_type' => static::HTTP_RETURN_TYPE_ARRAY,
]
);
}
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 = [] ) {
return $this->ai_request(
'POST',
'text/completion',
[
'prompt' => $prompt,
'context' => wp_json_encode( $context ),
'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 = [] ) {
return $this->ai_request(
'POST',
'text/enhance-image-prompt',
[
'prompt' => $prompt,
'context' => wp_json_encode( $context ),
'api_version' => ELEMENTOR_VERSION,
'site_lang' => get_bloginfo( 'language' ),
]
);
}
public function get_edit_text( $input, $instruction, $context = [] ) {
return $this->ai_request(
'POST',
'text/edit',
[
'input' => $input,
'instruction' => $instruction,
'context' => wp_json_encode( $context ),
'api_version' => ELEMENTOR_VERSION,
'site_lang' => get_bloginfo( 'language' ),
]
);
}
public function get_custom_code( $prompt, $language, $context = [] ) {
return $this->ai_request(
'POST',
'text/custom-code',
[
'prompt' => $prompt,
'language' => $language,
'context' => wp_json_encode( $context ),
'api_version' => ELEMENTOR_VERSION,
'site_lang' => get_bloginfo( 'language' ),
]
);
}
public function get_custom_css( $prompt, $html_markup, $element_id, $context = [] ) {
return $this->ai_request(
'POST',
'text/custom-css',
[
'prompt' => $prompt,
'html_markup' => $html_markup,
'element_id' => $element_id,
'context' => wp_json_encode( $context ),
'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( $prompt, $prompt_settings, $context = [] ) {
return $this->ai_request(
'POST',
'image/text-to-image',
[
self::PROMPT => $prompt,
self::IMAGE_TYPE => $prompt_settings[ self::IMAGE_TYPE ] . '/' . $prompt_settings[ self::STYLE_PRESET ],
self::ASPECT_RATIO => $prompt_settings[ self::ASPECT_RATIO ],
'context' => wp_json_encode( $context ),
'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 = [] ) {
$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 ),
'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 = [] ) {
$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 ),
'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 = [] ) {
$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 ),
'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 = [] ) {
$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 ),
'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 = [] ) {
$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 ),
'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 = [] ) {
$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 ),
'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( $prompt, $context, $variation_type ) {
return $this->ai_request(
'POST',
'generate/layout',
[
'prompt' => $prompt,
'context' => $context ?? [],
'api_version' => ELEMENTOR_VERSION,
'site_lang' => get_bloginfo( 'language' ),
'variationType' => (int) $variation_type,
]
);
}
public function get_layout_prompt_enhanced( $prompt, $context = [] ) {
return $this->ai_request(
'POST',
'generate/enhance-prompt',
[
'prompt' => $prompt,
'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() {}
}
modules/admin-top-bar/module.php 0000644 00000006304 15076057101 0012651 0 ustar 00 <?php
namespace Elementor\Modules\AdminTopBar;
use Elementor\Plugin;
use Elementor\Core\Base\App as BaseApp;
use Elementor\Core\Experiments\Manager;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Module extends BaseApp {
/**
* @return bool
*/
public static function is_active() {
return is_admin();
}
/**
* @return string
*/
public function get_name() {
return 'admin-top-bar';
}
private function render_admin_top_bar() {
?>
<div id="e-admin-top-bar-root">
</div>
<?php
}
/**
* Enqueue admin scripts
*/
private function enqueue_scripts() {
wp_enqueue_style( 'elementor-admin-top-bar-fonts', 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap', [], ELEMENTOR_VERSION );
wp_enqueue_style( 'elementor-admin-top-bar', $this->get_css_assets_url( 'admin-top-bar', null, 'default', true ), [], ELEMENTOR_VERSION );
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' );
$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();
} );
} );
}
}
modules/page-templates/module.php 0000644 00000025755 15076057101 0013142 0 ustar 00 <?php
namespace Elementor\Modules\PageTemplates;
use Elementor\Controls_Manager;
use Elementor\Core\Base\Document;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Core\Kits\Documents\Kit;
use Elementor\Plugin;
use Elementor\Utils;
use Elementor\Core\DocumentTypes\PageBase as PageBase;
use Elementor\Modules\Library\Documents\Page as LibraryPageDocument;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Elementor page templates module.
*
* Elementor page templates module handler class is responsible for registering
* and managing Elementor page templates modules.
*
* @since 2.0.0
*/
class Module extends BaseModule {
/**
* The of the theme.
*/
const TEMPLATE_THEME = 'elementor_theme';
/**
* Elementor Canvas template name.
*/
const TEMPLATE_CANVAS = 'elementor_canvas';
/**
* Elementor Header & Footer template name.
*/
const TEMPLATE_HEADER_FOOTER = 'elementor_header_footer';
/**
* Print callback.
*
* Holds the page template callback content.
*
* @since 2.0.0
* @access protected
*
* @var callable
*/
protected $print_callback;
/**
* Get module name.
*
* Retrieve the page templates module name.
*
* @since 2.0.0
* @access public
*
* @return string Module name.
*/
public function get_name() {
return 'page-templates';
}
/**
* Template include.
*
* Update the path for the Elementor Canvas template.
*
* Fired by `template_include` filter.
*
* @since 2.0.0
* @access public
*
* @param string $template The path of the template to include.
*
* @return string The path of the template to include.
*/
public function template_include( $template ) {
if ( is_singular() ) {
$document = Plugin::$instance->documents->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' => '<b>' . esc_html__( 'The default page template as defined in Elementor Panel → Hamburger Menu → Site Settings.', 'elementor' ) . '</b>',
'content_classes' => 'elementor-descriptor',
'condition' => [
$control_id => 'default',
],
]
);
$document->add_control(
$control_id . '_theme_description',
[
'type' => Controls_Manager::RAW_HTML,
'raw' => '<b>' . esc_html__( 'Default Page Template from your theme.', 'elementor' ) . '</b>',
'content_classes' => 'elementor-descriptor',
'condition' => [
$control_id => self::TEMPLATE_THEME,
],
]
);
$document->add_control(
$control_id . '_canvas_description',
[
'type' => Controls_Manager::RAW_HTML,
'raw' => '<b>' . esc_html__( 'No header, no footer, just Elementor', 'elementor' ) . '</b>',
'content_classes' => 'elementor-descriptor',
'condition' => [
$control_id => self::TEMPLATE_CANVAS,
],
]
);
$document->add_control(
$control_id . '_header_footer_description',
[
'type' => Controls_Manager::RAW_HTML,
'raw' => '<b>' . esc_html__( 'This template includes the header, full-width content and footer', 'elementor' ) . '</b>',
'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 );
}
}
modules/page-templates/templates/header-footer.php 0000644 00000001332 15076057101 0016360 0 ustar 00 <?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
\Elementor\Plugin::$instance->frontend->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();
modules/page-templates/templates/canvas.php 0000644 00000002517 15076057101 0015115 0 ustar 00 <?php
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
\Elementor\Plugin::$instance->frontend->add_body_class( 'elementor-template-canvas' );
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<?php if ( ! current_theme_supports( 'title-tag' ) ) : ?>
<title><?php echo wp_get_document_title(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></title>
<?php endif; ?>
<?php wp_head(); ?>
<?php
// Keep the following line after `wp_head()` call, to ensure it's not overridden by another templates.
Utils::print_unescaped_internal_string( Utils::get_meta_viewport( 'canvas' ) );
?>
</head>
<body <?php body_class(); ?>>
<?php
Elementor\Modules\PageTemplates\Module::body_open();
/**
* Before canvas page template content.
*
* Fires before the content of Elementor canvas page template.
*
* @since 1.0.0
*/
do_action( 'elementor/page_templates/canvas/before_content' );
\Elementor\Plugin::$instance->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();
?>
</body>
</html>
modules/usage/usage-reporter.php 0000644 00000006011 15076057101 0012775 0 ustar 00 <?php
namespace Elementor\Modules\Usage;
use Elementor\Modules\System_Info\Reporters\Base;
use Elementor\Utils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Elementor usage report.
*
* Elementor system report handler class responsible for generating a report for
* the user.
*/
class Usage_Reporter extends Base {
const RECALC_ACTION = 'elementor_usage_recalc';
public function get_title() {
return esc_html__( 'Elements Usage', 'elementor' );
}
public function get_fields() {
return [
'usage' => '',
];
}
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 .= '<a id="elementor-usage-recalc" href="' . esc_url( $url ) . '#elementor-usage-recalc" class="box-title-tool">Recalculate</a>';
} 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 .= '<tr><td>' . $data['title'] . ' ( ' . $data['count'] . ' )</td><td>';
foreach ( $data['elements'] as $element => $count ) {
$usage .= $element . ': ' . $count . PHP_EOL;
}
$usage .= '</td></tr>';
}
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();
?>
<script>
// Origin file: modules/usage/usage-reporter.php - get_remove_recalc_query_string_script()
{
const url = new URL( window.location );
url.hash = '';
url.searchParams.delete( 'elementor_usage_recalc' );
url.searchParams.delete( '_wpnonce' );
history.replaceState( '', window.title, url.toString() );
}
</script>
<?php
return ob_get_clean();
}
}
modules/usage/settings-reporter.php 0000644 00000002360 15076057101 0013534 0 ustar 00 <?php
namespace Elementor\Modules\Usage;
use Elementor\Modules\System_Info\Reporters\Base as Base_Reporter;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Settings_Reporter extends Base_Reporter {
public function get_title() {
return esc_html__( 'Settings', 'elementor' );
}
public function get_fields() {
return [
'settings' => '',
];
}
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 .= '<tr><td>' . $setting_name . '</td><td>' . $setting_value_text . '</td></tr>';
}
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,
];
}
}
modules/usage/module.php 0000644 00000041342 15076057101 0011324 0 ustar 00 <?php
namespace Elementor\Modules\Usage;
use Elementor\Core\Base\Document;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Core\DynamicTags\Manager;
use Elementor\Modules\System_Info\Module as System_Info;
use Elementor\Plugin;
use Elementor\Settings;
use Elementor\Tracker;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Elementor usage module.
*
* Elementor usage module handler class is responsible for registering and
* managing Elementor usage data.
*
*/
class Module extends BaseModule {
const GENERAL_TAB = 'general';
const META_KEY = '_elementor_controls_usage';
const OPTION_NAME = 'elementor_controls_usage';
/**
* @var bool
*/
private $is_document_saving = false;
/**
* Get module name.
*
* Retrieve the usage module name.
*
* @access public
*
* @return string Module name.
*/
public function get_name() {
return 'usage';
}
/**
* Get doc type count.
*
* Get count of documents based on doc type
*
* Remove 'wp-' from $doc_type for BC, support doc type change since 2.7.0.
*
* @param \Elementor\Core\Documents_Manager $doc_class
* @param String $doc_type
*
* @return int
*/
public function get_doc_type_count( $doc_class, $doc_type ) {
static $posts = null;
static $library = null;
if ( null === $posts ) {
$posts = \Elementor\Tracker::get_posts_usage();
}
if ( null === $library ) {
$library = \Elementor\Tracker::get_library_usage();
}
$posts_usage = $posts;
if ( $doc_class::get_property( 'show_in_library' ) ) {
$posts_usage = $library;
}
$doc_type_common = str_replace( 'wp-', '', $doc_type );
$doc_usage = isset( $posts_usage[ $doc_type_common ] ) ? $posts_usage[ $doc_type_common ] : 0;
return is_array( $doc_usage ) ? $doc_usage['publish'] : $doc_usage;
}
/**
* Get formatted usage.
*
* Retrieve formatted usage, for frontend.
*
* @param String format
*
* @return array
*/
public function get_formatted_usage( $format = 'html' ) {
$usage = [];
foreach ( get_option( self::OPTION_NAME, [] ) as $doc_type => $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;
}
}
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 );
}
}
modules/apps/admin-pointer.php 0000644 00000003652 15076057101 0012446 0 ustar 00 <?php
namespace Elementor\Modules\Apps;
use Elementor\Core\Upgrade\Manager as Upgrade_Manager;
use Elementor\User;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Admin_Pointer {
const RELEASE_VERSION = '3.15.0';
const CURRENT_POINTER_SLUG = 'e-apps';
public static function add_hooks() {
add_action( 'admin_print_footer_scripts-index.php', [ __CLASS__, 'admin_print_script' ] );
}
public static function admin_print_script() {
if ( static::is_dismissed() || static::is_new_installation() ) {
return;
}
wp_enqueue_script( 'wp-pointer' );
wp_enqueue_style( 'wp-pointer' );
$pointer_content = '<h3>' . esc_html__( 'New! Popular Apps', 'elementor' ) . '</h3>';
$pointer_content .= '<p>' . esc_html__( 'Discover our collection of plugins and add-ons carefully selected to enhance your Elementor website and unleash your creativity.', 'elementor' ) . '</p>';
$pointer_content .= sprintf(
'<p><a class="button button-primary" href="%s">%s</a></p>',
admin_url( 'admin.php?page=' . Module::PAGE_ID ),
esc_html__( 'Explore Apps', 'elementor' )
)
?>
<script>
jQuery( document ).ready( function( $ ) {
$( '#toplevel_page_elementor' ).pointer( {
content: '<?php echo $pointer_content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>',
position: {
edge: <?php echo is_rtl() ? "'right'" : "'left'"; ?>,
align: 'center'
},
close: function() {
elementorCommon.ajax.addRequest( 'introduction_viewed', {
data: {
introductionKey: '<?php echo esc_attr( static::CURRENT_POINTER_SLUG ); ?>',
},
} );
}
} ).pointer( 'open' );
} );
</script>
<?php
}
private static function is_dismissed() {
return User::get_introduction_meta( static::CURRENT_POINTER_SLUG );
}
private static function is_new_installation() {
return Upgrade_Manager::install_compare( static::RELEASE_VERSION, '>=' );
}
}
modules/apps/module.php 0000644 00000003173 15076057101 0011163 0 ustar 00 <?php
namespace Elementor\Modules\Apps;
use Elementor\Core\Admin\Menu\Admin_Menu_Manager;
use Elementor\Core\Base\Module as BaseModule;
use Elementor\Settings;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
class Module extends BaseModule {
const PAGE_ID = 'elementor-apps';
public function get_name() {
return 'apps';
}
public function __construct() {
parent::__construct();
Admin_Pointer::add_hooks();
add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) {
$admin_menu->register( 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__( 'Apps', 'elementor' ),
'url' => admin_url( 'admin.php?page=' . static::PAGE_ID ),
'icon' => 'apps',
'keywords' => [ 'apps', 'addon', 'plugin', 'extension', 'integration' ],
];
return $categories;
} );
}
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;
}
}
modules/apps/images/hover.svg 0000644 00000002554 15076057101 0012300 0 ustar 00 <svg width="800px" height="800px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<circle cx="512" cy="512" r="512" style="fill:#229e87"/>
<path d="M307.87 456.11c-14.54 0-26.48 4.15-32.71 13V412.5h-33.24v151.6h33.75v-56.59c0-13.5 7.79-20.77 21.29-20.77 11.94 0 17.65 6.75 17.65 18.7v58.66h33.23v-60.75c0-30.11-16.61-47.24-39.97-47.24zm109 0c-32.71 0-58.15 22.85-58.15 55.56 0 32.19 25.44 55 58.15 55s58.15-22.84 58.15-55-25.41-55.56-58.12-55.56zm0 82c-15.06 0-23.89-10.91-23.89-26.48 0-16.1 9.35-26.48 23.89-26.48 15.05 0 23.88 10.91 23.88 26.48s-8.75 26.51-23.85 26.51zm189.51-79.44h-28c-11.42 0-25.44 6.75-32.19 23.37l-4.19 7.82c-4.67 10.91-8.31 18.17-8.31 18.17l-1 4.67-1.57-4.7s-3.63-7.78-8.3-18.17l-3.64-8.31c-6.75-16.62-21.29-23.37-32.19-23.37H459s11.42 3.12 23.37 23.37c11.89 19.76 44.63 84.67 44.63 84.67h11.95s32.71-64.9 44.65-84.63c11.42-19.73 22.84-22.84 22.84-22.84zm43.62-2.55c-32.71 0-58.15 22.84-58.15 55 0 32.71 24.41 55.55 62.31 55.55 22.33 0 34.79-6.75 45.17-15.57l-19.21-20.77a46.87 46.87 0 0 1-26.48 8.31c-15.05 0-23.88-6.23-27.52-17.14h80.48c1.56-36.86-18.69-65.42-56.59-65.42zm-23.88 45.17c2.59-12.46 11.42-18.69 24.92-18.69 13 0 20.77 6.75 22.33 18.69zm156.28-45.17c-10.91 0-23.37 3.63-29.6 15.05v-13h-33.72v105.4h33.75v-46.71c0-22.33 8.31-29.6 23.37-29.6a28.14 28.14 0 0 1 13 3.12l10.91-30.63a50 50 0 0 0-17.65-3.63z" style="fill:#fff"/>
</svg>
modules/apps/images/profile-builder.svg 0000644 00000007164 15076057101 0014243 0 ustar 00 <svg width="124" height="124" fill="none" xmlns="http://www.w3.org/2000/svg"><g filter="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M49.6 20.321c0 11.223-7.167 16.879-18.606 16.879-11.44 0-18.594-5.656-18.594-16.879C12.4 9.098 19.554 0 30.994 0 42.434 0 49.6 9.098 49.6 20.321zM30.994 31.078c6.055 0 10.964-4.816 10.964-10.757 0-5.94-4.909-10.757-10.964-10.757-6.055 0-10.964 4.816-10.964 10.757 0 5.94 4.909 10.757 10.964 10.757z" fill="url(#b)"/><path fill-rule="evenodd" clip-rule="evenodd" d="M111.6 20.321c0 11.223-7.155 16.879-18.594 16.879-11.44 0-18.606-5.656-18.606-16.879C74.4 9.098 81.566 0 93.006 0 104.445 0 111.6 9.098 111.6 20.321zM93.006 31.078c6.055 0 10.964-4.816 10.964-10.757 0-5.94-4.909-10.757-10.964-10.757-6.055 0-10.964 4.816-10.964 10.757 0 5.94 4.909 10.757 10.964 10.757z" fill="url(#c)"/><path d="M40.532 46.278c-6.194-2.576-12.86-2.58-19.056-.01-6.195 2.568-11.604 7.578-15.458 14.32C2.163 67.328.058 75.458 0 83.824c-.057 8.366 1.938 16.542 5.7 23.368 3.763 6.827 9.102 11.958 15.262 14.665 6.159 2.707 12.825 2.853 19.053.417 6.227-2.436 11.7-7.33 15.641-13.988.125-.211.238-.429.339-.653l.012.01 19.878-39.588-.016-.014.01-.016c2.64-4.624 6.344-8.062 10.59-9.827 4.245-1.765 8.814-1.768 13.06-.007 4.247 1.76 7.954 5.195 10.596 9.815 2.643 4.621 4.085 10.194 4.124 15.927.039 5.734-1.328 11.339-3.907 16.018-2.579 4.679-6.239 8.196-10.46 10.052-4.222 1.855-8.791 1.955-13.06.286-4.269-1.67-8.02-5.025-10.721-9.588-1.63-2.754-4.688-3.288-6.83-1.193-2.143 2.094-2.559 6.025-.929 8.778 3.941 6.658 9.414 11.552 15.641 13.988 6.228 2.436 12.894 2.29 19.053-.417 6.16-2.707 11.499-7.838 15.262-14.665 3.762-6.826 5.757-15.002 5.7-23.368-.057-8.365-2.162-16.495-6.017-23.236-3.854-6.742-9.263-11.752-15.458-14.32-6.195-2.57-12.862-2.566-19.056.01-6.193 2.575-11.599 7.591-15.449 14.336-.12.21-.228.426-.324.647l-19.842 39.515c-2.698 4.525-6.431 7.852-10.676 9.513-4.269 1.669-8.838 1.569-13.06-.286-4.222-1.856-7.881-5.373-10.46-10.052-2.58-4.68-3.946-10.283-3.907-16.017.039-5.734 1.481-11.307 4.123-15.928 2.643-4.62 6.35-8.055 10.596-9.815 4.247-1.76 8.816-1.758 13.062.007 4.245 1.765 7.95 5.203 10.589 9.827 1.592 2.79 4.643 3.392 6.814 1.346 2.17-2.047 2.639-5.967 1.047-8.757-3.85-6.745-9.256-11.761-15.45-14.336z" fill="url(#d)"/></g><defs><linearGradient id="b" x1="0" y1="59.52" x2="124" y2="58.28" gradientUnits="userSpaceOnUse"><stop stop-color="#F7AA0F"/><stop offset=".38" stop-color="#F7AA10"/><stop offset=".646" stop-color="#1116B0"/><stop offset="1" stop-color="#0F15B0"/></linearGradient><linearGradient id="c" x1="0" y1="59.52" x2="124" y2="58.28" gradientUnits="userSpaceOnUse"><stop stop-color="#F7AA0F"/><stop offset=".38" stop-color="#F7AA10"/><stop offset=".646" stop-color="#1116B0"/><stop offset="1" stop-color="#0F15B0"/></linearGradient><linearGradient id="d" x1="0" y1="59.52" x2="124" y2="58.28" gradientUnits="userSpaceOnUse"><stop stop-color="#F7AA0F"/><stop offset=".38" stop-color="#F7AA10"/><stop offset=".646" stop-color="#1116B0"/><stop offset="1" stop-color="#0F15B0"/></linearGradient><filter id="a" x="0" y="0" width="126.2" height="126.2" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset dx="2.2" dy="2.2"/><feGaussianBlur stdDeviation="1.305"/><feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.16 0"/><feBlend in2="shape" result="effect1_innerShadow_3251_49931"/></filter></defs></svg>