kits/documents/tabs/settings-custom-css.php 0000644 00000001300 14720700033 0015105 0 ustar 00 controls_manager->add_custom_css_controls( $this->parent, $this->get_id() );
}
}
kits/documents/tabs/settings-background.php 0000644 00000004045 14720700033 0015135 0 ustar 00 start_controls_section(
'section_background',
[
'label' => $this->get_title(),
'tab' => $this->get_id(),
]
);
$this->add_group_control(
Group_Control_Background::get_type(),
[
'name' => 'body_background',
'types' => [ 'classic', 'gradient' ],
'selector' => '{{WRAPPER}}',
'fields_options' => [
'background' => [
'frontend_available' => true,
],
'color' => [
'dynamic' => [],
],
'color_b' => [
'dynamic' => [],
],
],
]
);
$this->add_control(
'mobile_browser_background',
[
'label' => esc_html__( 'Mobile Browser Background', 'elementor' ),
'type' => Controls_Manager::COLOR,
'description' => esc_html__( 'The `theme-color` meta tag will only be available in supported browsers and devices.', 'elementor' ),
'separator' => 'before',
]
);
$this->add_control(
'body_overscroll_behavior',
[
'label' => esc_html__( 'Overscroll Behavior', 'elementor' ),
'type' => Controls_Manager::SELECT,
'options' => [
'' => esc_html__( 'Default', 'elementor' ),
'none' => esc_html__( 'None', 'elementor' ),
'auto' => esc_html__( 'Auto', 'elementor' ),
'contain' => esc_html__( 'Contain', 'elementor' ),
],
'separator' => 'before',
'selectors' => [
'{{WRAPPER}}' => 'overscroll-behavior: {{VALUE}};',
],
]
);
$this->end_controls_section();
}
}
kits/documents/tabs/theme-style-form-fields.php 0000644 00000012470 14720700033 0015626 0 ustar 00 start_controls_section(
'section_form_fields',
[
'label' => esc_html__( 'Form Fields', 'elementor' ),
'tab' => $this->get_id(),
]
);
$this->add_default_globals_notice();
$this->add_control(
'form_label_heading',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Label', 'elementor' ),
]
);
$this->add_control(
'form_label_color',
[
'label' => esc_html__( 'Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$label_selector => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'form_label_typography',
'selector' => $label_selector,
]
);
$this->add_control(
'form_field_heading',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Field', 'elementor' ),
'separator' => 'before',
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'form_field_typography',
'selector' => $input_selector,
]
);
$this->start_controls_tabs( 'tabs_form_field_style' );
$this->start_controls_tab(
'tab_form_field_normal',
[
'label' => esc_html__( 'Normal', 'elementor' ),
]
);
$this->add_form_field_state_tab_controls( 'form_field', $input_selector );
$this->end_controls_tab();
$this->start_controls_tab(
'tab_form_field_focus',
[
'label' => esc_html__( 'Focus', 'elementor' ),
]
);
$this->add_form_field_state_tab_controls( 'form_field_focus', $input_focus_selector );
$this->add_control(
'form_field_focus_transition_duration',
[
'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (ms)',
'type' => Controls_Manager::SLIDER,
'selectors' => [
$input_selector => 'transition: {{SIZE}}ms',
],
'range' => [
'px' => [
'min' => 0,
'max' => 3000,
'step' => 100,
],
],
]
);
$this->end_controls_tab();
$this->end_controls_tabs();
$this->add_responsive_control(
'form_field_padding',
[
'label' => esc_html__( 'Padding', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'selectors' => [
$input_selector => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
'separator' => 'before',
]
);
$this->end_controls_section();
}
private function add_form_field_state_tab_controls( $prefix, $selector ) {
$this->add_control(
$prefix . '_text_color',
[
'label' => esc_html__( 'Text Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$selector => 'color: {{VALUE}};',
],
]
);
$this->add_control(
$prefix . '_accent_color',
[
'label' => esc_html__( 'Accent Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$selector => 'accent-color: {{VALUE}};',
],
]
);
$this->add_control(
$prefix . '_background_color',
[
'label' => esc_html__( 'Background Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$selector => 'background-color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Box_Shadow::get_type(),
[
'name' => $prefix . '_box_shadow',
'selector' => $selector,
]
);
$this->add_group_control(
Group_Control_Border::get_type(),
[
'name' => $prefix . '_border',
'selector' => $selector,
'fields_options' => [
'color' => [
'dynamic' => [],
],
],
]
);
$this->add_control(
$prefix . '_border_radius',
[
'label' => esc_html__( 'Border Radius', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
'selectors' => [
$selector => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
]
);
}
}
kits/documents/tabs/theme-style-buttons.php 0000644 00000013121 14720700033 0015107 0 ustar 00 start_controls_section(
'section_buttons',
[
'label' => esc_html__( 'Buttons', 'elementor' ),
'tab' => $this->get_id(),
]
);
$this->add_default_globals_notice();
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'button_typography',
'selector' => $button_selector,
]
);
$this->add_group_control(
Group_Control_Text_Shadow::get_type(),
[
'name' => 'button_text_shadow',
'selector' => $button_selector,
]
);
$this->start_controls_tabs( 'tabs_button_style' );
$this->start_controls_tab(
'tab_button_normal',
[
'label' => esc_html__( 'Normal', 'elementor' ),
]
);
$this->add_control(
'button_text_color',
[
'label' => esc_html__( 'Text Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$button_selector => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Background::get_type(),
[
'name' => 'button_background',
'types' => [ 'classic', 'gradient' ],
'exclude' => [ 'image' ],
'selector' => $button_selector,
'fields_options' => [
'background' => [
'default' => 'classic',
],
'color' => [
'dynamic' => [],
],
'color_b' => [
'dynamic' => [],
],
],
]
);
$this->add_group_control(
Group_Control_Box_Shadow::get_type(),
[
'name' => 'button_box_shadow',
'selector' => $button_selector,
]
);
$this->add_group_control(
Group_Control_Border::get_type(),
[
'name' => 'button_border',
'selector' => $button_selector,
'fields_options' => [
'color' => [
'dynamic' => [],
],
],
]
);
$this->add_control(
'button_border_radius',
[
'label' => esc_html__( 'Border Radius', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
'selectors' => [
$button_selector => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
]
);
$this->end_controls_tab();
$this->start_controls_tab(
'tab_button_hover',
[
'label' => esc_html__( 'Hover', 'elementor' ),
]
);
$this->add_control(
'button_hover_text_color',
[
'label' => esc_html__( 'Text Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$button_hover_selector => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Background::get_type(),
[
'name' => 'button_hover_background',
'types' => [ 'classic', 'gradient' ],
'exclude' => [ 'image' ],
'selector' => $button_hover_selector,
'fields_options' => [
'background' => [
'default' => 'classic',
],
'color' => [
'dynamic' => [],
],
'color_b' => [
'dynamic' => [],
],
],
]
);
$this->add_group_control(
Group_Control_Box_Shadow::get_type(),
[
'name' => 'button_hover_box_shadow',
'selector' => $button_hover_selector,
]
);
$this->add_group_control(
Group_Control_Border::get_type(),
[
'name' => 'button_hover_border',
'selector' => $button_hover_selector,
'fields_options' => [
'color' => [
'dynamic' => [],
],
],
]
);
$this->add_control(
'button_hover_border_radius',
[
'label' => esc_html__( 'Border Radius', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
'selectors' => [
$button_hover_selector => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
]
);
$this->end_controls_tab();
$this->end_controls_tabs();
$this->add_responsive_control(
'button_padding',
[
'label' => esc_html__( 'Padding', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'selectors' => [
$button_selector => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
'separator' => 'before',
]
);
$this->end_controls_section();
}
}
kits/documents/tabs/theme-style-typography.php 0000644 00000010676 14720700033 0015633 0 ustar 00 start_controls_section(
'section_typography',
[
'label' => esc_html__( 'Typography', 'elementor' ),
'tab' => $this->get_id(),
]
);
$this->add_default_globals_notice();
$this->add_control(
'body_heading',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Body', 'elementor' ),
]
);
$this->add_control(
'body_color',
[
'label' => esc_html__( 'Text Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
'{{WRAPPER}}' => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'body_typography',
'selector' => '{{WRAPPER}}',
]
);
$this->add_responsive_control(
'paragraph_spacing',
[
'label' => esc_html__( 'Paragraph Spacing', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'selectors' => [
'{{WRAPPER}} p' => 'margin-bottom: {{SIZE}}{{UNIT}}',
],
'range' => [
'px' => [
'max' => 100,
],
'em' => [
'min' => 0.1,
'max' => 20,
],
],
'size_units' => [ 'px', 'em', 'rem', 'vh', 'custom' ],
]
);
//Link Selectors
$link_selectors = [
'{{WRAPPER}} a',
];
$link_hover_selectors = [
'{{WRAPPER}} a:hover',
];
$link_selectors = implode( ',', $link_selectors );
$link_hover_selectors = implode( ',', $link_hover_selectors );
$this->add_control(
'link_heading',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Link', 'elementor' ),
'separator' => 'before',
]
);
$this->start_controls_tabs( 'tabs_link_style' );
$this->start_controls_tab(
'tab_link_normal',
[
'label' => esc_html__( 'Normal', 'elementor' ),
]
);
$this->add_control(
'link_normal_color',
[
'label' => esc_html__( 'Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$link_selectors => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'link_normal_typography',
'selector' => $link_selectors,
]
);
$this->end_controls_tab();
$this->start_controls_tab(
'tab_link_hover',
[
'label' => esc_html__( 'Hover', 'elementor' ),
]
);
$this->add_control(
'link_hover_color',
[
'label' => esc_html__( 'Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$link_hover_selectors => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'link_hover_typography',
'selector' => $link_hover_selectors,
]
);
$this->end_controls_tab();
$this->end_controls_tabs();
// Headings.
$this->add_element_controls( 'H1', 'h1', '{{WRAPPER}} h1' );
$this->add_element_controls( 'H2', 'h2', '{{WRAPPER}} h2' );
$this->add_element_controls( 'H3', 'h3', '{{WRAPPER}} h3' );
$this->add_element_controls( 'H4', 'h4', '{{WRAPPER}} h4' );
$this->add_element_controls( 'H5', 'h5', '{{WRAPPER}} h5' );
$this->add_element_controls( 'H6', 'h6', '{{WRAPPER}} h6' );
$this->end_controls_section();
}
private function add_element_controls( $label, $prefix, $selector ) {
$this->add_control(
$prefix . '_heading',
[
'type' => Controls_Manager::HEADING,
'label' => $label,
'separator' => 'before',
]
);
$this->add_control(
$prefix . '_color',
[
'label' => esc_html__( 'Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'dynamic' => [],
'selectors' => [
$selector => 'color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => $prefix . '_typography',
'selector' => $selector,
]
);
}
}
kits/documents/tabs/global-typography.php 0000644 00000012343 14720700033 0014624 0 ustar 00 start_controls_section(
'section_text_style',
[
'label' => esc_html__( 'Global Fonts', 'elementor' ),
'tab' => $this->get_id(),
]
);
$repeater = new Repeater();
$repeater->add_control(
'title',
[
'type' => Controls_Manager::TEXT,
'label_block' => true,
'required' => true,
]
);
$repeater->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => self::TYPOGRAPHY_NAME,
'label' => '',
'global' => [
'active' => false,
],
'fields_options' => [
'font_family' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-font-family: "{{VALUE}}"',
],
],
'font_size' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-font-size: {{SIZE}}{{UNIT}}',
],
],
'font_weight' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-font-weight: {{VALUE}}',
],
],
'text_transform' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-text-transform: {{VALUE}}',
],
],
'font_style' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-font-style: {{VALUE}}',
],
],
'text_decoration' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-text-decoration: {{VALUE}}',
],
],
'line_height' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-line-height: {{SIZE}}{{UNIT}}',
],
],
'letter_spacing' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-letter-spacing: {{SIZE}}{{UNIT}}',
],
],
'word_spacing' => [
'selectors' => [
'{{SELECTOR}}' => '--e-global-typography-{{external._id.VALUE}}-word-spacing: {{SIZE}}{{UNIT}}',
],
],
],
]
);
$typography_key = self::TYPOGRAPHY_GROUP_PREFIX . 'typography';
$font_family_key = self::TYPOGRAPHY_GROUP_PREFIX . 'font_family';
$font_weight_key = self::TYPOGRAPHY_GROUP_PREFIX . 'font_weight';
$default_typography = [
[
'_id' => 'primary',
'title' => esc_html__( 'Primary', 'elementor' ),
$typography_key => 'custom',
$font_family_key => 'Roboto',
$font_weight_key => '600',
],
[
'_id' => 'secondary',
'title' => esc_html__( 'Secondary', 'elementor' ),
$typography_key => 'custom',
$font_family_key => 'Roboto Slab',
$font_weight_key => '400',
],
[
'_id' => 'text',
'title' => esc_html__( 'Text', 'elementor' ),
$typography_key => 'custom',
$font_family_key => 'Roboto',
$font_weight_key => '400',
],
[
'_id' => 'accent',
'title' => esc_html__( 'Accent', 'elementor' ),
$typography_key => 'custom',
$font_family_key => 'Roboto',
$font_weight_key => '500',
],
];
$this->add_control(
'heading_system_typography',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'System Fonts', 'elementor' ),
]
);
$this->add_control(
'system_typography',
[
'type' => Global_Style_Repeater::CONTROL_TYPE,
'fields' => $repeater->get_controls(),
'default' => $default_typography,
'item_actions' => [
'add' => false,
'remove' => false,
],
'separator' => 'after',
]
);
$this->add_control(
'heading_custom_typography',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Custom Fonts', 'elementor' ),
]
);
$this->add_control(
'custom_typography',
[
'type' => Global_Style_Repeater::CONTROL_TYPE,
'fields' => $repeater->get_controls(),
]
);
$this->add_control(
'default_generic_fonts',
[
'label' => esc_html__( 'Fallback Font Family', 'elementor' ),
'type' => Controls_Manager::TEXT,
'default' => 'Sans-serif',
'description' => esc_html__( 'The list of fonts used if the chosen font is not available.', 'elementor' ),
'label_block' => true,
'separator' => 'before',
'ai' => [
'active' => false,
],
]
);
$this->end_controls_section();
}
}
kits/documents/tabs/tab-base.php 0000644 00000004224 14720700033 0012635 0 ustar 00 register_tab();
$this->register_tab_controls();
}
public function on_save( $data ) {}
/**
* Before Save
*
* Allows for modifying the kit data before it is saved to the database.
*
* @param array $data
* @return array
*/
public function before_save( array $data ) {
return $data;
}
protected function register_tab() {
Controls_Manager::add_tab( $this->get_id(), $this->get_title() );
}
protected function add_default_globals_notice() {
// Get the current section config (array - section id and tab) to use for creating a unique control ID and name
$current_section = $this->parent->get_current_section();
/** @var Manager $module */
$kits_manager = Plugin::$instance->kits_manager;
if ( $kits_manager->is_custom_colors_enabled() || $kits_manager->is_custom_typography_enabled() ) {
$this->add_control(
$current_section['section'] . '_schemes_notice',
[
'name' => $current_section['section'] . '_schemes_notice',
'type' => Controls_Manager::ALERT,
'alert_type' => 'warning',
'content' => sprintf(
/* translators: 1: Link open tag, 2: Link close tag. */
esc_html__( 'In order for Theme Style to affect all relevant Elementor elements, please disable Default Colors and Fonts from the %1$sSettings Page%2$s.', 'elementor' ),
'',
''
),
'render_type' => 'ui',
]
);
}
}
}
kits/documents/tabs/settings-site-identity.php 0000644 00000010421 14720700033 0015604 0 ustar 00 start_controls_section(
'section_' . $this->get_id(),
[
'label' => $this->get_title(),
'tab' => $this->get_id(),
]
);
$this->add_control(
$this->get_id() . '_refresh_notice',
[
'type' => Controls_Manager::ALERT,
'alert_type' => 'info',
'content' => sprintf(
/* translators: 1: Link open tag, 2: Link open tag, 3: Link close tag. */
esc_html__( 'Changes will be reflected only after %1$s saving %3$s and %2$s reloading %3$s preview.', 'elementor' ),
'',
'',
''
),
]
);
$this->add_control(
'site_name',
[
'label' => esc_html__( 'Site Name', 'elementor' ),
'default' => get_option( 'blogname' ),
'placeholder' => esc_html__( 'Choose name', 'elementor' ),
'label_block' => true,
'export' => false,
]
);
$this->add_control(
'site_description',
[
'label' => esc_html__( 'Site Description', 'elementor' ),
'default' => get_option( 'blogdescription' ),
'placeholder' => esc_html__( 'Choose description', 'elementor' ),
'label_block' => true,
'export' => false,
]
);
$this->add_control(
'site_logo',
[
'label' => esc_html__( 'Site Logo', 'elementor' ),
'type' => Controls_Manager::MEDIA,
'should_include_svg_inline_option' => $should_include_svg_inline_option,
'default' => [
'id' => $custom_logo_id,
'url' => $custom_logo_src ? $custom_logo_src[0] : '',
],
'description' => sprintf(
/* translators: 1: Width number pixel, 2: Height number pixel. */
esc_html__( 'Suggested image dimensions: %1$s × %2$s pixels.', 'elementor' ),
'350',
'100'
),
'export' => false,
]
);
$this->add_control(
'site_favicon',
[
'label' => esc_html__( 'Site Favicon', 'elementor' ),
'type' => Controls_Manager::MEDIA,
'should_include_svg_inline_option' => $should_include_svg_inline_option,
'default' => [
'id' => $site_icon_id,
'url' => $site_icon_src ? $site_icon_src[0] : '',
],
'description' => esc_html__( 'Suggested favicon dimensions: 512 × 512 pixels.', 'elementor' ),
'export' => false,
]
);
$this->end_controls_section();
}
public function on_save( $data ) {
if (
! isset( $data['settings']['post_status'] ) ||
Document::STATUS_PUBLISH !== $data['settings']['post_status'] ||
// Should check for the current action to avoid infinite loop
// when updating options like: "blogname" and "blogdescription".
strpos( current_action(), 'update_option_' ) === 0
) {
return;
}
if ( isset( $data['settings']['site_name'] ) ) {
update_option( 'blogname', $data['settings']['site_name'] );
}
if ( isset( $data['settings']['site_description'] ) ) {
update_option( 'blogdescription', $data['settings']['site_description'] );
}
if ( isset( $data['settings']['site_logo'] ) ) {
set_theme_mod( 'custom_logo', $data['settings']['site_logo']['id'] );
}
if ( isset( $data['settings']['site_favicon'] ) ) {
update_option( 'site_icon', $data['settings']['site_favicon']['id'] );
}
}
}
kits/documents/tabs/settings-page-transitions.php 0000644 00000001373 14720700033 0016306 0 ustar 00 controls_manager->add_page_transitions_controls( $this->parent, $this->get_id() );
}
}
kits/documents/tabs/settings-layout.php 0000644 00000027561 14720700033 0014343 0 ustar 00 start_controls_section(
'section_' . $this->get_id(),
[
'label' => esc_html__( 'Layout Settings', 'elementor' ),
'tab' => $this->get_id(),
]
);
$this->add_responsive_control(
'container_width',
[
'label' => esc_html__( 'Content Width', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'default' => [
'size' => 1140,
],
'tablet_default' => [
'size' => $breakpoints_default_config[ $breakpoint_key_tablet ]['default_value'],
],
'mobile_default' => [
'size' => $breakpoints_default_config[ $breakpoint_key_mobile ]['default_value'],
],
'range' => [
'px' => [
'min' => 300,
'max' => 1500,
'step' => 10,
],
],
'description' => esc_html__( 'Sets the default width of the content area (Default: 1140px)', 'elementor' ),
'selectors' => [
'.elementor-section.elementor-section-boxed > .elementor-container' => 'max-width: {{SIZE}}{{UNIT}}',
'.e-con' => '--container-max-width: {{SIZE}}{{UNIT}}',
],
]
);
$is_container_active = Plugin::instance()->experiments->is_feature_active( 'container' );
if ( $is_container_active ) {
$this->add_responsive_control(
'container_padding',
[
'label' => esc_html__( 'Container Padding', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'description' => esc_html__( 'Sets the default space inside the container (Default is 10px)', 'elementor' ),
'selectors' => [
'.e-con' => '--container-default-padding-top: {{TOP}}{{UNIT}}; --container-default-padding-right: {{RIGHT}}{{UNIT}}; --container-default-padding-bottom: {{BOTTOM}}{{UNIT}}; --container-default-padding-left: {{LEFT}}{{UNIT}};',
],
]
);
}
$widgets_space_label = $is_container_active
? esc_html__( 'Gaps', 'elementor' )
: esc_html__( 'Widgets Space', 'elementor' );
$this->add_control(
'space_between_widgets',
[
'label' => $widgets_space_label,
'type' => Controls_Manager::GAPS,
'default' => [
'row' => '20',
'column' => '20',
'unit' => 'px',
],
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'placeholder' => [
'row' => '20',
'column' => '20',
],
'description' => esc_html__( 'Sets the default space between widgets (Default: 20px)', 'elementor' ),
'selectors' => [
'.elementor-widget:not(:last-child)' => 'margin-block-end: {{ROW}}{{UNIT}}',
'.elementor-element' => '--widgets-spacing: {{ROW}}{{UNIT}} {{COLUMN}}{{UNIT}}',
],
'conversion_map' => [
'old_key' => 'size',
'new_key' => 'column',
],
'upgrade_conversion_map' => [
'old_key' => 'size',
'new_keys' => [ 'column', 'row' ],
],
'validators' => [
'Number' => [
'min' => 0,
],
],
]
);
$this->add_control(
'page_title_selector',
[
'label' => esc_html__( 'Page Title Selector', 'elementor' ),
'type' => Controls_Manager::TEXT,
'default' => 'h1.entry-title',
'placeholder' => 'h1.entry-title',
'description' => esc_html__( 'Elementor lets you hide the page title. This works for themes that have "h1.entry-title" selector. If your theme\'s selector is different, please enter it above.', 'elementor' ),
'label_block' => true,
'ai' => [
'active' => false,
],
'selectors' => [
// Hack to convert the value into a CSS selector.
'' => '}{{VALUE}}{display: var(--page-title-display)',
],
]
);
$this->add_control(
'stretched_section_container',
[
'label' => esc_html__( 'Stretched Section Fit To', 'elementor' ),
'type' => Controls_Manager::TEXT,
'placeholder' => 'body',
'description' => esc_html__( 'Enter parent element selector to which stretched sections will fit to (e.g. #primary / .wrapper / main etc). Leave blank to fit to page width.', 'elementor' ),
'label_block' => true,
'frontend_available' => true,
'ai' => [
'active' => false,
],
]
);
/**
* @var PageTemplatesModule $page_templates_module
*/
$page_templates_module = Plugin::$instance->modules_manager->get_modules( 'page-templates' );
$page_templates = $page_templates_module->add_page_templates( [], null, null );
// Removes the Theme option from the templates because 'default' is already handled.
unset( $page_templates[ PageTemplatesModule::TEMPLATE_THEME ] );
$page_template_control_options = [
'label' => esc_html__( 'Default Page Layout', 'elementor' ),
'options' => [
// This is here because the "Theme" string is different than the default option's string.
'default' => esc_html__( 'Theme', 'elementor' ),
] + $page_templates,
];
$page_templates_module->add_template_controls( $this->parent, 'default_page_template', $page_template_control_options );
$this->end_controls_section();
$this->start_controls_section(
'section_breakpoints',
[
'label' => esc_html__( 'Breakpoints', 'elementor' ),
'tab' => $this->get_id(),
]
);
$prefix = Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX;
$options = [];
foreach ( $breakpoints_default_config as $breakpoint_key => $breakpoint ) {
$options[ $prefix . $breakpoint_key ] = $breakpoint['label'];
}
if ( Plugin::$instance->experiments->is_feature_active( 'additional_custom_breakpoints' ) ) {
$active_breakpoints_control_type = Controls_Manager::SELECT2;
} else {
$active_breakpoints_control_type = Controls_Manager::HIDDEN;
}
$this->add_control(
self::ACTIVE_BREAKPOINTS_CONTROL_ID,
[
'label' => esc_html__( 'Active Breakpoints', 'elementor' ),
'type' => $active_breakpoints_control_type,
'description' => esc_html__( 'Mobile and Tablet options cannot be deleted.', 'elementor' ),
'options' => $options,
'default' => [
$prefix . $breakpoint_key_mobile,
$prefix . $breakpoint_key_tablet,
],
'select2options' => [
'allowClear' => false,
],
'lockedOptions' => [
$prefix . $breakpoint_key_mobile,
$prefix . $breakpoint_key_tablet,
],
'label_block' => true,
'render_type' => 'none',
'frontend_available' => true,
'multiple' => true,
]
);
$this->add_breakpoints_controls();
// Include the old mobile and tablet breakpoint controls as hidden for backwards compatibility.
$this->add_control( 'viewport_md', [ 'type' => Controls_Manager::HIDDEN ] );
$this->add_control( 'viewport_lg', [ 'type' => Controls_Manager::HIDDEN ] );
$this->end_controls_section();
}
/**
* Before Save
*
* Runs Before the Kit document is saved.
*
* For backwards compatibility, when the mobile and tablet breakpoints are updated, we also update the
* old breakpoint settings ('viewport_md', 'viewport_lg' ) with the saved values + 1px. The reason 1px
* is added is because the old breakpoints system was min-width based, and the new system introduced in
* Elementor v3.2.0 is max-width based.
*
* @since 3.2.0
*
* @param array $data
* @return array $data
*/
public function before_save( array $data ) {
// When creating a default kit, $data['settings'] is empty and should remain empty, so settings.
if ( empty( $data['settings'] ) ) {
return $data;
}
$prefix = Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX;
$mobile_breakpoint_key = $prefix . Breakpoints_Manager::BREAKPOINT_KEY_MOBILE;
$tablet_breakpoint_key = $prefix . Breakpoints_Manager::BREAKPOINT_KEY_TABLET;
$default_breakpoint_config = Breakpoints_Manager::get_default_config();
// Update the old mobile breakpoint. If the setting is empty, use the default value.
$data['settings'][ $prefix . 'md' ] = empty( $data['settings'][ $mobile_breakpoint_key ] )
? $default_breakpoint_config[ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE ]['default_value'] + 1
: $data['settings'][ $mobile_breakpoint_key ] + 1;
// Update the old tablet breakpoint. If the setting is empty, use the default value.
$data['settings'][ $prefix . 'lg' ] = empty( $data['settings'][ $tablet_breakpoint_key ] )
? $default_breakpoint_config[ Breakpoints_Manager::BREAKPOINT_KEY_TABLET ]['default_value'] + 1
: $data['settings'][ $tablet_breakpoint_key ] + 1;
return $data;
}
public function on_save( $data ) {
if ( ! isset( $data['settings'] ) || ( isset( $data['settings']['post_status'] ) && Document::STATUS_PUBLISH !== $data['settings']['post_status'] ) ) {
return;
}
$should_compile_css = false;
$breakpoints_default_config = Breakpoints_Manager::get_default_config();
foreach ( $breakpoints_default_config as $breakpoint_key => $default_config ) {
$breakpoint_setting_key = Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX . $breakpoint_key;
if ( isset( $data['settings'][ $breakpoint_setting_key ] ) ) {
$should_compile_css = true;
}
}
if ( $should_compile_css ) {
Breakpoints_Manager::compile_stylesheet_templates();
}
}
private function add_breakpoints_controls() {
$default_breakpoints_config = Breakpoints_Manager::get_default_config();
$prefix = Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX;
// If the ACB experiment is inactive, only add the mobile and tablet controls.
if ( ! Plugin::$instance->experiments->is_feature_active( 'additional_custom_breakpoints' ) ) {
$default_breakpoints_config = array_intersect_key( $default_breakpoints_config, array_flip( [ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE, Breakpoints_Manager::BREAKPOINT_KEY_TABLET ] ) );
}
// Add a control for each of the **default** breakpoints.
foreach ( $default_breakpoints_config as $breakpoint_key => $default_breakpoint_config ) {
$this->add_control(
'breakpoint_' . $breakpoint_key . '_heading',
[
'label' => $default_breakpoint_config['label'],
'type' => Controls_Manager::HEADING,
'separator' => 'before',
'conditions' => [
'terms' => [
[
'name' => 'active_breakpoints',
'operator' => 'contains',
'value' => $prefix . $breakpoint_key,
],
],
],
]
);
$control_config = [
'label' => esc_html__( 'Breakpoint', 'elementor' ) . ' (px)',
'type' => Controls_Manager::NUMBER,
'placeholder' => $default_breakpoint_config['default_value'],
'frontend_available' => true,
'validators' => [
'Breakpoint' => [
'breakpointName' => $breakpoint_key,
],
],
'conditions' => [
'terms' => [
[
'name' => 'active_breakpoints',
'operator' => 'contains',
'value' => $prefix . $breakpoint_key,
],
],
],
];
if ( Breakpoints_Manager::BREAKPOINT_KEY_WIDESCREEN === $breakpoint_key ) {
$control_config['description'] = esc_html__(
'Widescreen breakpoint settings will apply from the selected value and up.',
'elementor'
);
}
// Add the breakpoint Control itself.
$this->add_control( $prefix . $breakpoint_key, $control_config );
}
}
}
kits/documents/tabs/settings-lightbox.php 0000644 00000011400 14720700033 0014627 0 ustar 00 start_controls_section(
'section_' . $this->get_id(),
[
'label' => $this->get_title(),
'tab' => $this->get_id(),
]
);
$this->add_control(
'global_image_lightbox',
[
'label' => esc_html__( 'Image Lightbox', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'default' => 'yes',
'description' => esc_html__( 'Open all image links in a lightbox popup window. The lightbox will automatically work on any link that leads to an image file.', 'elementor' ),
'frontend_available' => true,
]
);
$this->add_control(
'lightbox_enable_counter',
[
'label' => esc_html__( 'Counter', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'default' => 'yes',
'frontend_available' => true,
]
);
$this->add_control(
'lightbox_enable_fullscreen',
[
'label' => esc_html__( 'Fullscreen', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'default' => 'yes',
'frontend_available' => true,
]
);
$this->add_control(
'lightbox_enable_zoom',
[
'label' => esc_html__( 'Zoom', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'default' => 'yes',
'frontend_available' => true,
]
);
$this->add_control(
'lightbox_enable_share',
[
'label' => esc_html__( 'Share', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'default' => 'yes',
'frontend_available' => true,
]
);
$this->add_control(
'lightbox_title_src',
[
'label' => esc_html__( 'Title', 'elementor' ),
'type' => Controls_Manager::SELECT,
'options' => [
'' => esc_html__( 'None', 'elementor' ),
'title' => esc_html__( 'Title', 'elementor' ),
'caption' => esc_html__( 'Caption', 'elementor' ),
'alt' => esc_html__( 'Alt', 'elementor' ),
'description' => esc_html__( 'Description', 'elementor' ),
],
'default' => 'title',
'frontend_available' => true,
]
);
$this->add_control(
'lightbox_description_src',
[
'label' => esc_html__( 'Description', 'elementor' ),
'type' => Controls_Manager::SELECT,
'options' => [
'' => esc_html__( 'None', 'elementor' ),
'title' => esc_html__( 'Title', 'elementor' ),
'caption' => esc_html__( 'Caption', 'elementor' ),
'alt' => esc_html__( 'Alt', 'elementor' ),
'description' => esc_html__( 'Description', 'elementor' ),
],
'default' => 'description',
'frontend_available' => true,
]
);
$this->add_control(
'lightbox_color',
[
'label' => esc_html__( 'Background Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'selectors' => [
'.elementor-lightbox' => 'background-color: {{VALUE}}',
],
]
);
$this->add_control(
'lightbox_ui_color',
[
'label' => esc_html__( 'UI Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'selectors' => [
'.elementor-lightbox' => '--lightbox-ui-color: {{VALUE}}',
],
]
);
$this->add_control(
'lightbox_ui_color_hover',
[
'label' => esc_html__( 'UI Hover Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'selectors' => [
'.elementor-lightbox' => '--lightbox-ui-color-hover: {{VALUE}}',
],
]
);
$this->add_control(
'lightbox_text_color',
[
'label' => esc_html__( 'Text Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'selectors' => [
'.elementor-lightbox' => '--lightbox-text-color: {{VALUE}}',
],
]
);
$this->add_control(
'lightbox_icons_size',
[
'label' => esc_html__( 'Toolbar Icons Size', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ],
'selectors' => [
'.elementor-lightbox' => '--lightbox-header-icons-size: {{SIZE}}{{UNIT}}',
],
'separator' => 'before',
]
);
$this->add_control(
'lightbox_slider_icons_size',
[
'label' => esc_html__( 'Navigation Icons Size', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ],
'selectors' => [
'.elementor-lightbox' => '--lightbox-navigation-icons-size: {{SIZE}}{{UNIT}}',
],
'separator' => 'before',
]
);
$this->end_controls_section();
}
}
kits/documents/tabs/theme-style-images.php 0000644 00000011017 14720700033 0014660 0 ustar 00 start_controls_section(
'section_images',
[
'label' => esc_html__( 'Images', 'elementor' ),
'tab' => $this->get_id(),
]
);
$this->add_default_globals_notice();
$this->start_controls_tabs( 'tabs_image_style' );
$this->start_controls_tab(
'tab_image_normal',
[
'label' => esc_html__( 'Normal', 'elementor' ),
]
);
$this->add_group_control(
Group_Control_Border::get_type(),
[
'name' => 'image_border',
'selector' => $image_selectors,
'fields_options' => [
'color' => [
'dynamic' => [],
],
],
]
);
$this->add_responsive_control(
'image_border_radius',
[
'label' => esc_html__( 'Border Radius', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
'selectors' => [
$image_selectors => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
]
);
$this->add_control(
'image_opacity',
[
'label' => esc_html__( 'Opacity', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'range' => [
'px' => [
'max' => 1,
'min' => 0.10,
'step' => 0.01,
],
],
'selectors' => [
$image_selectors => 'opacity: {{SIZE}};',
],
]
);
$this->add_group_control(
Group_Control_Box_Shadow::get_type(),
[
'name' => 'image_box_shadow',
'exclude' => [
'box_shadow_position',
],
'selector' => $image_selectors,
]
);
$this->add_group_control(
Group_Control_Css_Filter::get_type(),
[
'name' => 'image_css_filters',
'selector' => '{{WRAPPER}} img',
]
);
$this->end_controls_tab();
$this->start_controls_tab(
'tab_image_hover',
[
'label' => esc_html__( 'Hover', 'elementor' ),
]
);
$this->add_group_control(
Group_Control_Border::get_type(),
[
'name' => 'image_hover_border',
'selector' => '{{WRAPPER}} img:hover',
'fields_options' => [
'color' => [
'dynamic' => [],
],
],
]
);
$this->add_responsive_control(
'image_hover_border_radius',
[
'label' => esc_html__( 'Border Radius', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
'selectors' => [
$image_hover_selectors => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
]
);
$this->add_control(
'image_hover_opacity',
[
'label' => esc_html__( 'Opacity', 'elementor' ),
'type' => Controls_Manager::SLIDER,
'range' => [
'px' => [
'max' => 1,
'min' => 0.10,
'step' => 0.01,
],
],
'selectors' => [
$image_hover_selectors => 'opacity: {{SIZE}};',
],
]
);
$this->add_group_control(
Group_Control_Box_Shadow::get_type(),
[
'name' => 'image_hover_box_shadow',
'exclude' => [
'box_shadow_position',
],
'selector' => $image_hover_selectors,
]
);
$this->add_group_control(
Group_Control_Css_Filter::get_type(),
[
'name' => 'image_hover_css_filters',
'selector' => $image_hover_selectors,
]
);
$this->add_control(
'image_hover_transition',
[
'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)',
'type' => Controls_Manager::SLIDER,
'range' => [
'px' => [
'min' => 0,
'max' => 3,
'step' => 0.1,
],
],
'selectors' => [
$image_selectors => 'transition-duration: {{SIZE}}s',
],
]
);
$this->end_controls_tab();
$this->end_controls_tabs();
$this->end_controls_section();
}
}
kits/documents/tabs/global-colors.php 0000644 00000005371 14720700033 0013722 0 ustar 00 start_controls_section(
'section_global_colors',
[
'label' => esc_html__( 'Global Colors', 'elementor' ),
'tab' => $this->get_id(),
]
);
$repeater = new Repeater();
$repeater->add_control(
'title',
[
'type' => Controls_Manager::TEXT,
'label_block' => true,
'required' => true,
]
);
// Color Value
$repeater->add_control(
'color',
[
'type' => Controls_Manager::COLOR,
'label_block' => true,
'selectors' => [
'{{WRAPPER}}' => '--e-global-color-{{_id.VALUE}}: {{VALUE}}',
],
'global' => [
'active' => false,
],
]
);
$default_colors = [
[
'_id' => 'primary',
'title' => esc_html__( 'Primary', 'elementor' ),
'color' => '#6EC1E4',
],
[
'_id' => 'secondary',
'title' => esc_html__( 'Secondary', 'elementor' ),
'color' => '#54595F',
],
[
'_id' => 'text',
'title' => esc_html__( 'Text', 'elementor' ),
'color' => '#7A7A7A',
],
[
'_id' => 'accent',
'title' => esc_html__( 'Accent', 'elementor' ),
'color' => '#61CE70',
],
];
$this->add_control(
'heading_system_colors',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'System Colors', 'elementor' ),
]
);
$this->add_control(
'system_colors',
[
'type' => Global_Style_Repeater::CONTROL_TYPE,
'fields' => $repeater->get_controls(),
'default' => $default_colors,
'item_actions' => [
'add' => false,
'remove' => false,
],
'separator' => 'after',
]
);
$this->add_control(
'heading_custom_colors',
[
'type' => Controls_Manager::HEADING,
'label' => esc_html__( 'Custom Colors', 'elementor' ),
]
);
$this->add_control(
'custom_colors',
[
'type' => Global_Style_Repeater::CONTROL_TYPE,
'fields' => $repeater->get_controls(),
]
);
$this->end_controls_section();
}
}
kits/documents/kit.php 0000644 00000012613 14720700033 0011016 0 ustar 00 register_tabs();
}
public static function get_properties() {
$properties = parent::get_properties();
$properties['has_elements'] = false;
$properties['show_in_finder'] = false;
$properties['show_on_admin_bar'] = false;
$properties['edit_capability'] = 'edit_theme_options';
$properties['support_kit'] = true;
return $properties;
}
public static function get_type() {
return 'kit';
}
public static function get_title() {
return esc_html__( 'Kit', 'elementor' );
}
/**
* @return Tabs\Tab_Base[]
*/
public function get_tabs() {
return $this->tabs;
}
/**
* Retrieve a tab by ID.
*
* @param $id
*
* @return Tabs\Tab_Base
*/
public function get_tab( $id ) {
return self::get_items( $this->get_tabs(), $id );
}
protected function get_have_a_look_url() {
return '';
}
public static function get_editor_panel_config() {
$config = parent::get_editor_panel_config();
$config['default_route'] = 'panel/global/menu';
$config['needHelpUrl'] = 'https://go.elementor.com/global-settings/';
return $config;
}
public function get_css_wrapper_selector() {
return '.elementor-kit-' . $this->get_main_id();
}
public function save( $data ) {
foreach ( $this->tabs as $tab ) {
$data = $tab->before_save( $data );
}
$saved = parent::save( $data );
if ( ! $saved ) {
return false;
}
// Should set is_saving to true, to avoid infinite loop when updating
// settings like: 'site_name" or "site_description".
$this->set_is_saving( true );
foreach ( $this->tabs as $tab ) {
$tab->on_save( $data );
}
$this->set_is_saving( false );
// When deleting a global color or typo, the css variable still exists in the frontend
// but without any value and it makes the element to be un styled even if there is a default style for the base element,
// for that reason this method removes css files of the entire site.
Plugin::instance()->files_manager->clear_cache();
return $saved;
}
/**
* Register a kit settings menu.
*
* @param $id
* @param $class
*/
public function register_tab( $id, $class ) {
$this->tabs[ $id ] = new $class( $this );
}
/**
* @inheritDoc
*/
protected function get_initial_config() {
$config = parent::get_initial_config();
foreach ( $this->tabs as $id => $tab ) {
$config['tabs'][ $id ] = [
'id' => $id,
'title' => $tab->get_title(),
'icon' => $tab->get_icon(),
'group' => $tab->get_group(),
'helpUrl' => $tab->get_help_url(),
'additionalContent' => $tab->get_additional_tab_content(),
];
}
return $config;
}
/**
* @since 3.1.0
* @access protected
*/
protected function register_controls() {
$this->register_document_controls();
foreach ( $this->tabs as $tab ) {
$tab->register_controls();
}
}
protected function get_post_statuses() {
return [
'draft' => sprintf( '%s (%s)', esc_html__( 'Disabled', 'elementor' ), esc_html__( 'Draft', 'elementor' ) ),
'publish' => esc_html__( 'Published', 'elementor' ),
];
}
public function add_repeater_row( $control_id, $item ) {
$meta_key = PageManager::META_KEY;
$document_settings = $this->get_meta( $meta_key );
if ( ! $document_settings ) {
$document_settings = [];
}
if ( ! isset( $document_settings[ $control_id ] ) ) {
$document_settings[ $control_id ] = [];
}
$document_settings[ $control_id ][] = $item;
$page_settings_manager = SettingsManager::get_settings_managers( 'page' );
$page_settings_manager->save_settings( $document_settings, $this->get_id() );
/** @var Kit $autosave **/
$autosave = $this->get_autosave();
if ( $autosave ) {
$autosave->add_repeater_row( $control_id, $item );
}
// Remove Post CSS.
$post_css = Post_CSS::create( $this->post->ID );
$post_css->delete();
// Refresh Cache.
Plugin::$instance->documents->get( $this->post->ID, false );
$post_css = Post_CSS::create( $this->post->ID );
$post_css->enqueue();
}
/**
* Register default tabs (menu pages) for site settings.
*/
private function register_tabs() {
$tabs = [
'global-colors' => Tabs\Global_Colors::class,
'global-typography' => Tabs\Global_Typography::class,
'theme-style-typography' => Tabs\Theme_Style_Typography::class,
'theme-style-buttons' => Tabs\Theme_Style_Buttons::class,
'theme-style-images' => Tabs\Theme_Style_Images::class,
'theme-style-form-fields' => Tabs\Theme_Style_Form_Fields::class,
'settings-site-identity' => Tabs\Settings_Site_Identity::class,
'settings-background' => Tabs\Settings_Background::class,
'settings-layout' => Tabs\Settings_Layout::class,
'settings-lightbox' => Tabs\Settings_Lightbox::class,
'settings-page-transitions' => Tabs\Settings_Page_Transitions::class,
'settings-custom-css' => Tabs\Settings_Custom_CSS::class,
];
foreach ( $tabs as $id => $class ) {
$this->register_tab( $id, $class );
}
do_action( 'elementor/kit/register_tabs', $this );
}
}
kits/manager.php 0000644 00000032462 14720700033 0007644 0 ustar 00 documents->get( $kit_id );
if ( ! $this->is_valid_kit( $kit ) ) {
return $this->get_empty_kit_instance();
}
return $kit;
}
public function get_active_kit() {
return $this->get_kit( $this->get_active_id() );
}
public function get_active_kit_for_frontend() {
$kit = Plugin::$instance->documents->get_doc_for_frontend( $this->get_active_id() );
if ( ! $this->is_valid_kit( $kit ) ) {
return $this->get_empty_kit_instance();
}
return $kit;
}
/**
* @param $kit
*
* @return bool
*/
private function is_valid_kit( $kit ) {
return $kit && $kit instanceof Kit && 'trash' !== $kit->get_main_post()->post_status;
}
/**
* Returns an empty kit for situation when there is no kit in the site.
*
* @return Kit
* @throws \Exception
*/
private function get_empty_kit_instance() {
return new Kit( [
'settings' => [],
'post_id' => 0,
] );
}
/**
* Checks if specific post is a kit.
*
* @param $post_id
*
* @return bool
*/
public function is_kit( $post_id ) {
$document = Plugin::$instance->documents->get( $post_id );
return $document && $document instanceof Kit && ! $document->is_revision();
}
/**
* Init kit controls.
*
* A temp solution in order to avoid init kit group control from within another group control.
*
* After moving the `default_font` to the kit, the Typography group control cause initialize the kit controls at: https://github.com/elementor/elementor/blob/e6e1db9eddef7e3c1a5b2ba0c2338e2af2a3bfe3/includes/controls/groups/typography.php#L91
* and because the group control is a singleton, its args are changed to the last kit group control.
*/
public function init_kit_controls() {
$this->get_active_kit_for_frontend()->get_settings();
}
public function get_current_settings( $setting = null ) {
$kit = $this->get_active_kit_for_frontend();
if ( ! $kit ) {
return '';
}
return $kit->get_settings( $setting );
}
public function create( array $kit_data = [], array $kit_meta_data = [] ) {
$default_kit_data = [
'post_status' => 'publish',
];
$kit_data = array_merge( $default_kit_data, $kit_data );
$kit_data['post_type'] = Source_Local::CPT;
$kit = Plugin::$instance->documents->create( 'kit', $kit_data, $kit_meta_data );
if ( isset( $kit_data['settings'] ) ) {
$kit->save( [ 'settings' => $kit_data['settings'] ] );
}
return $kit->get_id();
}
public function create_new_kit( $kit_name = '', $settings = [], $active = true ) {
$kit_name = $kit_name ? $kit_name : esc_html__( 'Custom', 'elementor' );
$id = $this->create( [
'post_title' => $kit_name,
'settings' => $settings,
] );
if ( $active ) {
update_option( self::OPTION_PREVIOUS, $this->get_active_id() );
update_option( self::OPTION_ACTIVE, $id );
}
return $id;
}
public function create_default() {
return $this->create( [
'post_title' => esc_html__( 'Default Kit', 'elementor' ),
] );
}
/**
* Create a default kit if needed.
*
* This action runs on activation hook, all the Plugin components do not exists and
* the Document manager and Kits manager instances cannot be used.
*
* @return int|void|\WP_Error
*/
public static function create_default_kit() {
if ( get_option( self::OPTION_ACTIVE ) ) {
return;
}
$id = wp_insert_post( [
'post_title' => esc_html__( 'Default Kit', 'elementor' ),
'post_type' => Source_Local::CPT,
'post_status' => 'publish',
'meta_input' => [
'_elementor_edit_mode' => 'builder',
Document::TYPE_META_KEY => 'kit',
],
] );
update_option( self::OPTION_ACTIVE, $id );
return $id;
}
/**
* @param $imported_kit_id int The id of the imported kit that should be deleted.
* @param $active_kit_id int The id of the kit that should set as 'active_kit' after the deletion.
* @param $previous_kit_id int The id of the kit that should set as 'previous_kit' after the deletion.
* @return void
*/
public function revert( int $imported_kit_id, int $active_kit_id, int $previous_kit_id ) {
// If the kit that should set as active is not a valid kit then abort the revert.
if ( ! $this->is_kit( $active_kit_id ) ) {
return;
}
// This a hacky solution to avoid from the revert process to be interrupted by the `trash_kit_confirmation`.
$this->should_skip_trash_kit_confirmation = true;
$kit = $this->get_kit( $imported_kit_id );
$kit->force_delete();
$this->should_skip_trash_kit_confirmation = false;
update_option( self::OPTION_ACTIVE, $active_kit_id );
if ( $this->is_kit( $previous_kit_id ) ) {
update_option( self::OPTION_PREVIOUS, $previous_kit_id );
}
}
/**
* @param Documents_Manager $documents_manager
*/
public function register_document( $documents_manager ) {
$documents_manager->register_document_type( 'kit', Kit::get_class_full_name() );
}
public function localize_settings( $settings ) {
$kit = $this->get_active_kit();
$kit_controls = $kit->get_controls();
$design_system_controls = [
'colors' => $kit_controls['system_colors']['fields'],
'typography' => $kit_controls['system_typography']['fields'],
];
$settings = array_replace_recursive( $settings, [
'kit_id' => $kit->get_main_id(),
'kit_config' => [
'typography_prefix' => Global_Typography::TYPOGRAPHY_GROUP_PREFIX,
'design_system_controls' => $design_system_controls,
],
'user' => [
'can_edit_kit' => $kit->is_editable_by_current_user(),
],
] );
return $settings;
}
public function preview_enqueue_styles() {
$kit = $this->get_kit_for_frontend();
if ( $kit ) {
// On preview, the global style is not enqueued.
$this->frontend_before_enqueue_styles();
Plugin::$instance->frontend->print_fonts_links();
}
}
public function frontend_before_enqueue_styles() {
$kit = $this->get_kit_for_frontend();
if ( $kit ) {
if ( $kit->is_autosave() ) {
$css_file = Post_Preview::create( $kit->get_id() );
} else {
$css_file = Post_CSS::create( $kit->get_id() );
}
$css_file->enqueue();
}
}
public function render_panel_html() {
require __DIR__ . '/views/panel.php';
}
public function get_kit_for_frontend() {
$kit = false;
$active_kit = $this->get_active_kit();
$is_kit_preview = is_preview() && isset( $_GET['preview_id'] ) && $active_kit->get_main_id() === (int) $_GET['preview_id'];
if ( $is_kit_preview ) {
$kit = Plugin::$instance->documents->get_doc_or_auto_save( $active_kit->get_main_id(), get_current_user_id() );
} elseif ( 'publish' === $active_kit->get_main_post()->post_status ) {
$kit = $active_kit;
}
return $kit;
}
public function update_kit_settings_based_on_option( $key, $value ) {
/** @var Kit $active_kit */
$active_kit = $this->get_active_kit();
if ( $active_kit->is_saving() ) {
return;
}
$active_kit->update_settings( [ $key => $value ] );
}
/**
* Map Scheme To Global
*
* Convert a given scheme value to its corresponding default global value
*
* @param string $type 'color'/'typography'
* @param $value
* @return mixed
*/
private function map_scheme_to_global( $type, $value ) {
$schemes_to_globals_map = [
'color' => [
'1' => Global_Colors::COLOR_PRIMARY,
'2' => Global_Colors::COLOR_SECONDARY,
'3' => Global_Colors::COLOR_TEXT,
'4' => Global_Colors::COLOR_ACCENT,
],
'typography' => [
'1' => Global_Typography::TYPOGRAPHY_PRIMARY,
'2' => Global_Typography::TYPOGRAPHY_SECONDARY,
'3' => Global_Typography::TYPOGRAPHY_TEXT,
'4' => Global_Typography::TYPOGRAPHY_ACCENT,
],
];
return $schemes_to_globals_map[ $type ][ $value ];
}
/**
* Convert Scheme to Default Global
*
* If a control has a scheme property, convert it to a default Global.
*
* @param $scheme - Control scheme property
* @return array - Control/group control args
* @since 3.0.0
* @access public
*/
public function convert_scheme_to_global( $scheme ) {
if ( isset( $scheme['type'] ) && isset( $scheme['value'] ) ) {
//_deprecated_argument( $args['scheme'], '3.0.0', 'Schemes are now deprecated - use $args[\'global\'] instead.' );
return $this->map_scheme_to_global( $scheme['type'], $scheme['value'] );
}
// Typography control 'scheme' properties usually only include the string with the typography value ('1'-'4').
return $this->map_scheme_to_global( 'typography', $scheme );
}
public function register_controls() {
$controls_manager = Plugin::$instance->controls_manager;
$controls_manager->register( new Repeater() );
}
public function is_custom_colors_enabled() {
return ! get_option( 'elementor_disable_color_schemes' );
}
public function is_custom_typography_enabled() {
return ! get_option( 'elementor_disable_typography_schemes' );
}
/**
* Add kit wrapper body class.
*
* It should be added even for non Elementor pages,
* in order to support embedded templates.
*/
private function add_body_class() {
$kit = $this->get_kit_for_frontend();
if ( $kit ) {
Plugin::$instance->frontend->add_body_class( 'elementor-kit-' . $kit->get_main_id() );
}
}
/**
* Send a confirm message before move a kit to trash, or if delete permanently not for trash.
*
* @param $post_id
* @param false $is_permanently_delete
*/
private function before_delete_kit( $post_id, $is_permanently_delete = false ) {
if ( $this->should_skip_trash_kit_confirmation ) {
return;
}
$document = Plugin::$instance->documents->get( $post_id );
if (
! $document ||
! $this->is_kit( $post_id ) ||
isset( $_GET['force_delete_kit'] ) || // phpcs:ignore -- nonce validation is not require here.
( $is_permanently_delete && $document->is_trash() )
) {
return;
}
ob_start();
require __DIR__ . '/views/trash-kit-confirmation.php';
$confirmation_content = ob_get_clean();
// PHPCS - the content does not contain user input value.
wp_die( new \WP_Error( 'cant_delete_kit', $confirmation_content ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Add 'Edit with elementor -> Site Settings' in admin bar.
*
* @param [] $admin_bar_config
*
* @return array $admin_bar_config
*/
private function add_menu_in_admin_bar( $admin_bar_config ) {
$document = Plugin::$instance->documents->get( get_the_ID() );
if ( ! $document || ! $document->is_built_with_elementor() ) {
$recent_edited_post = Utils::get_recently_edited_posts_query( [
'posts_per_page' => 1,
] );
if ( $recent_edited_post->post_count ) {
$posts = $recent_edited_post->get_posts();
$document = Plugin::$instance->documents->get( reset( $posts )->ID );
}
}
if ( $document ) {
$document_edit_url = add_query_arg(
[
'active-document' => $this->get_active_id(),
],
$document->get_edit_url()
);
$admin_bar_config['elementor_edit_page']['children'][] = [
'id' => 'elementor_site_settings',
'title' => esc_html__( 'Site Settings', 'elementor' ),
'sub_title' => esc_html__( 'Site', 'elementor' ),
'href' => $document_edit_url,
'class' => 'elementor-site-settings',
'parent_class' => 'elementor-second-section',
];
}
return $admin_bar_config;
}
public function __construct() {
add_action( 'elementor/documents/register', [ $this, 'register_document' ] );
add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
add_filter( 'elementor/editor/footer', [ $this, 'render_panel_html' ] );
add_action( 'elementor/frontend/after_enqueue_styles', [ $this, 'frontend_before_enqueue_styles' ], 0 );
add_action( 'elementor/preview/enqueue_styles', [ $this, 'preview_enqueue_styles' ], 0 );
add_action( 'elementor/controls/register', [ $this, 'register_controls' ] );
add_action( 'wp_trash_post', function ( $post_id ) {
$this->before_delete_kit( $post_id );
} );
add_action( 'before_delete_post', function ( $post_id ) {
$this->before_delete_kit( $post_id, true );
} );
add_action( 'update_option_blogname', function ( $old_value, $value ) {
$this->update_kit_settings_based_on_option( 'site_name', $value );
}, 10, 2 );
add_action( 'update_option_blogdescription', function ( $old_value, $value ) {
$this->update_kit_settings_based_on_option( 'site_description', $value );
}, 10, 2 );
add_action( 'wp_head', function() {
$this->add_body_class();
} );
add_filter( 'elementor/frontend/admin_bar/settings', function ( $admin_bar_config ) {
return $this->add_menu_in_admin_bar( $admin_bar_config );
}, 9 /* Before site-editor (theme-builder) */ );
}
}
kits/views/trash-kit-confirmation.php 0000644 00000003157 14720700033 0013762 0 ustar 00 '1' ],
get_delete_post_link( $post_id, '', $is_permanently_delete )
);
?>
kits/views/panel.php 0000644 00000003213 14720700033 0010456 0 ustar 00
kits/controls/repeater.php 0000644 00000003142 14720700033 0011675 0 ustar 00
<# if ( itemActions.add ) { #>
<# } #>
0,
'sm' => 480,
'md' => 768,
'lg' => 1025,
'xl' => 1440,
'xxl' => 1600,
];
/**
* Editable breakpoint keys.
*
* Holds the editable breakpoint keys.
*
* @since 1.0.0
* @deprecated 3.2.0
* @access private
* @static
*
* @var array Editable breakpoint keys.
*/
private static $editable_breakpoints_keys = [
'md',
'lg',
];
/**
* Get default breakpoints.
*
* Retrieve the default responsive breakpoints.
*
* @since 1.0.0
* @deprecated 3.2.0 Use `Elementor\Core\Breakpoints\Manager::get_default_config()` instead.
* @access public
* @static
*
* @return array Default breakpoints.
*/
public static function get_default_breakpoints() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.2.0', 'Elementor\Core\Breakpoints\Manager::get_default_config()' );
return self::$default_breakpoints;
}
/**
* Get editable breakpoints.
*
* Retrieve the editable breakpoints.
*
* @since 1.0.0
* @deprecated 3.2.0
* @access public
* @static
*
* @return array Editable breakpoints.
*/
public static function get_editable_breakpoints() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.2.0' );
return array_intersect_key( self::get_breakpoints(), array_flip( self::$editable_breakpoints_keys ) );
}
/**
* Get breakpoints.
*
* Retrieve the responsive breakpoints.
*
* @since 1.0.0
* @deprecated 3.2.0
* @access public
* @static
*
* @return array Responsive breakpoints.
*/
public static function get_breakpoints() {
return array_reduce(
array_keys( self::$default_breakpoints ), function( $new_array, $breakpoint_key ) {
if ( ! in_array( $breakpoint_key, self::$editable_breakpoints_keys ) ) {
$new_array[ $breakpoint_key ] = self::$default_breakpoints[ $breakpoint_key ];
} else {
$saved_option = Plugin::$instance->kits_manager->get_current_settings( self::BREAKPOINT_OPTION_PREFIX . $breakpoint_key );
$new_array[ $breakpoint_key ] = $saved_option ? (int) $saved_option : self::$default_breakpoints[ $breakpoint_key ];
}
return $new_array;
}, []
);
}
/**
* @since 2.1.0
* @deprecated 3.2.0 Use `Plugin::$instance->breakpoints->has_custom_breakpoints()` instead.
* @access public
* @static
*/
public static function has_custom_breakpoints() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.2.0', 'Plugin::$instance->breakpoints->has_custom_breakpoints()' );
return ! ! array_diff( self::$default_breakpoints, self::get_breakpoints() );
}
/**
* @since 2.1.0
* @deprecated 3.2.0 Use `Elementor\Core\Breakpoints\Manager::get_stylesheet_templates_path()` instead.
* @access public
* @static
*/
public static function get_stylesheet_templates_path() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.2.0', 'Elementor\Core\Breakpoints\Manager::get_stylesheet_templates_path()' );
return Breakpoints_Manager::get_stylesheet_templates_path();
}
/**
* @since 2.1.0
* @deprecated 3.2.0 Use `Elementor\Core\Breakpoints\Manager::compile_stylesheet_templates()` instead.
* @access public
* @static
*/
public static function compile_stylesheet_templates() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.2.0', 'Elementor\Core\Breakpoints\Manager::compile_stylesheet_templates()' );
Breakpoints_Manager::compile_stylesheet_templates();
}
}
responsive/files/frontend.php 0000644 00000011550 14720700033 0012371 0 ustar 00 template_file = $template_file;
parent::__construct( $file_name );
}
/**
* @since 2.1.0
* @access public
*/
public function parse_content() {
$breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
$breakpoints_keys = array_keys( $breakpoints );
$file_content = Utils::file_get_contents( $this->template_file );
// The regex pattern parses placeholders located in the frontend _templates.scss file.
$file_content = preg_replace_callback( '/ELEMENTOR_SCREEN_([A-Z_]+)(?:_(MIN|MAX|NEXT))/', function ( $placeholder_data ) use ( $breakpoints_keys, $breakpoints ) {
// Handle BC for legacy template files and Elementor Pro builds.
$placeholder_data = $this->maybe_convert_placeholder_data( $placeholder_data );
$breakpoint_index = array_search( strtolower( $placeholder_data[1] ), $breakpoints_keys, true );
if ( 'DESKTOP' === $placeholder_data[1] ) {
if ( 'MIN' === $placeholder_data[2] ) {
$value = Plugin::$instance->breakpoints->get_desktop_min_point();
} elseif ( isset( $breakpoints['widescreen'] ) ) {
// If the 'widescreen' breakpoint is active, the Desktop's max value is the Widescreen breakpoint - 1px.
$value = $breakpoints['widescreen']->get_value() - 1;
} else {
// If the 'widescreen' breakpoint is not active, the Desktop device should not have a max value.
$value = 99999;
}
} elseif ( false === $breakpoint_index ) {
// If the breakpoint in the placeholder is not active - use a -1 value for the media query, to make
// sure the setting is printed (to avoid a PHP error) but doesn't apply.
return -1;
} elseif ( 'WIDESCREEN' === $placeholder_data[1] ) {
$value = $breakpoints['widescreen']->get_value();
} else {
$breakpoint_index = array_search( strtolower( $placeholder_data[1] ), $breakpoints_keys, true );
$is_max_point = 'MAX' === $placeholder_data[2];
// If the placeholder capture is `MOBILE_NEXT` or `TABLET_NEXT`, the original breakpoint value is used.
if ( ! $is_max_point && 'NEXT' !== $placeholder_data[2] ) {
$breakpoint_index--;
}
$value = $breakpoints[ $breakpoints_keys[ $breakpoint_index ] ]->get_value();
if ( ! $is_max_point ) {
$value++;
}
}
return $value . 'px';
}, $file_content );
return $file_content;
}
/**
* Load meta.
*
* Retrieve the file meta data.
*
* @since 2.1.0
* @access protected
*/
protected function load_meta() {
$option = $this->load_meta_option();
$file_meta_key = $this->get_file_meta_key();
if ( empty( $option[ $file_meta_key ] ) ) {
return [];
}
return $option[ $file_meta_key ];
}
/**
* Update meta.
*
* Update the file meta data.
*
* @since 2.1.0
* @access protected
*
* @param array $meta New meta data.
*/
protected function update_meta( $meta ) {
$option = $this->load_meta_option();
$option[ $this->get_file_meta_key() ] = $meta;
update_option( static::META_KEY, $option );
}
/**
* Delete meta.
*
* Delete the file meta data.
*
* @since 2.1.0
* @access protected
*/
protected function delete_meta() {
$option = $this->load_meta_option();
$file_meta_key = $this->get_file_meta_key();
if ( isset( $option[ $file_meta_key ] ) ) {
unset( $option[ $file_meta_key ] );
}
if ( $option ) {
update_option( static::META_KEY, $option );
} else {
delete_option( static::META_KEY );
}
}
/**
* @since 2.1.0
* @access private
*/
private function get_file_meta_key() {
return pathinfo( $this->get_file_name(), PATHINFO_FILENAME );
}
/**
* @since 2.1.0
* @access private
*/
private function load_meta_option() {
$option = get_option( static::META_KEY );
if ( ! $option ) {
$option = [];
}
return $option;
}
/**
* Maybe Convert Placeholder Data
*
* Converts responsive placeholders in Elementor CSS template files from the legacy format into the new format.
* Used for backwards compatibility for old Pro versions that were built with an Elementor Core version <3.2.0.
*
* @since 3.2.3
*
* @param $placeholder_data
* @return mixed
*/
private function maybe_convert_placeholder_data( $placeholder_data ) {
switch ( $placeholder_data[1] ) {
case 'SM':
$placeholder_data[1] = 'MOBILE';
break;
case 'MD':
$placeholder_data[1] = 'TABLET';
break;
case 'LG':
$placeholder_data[1] = 'DESKTOP';
}
return $placeholder_data;
}
}
editor/editor.php 0000644 00000037267 14720700033 0010044 0 ustar 00 set_post_id( absint( $_REQUEST['post'] ) );
if ( ! $this->is_edit_mode( $this->post_id ) ) {
return;
}
// BC: From 2.9.0, the editor shouldn't handle the global post / current document.
// Use requested id and not the global in order to avoid conflicts with plugins that changes the global post.
query_posts( [
'p' => $this->post_id,
'post_type' => get_post_type( $this->post_id ),
] );
Plugin::$instance->db->switch_to_post( $this->post_id );
$document = Plugin::$instance->documents->get( $this->post_id );
Plugin::$instance->documents->switch_to_document( $document );
// Change mode to Builder
$document->set_is_built_with_elementor( true );
// End BC.
Loading_Inspection_Manager::instance()->register_inspections();
// Send MIME Type header like WP admin-header.
@header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
add_filter( 'show_admin_bar', '__return_false' );
// Remove all WordPress actions
remove_all_actions( 'wp_head' );
remove_all_actions( 'wp_print_styles' );
remove_all_actions( 'wp_print_head_scripts' );
remove_all_actions( 'wp_footer' );
// Handle `wp_head`
add_action( 'wp_head', 'wp_enqueue_scripts', 1 );
add_action( 'wp_head', 'wp_print_styles', 8 );
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
add_action( 'wp_head', 'wp_site_icon' );
add_action( 'wp_head', [ $this, 'editor_head_trigger' ], 30 );
// Handle `wp_footer`
add_action( 'wp_footer', 'wp_print_footer_scripts', 20 );
add_action( 'wp_footer', 'wp_auth_check_html', 30 );
add_action( 'wp_footer', [ $this, 'wp_footer' ] );
// Handle `wp_enqueue_scripts`
remove_all_actions( 'wp_enqueue_scripts' );
// Also remove all scripts hooked into after_wp_tiny_mce.
remove_all_actions( 'after_wp_tiny_mce' );
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 999999 );
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ], 999999 );
// Setup default heartbeat options
add_filter( 'heartbeat_settings', function( $settings ) {
$settings['interval'] = 15;
return $settings;
} );
// Tell to WP Cache plugins do not cache this request.
Utils::do_not_cache();
do_action( 'elementor/editor/init' );
$this->get_loader()->print_root_template();
// From the action it's an empty string, from tests its `false`
if ( false !== $die ) {
die;
}
}
/**
* Retrieve post ID.
*
* Get the ID of the current post.
*
* @since 1.8.0
* @access public
*
* @return int Post ID.
*/
public function get_post_id() {
return $this->post_id;
}
/**
* Redirect to new URL.
*
* Used as a fallback function for the old URL structure of Elementor page
* edit URL.
*
* Fired by `template_redirect` action.
*
* @since 1.6.0
* @access public
*/
public function redirect_to_new_url() {
if ( ! isset( $_GET['elementor'] ) ) {
return;
}
$document = Plugin::$instance->documents->get( get_the_ID() );
if ( ! $document ) {
wp_die( esc_html__( 'Document not found.', 'elementor' ) );
}
if ( ! $document->is_editable_by_current_user() || ! $document->is_built_with_elementor() ) {
return;
}
wp_safe_redirect( $document->get_edit_url() );
die;
}
/**
* Whether the edit mode is active.
*
* Used to determine whether we are in the edit mode.
*
* @since 1.0.0
* @access public
*
* @param int $post_id Optional. Post ID. Default is `null`, the current
* post ID.
*
* @return bool Whether the edit mode is active.
*/
public function is_edit_mode( $post_id = null ) {
if ( null !== $this->is_edit_mode ) {
return $this->is_edit_mode;
}
if ( empty( $post_id ) ) {
$post_id = $this->post_id;
}
$document = Plugin::$instance->documents->get( $post_id );
if ( ! $document || ! $document->is_editable_by_current_user() ) {
return false;
}
/** @var Module ajax */
$ajax_data = Plugin::$instance->common->get_component( 'ajax' )->get_current_action_data();
if ( ! empty( $ajax_data ) && 'get_document_config' === $ajax_data['action'] ) {
return true;
}
// Ajax request as Editor mode
$actions = [
'elementor',
// Templates
'elementor_get_templates',
'elementor_save_template',
'elementor_get_template',
'elementor_delete_template',
'elementor_import_template',
'elementor_library_direct_actions',
];
if ( isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], $actions ) ) {
return true;
}
return false;
}
/**
* Lock post.
*
* Mark the post as currently being edited by the current user.
*
* @since 1.0.0
* @access public
*
* @param int $post_id The ID of the post being edited.
*/
public function lock_post( $post_id ) {
if ( ! function_exists( 'wp_set_post_lock' ) ) {
require_once ABSPATH . 'wp-admin/includes/post.php';
}
wp_set_post_lock( $post_id );
}
/**
* Get locked user.
*
* Check what user is currently editing the post.
*
* @since 1.0.0
* @access public
*
* @param int $post_id The ID of the post being edited.
*
* @return \WP_User|false User information or false if the post is not locked.
*/
public function get_locked_user( $post_id ) {
if ( ! function_exists( 'wp_check_post_lock' ) ) {
require_once ABSPATH . 'wp-admin/includes/post.php';
}
$locked_user = wp_check_post_lock( $post_id );
if ( ! $locked_user ) {
return false;
}
return get_user_by( 'id', $locked_user );
}
/**
* NOTICE: This method not in use, it's here for backward compatibility.
*
* Print Editor Template.
*
* Include the wrapper template of the editor.
*
* @since 2.2.0
* @access public
*/
public function print_editor_template() {
include ELEMENTOR_PATH . 'includes/editor-templates/editor-wrapper.php';
}
/**
* Enqueue scripts.
*
* Registers all the editor scripts and enqueues them.
*
* @since 1.0.0
* @access public
*/
public function enqueue_scripts() {
remove_action( 'wp_enqueue_scripts', [ $this, __FUNCTION__ ], 999999 );
global $wp_styles, $wp_scripts;
// Reset global variable
$wp_styles = new \WP_Styles(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$wp_scripts = new \WP_Scripts(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$this->get_loader()->register_scripts();
/**
* Before editor enqueue scripts.
*
* Fires before Elementor editor scripts are enqueued.
*
* @since 1.0.0
*/
do_action( 'elementor/editor/before_enqueue_scripts' );
// Tweak for WP Admin menu icons
wp_print_styles( 'editor-buttons' );
$this->get_loader()->enqueue_scripts();
Plugin::$instance->controls_manager->enqueue_control_scripts();
/**
* After editor enqueue scripts.
*
* Fires after Elementor editor scripts are enqueued.
*
* @since 1.0.0
*/
do_action( 'elementor/editor/after_enqueue_scripts' );
}
/**
* Enqueue styles.
*
* Registers all the editor styles and enqueues them.
*
* @since 1.0.0
* @access public
*/
public function enqueue_styles() {
/**
* Before editor enqueue styles.
*
* Fires before Elementor editor styles are enqueued.
*
* @since 1.0.0
*/
do_action( 'elementor/editor/before_enqueue_styles' );
$this->get_loader()->register_styles();
$this->get_loader()->enqueue_styles();
$this->enqueue_theme_ui_styles();
$breakpoints = Plugin::$instance->breakpoints->get_breakpoints();
// The two breakpoints under 'tablet' need to be checked for values.
if ( $breakpoints[ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE ]->is_custom() || $breakpoints[ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA ]->is_enabled() ) {
wp_add_inline_style(
'elementor-editor',
'.elementor-device-tablet #elementor-preview-responsive-wrapper { width: ' . Plugin::$instance->breakpoints->get_device_min_breakpoint( Breakpoints_Manager::BREAKPOINT_KEY_TABLET ) . 'px; }'
);
}
/**
* After editor enqueue styles.
*
* Fires after Elementor editor styles are enqueued.
*
* @since 1.0.0
*/
do_action( 'elementor/editor/after_enqueue_styles' );
}
private function enqueue_theme_ui_styles() {
$ui_theme_selected = SettingsManager::get_settings_managers( 'editorPreferences' )->get_model()->get_settings( 'ui_theme' );
$ui_themes = [
'light',
'dark',
];
if ( 'auto' === $ui_theme_selected || ! in_array( $ui_theme_selected, $ui_themes, true ) ) {
$ui_light_theme_media_queries = '(prefers-color-scheme: light)';
$ui_dark_theme_media_queries = '(prefers-color-scheme: dark)';
} else {
$ui_light_theme_media_queries = 'none';
$ui_dark_theme_media_queries = 'none';
if ( 'light' === $ui_theme_selected ) {
$ui_light_theme_media_queries = 'all';
} elseif ( 'dark' === $ui_theme_selected ) {
$ui_dark_theme_media_queries = 'all';
}
}
$this->enqueue_theme_ui( 'light', $ui_light_theme_media_queries );
$this->enqueue_theme_ui( 'dark', $ui_dark_theme_media_queries );
}
private function enqueue_theme_ui( $ui_theme, $ui_theme_media_queries = 'all' ) {
$suffix = Utils::is_script_debug() ? '' : '.min';
wp_enqueue_style(
'e-theme-ui-' . $ui_theme,
ELEMENTOR_ASSETS_URL . 'css/theme-' . $ui_theme . $suffix . '.css',
[],
ELEMENTOR_VERSION,
$ui_theme_media_queries
);
}
/**
* Editor head trigger.
*
* Fires the 'elementor/editor/wp_head' action in the head tag in Elementor
* editor.
*
* @since 1.0.0
* @access public
*/
public function editor_head_trigger() {
/**
* Elementor editor head.
*
* Fires on Elementor editor head tag.
*
* Used to prints scripts or any other data in the head tag.
*
* @since 1.0.0
*/
do_action( 'elementor/editor/wp_head' );
}
/**
* WP footer.
*
* Prints Elementor editor with all the editor templates, and render controls,
* widgets and content elements.
*
* Fired by `wp_footer` action.
*
* @since 1.0.0
* @access public
*/
public function wp_footer() {
$plugin = Plugin::$instance;
$plugin->controls_manager->render_controls();
$plugin->widgets_manager->render_widgets_content();
$plugin->elements_manager->render_elements_content();
$plugin->dynamic_tags->print_templates();
$this->get_loader()->register_additional_templates();
/**
* Elementor editor footer.
*
* Fires on Elementor editor before closing the body tag.
*
* Used to prints scripts or any other HTML before closing the body tag.
*
* @since 1.0.0
*/
do_action( 'elementor/editor/footer' );
}
/**
* Set edit mode.
*
* Used to update the edit mode.
*
* @since 1.0.0
* @access public
*
* @param bool $edit_mode Whether the edit mode is active.
*/
public function set_edit_mode( $edit_mode ) {
$this->is_edit_mode = $edit_mode;
}
/**
* Editor constructor.
*
* Initializing Elementor editor and redirect from old URL structure of
* Elementor editor.
*
* @since 1.0.0
* @access public
*/
public function __construct() {
Plugin::$instance->data_manager_v2->register_controller( new Data\Globals\Controller() );
$this->notice_bar = new Notice_Bar();
$this->promotion = new Promotion();
add_action( 'admin_action_elementor', [ $this, 'init' ] );
add_action( 'template_redirect', [ $this, 'redirect_to_new_url' ] );
// Handle autocomplete feature for URL control.
add_filter( 'wp_link_query_args', [ $this, 'filter_wp_link_query_args' ] );
add_filter( 'wp_link_query', [ $this, 'filter_wp_link_query' ] );
}
/**
* @since 2.2.0
* @access public
*/
public function filter_wp_link_query_args( $query ) {
$library_cpt_key = array_search( Source_Local::CPT, $query['post_type'], true );
if ( false !== $library_cpt_key ) {
unset( $query['post_type'][ $library_cpt_key ] );
}
return $query;
}
/**
* @since 2.2.0
* @access public
*/
public function filter_wp_link_query( $results ) {
// PHPCS - The user data is not used.
if ( isset( $_POST['editor'] ) && 'elementor' === $_POST['editor'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
$post_type_object = get_post_type_object( 'post' );
$post_label = $post_type_object->labels->singular_name;
foreach ( $results as & $result ) {
if ( 'post' === get_post_type( $result['ID'] ) ) {
$result['info'] = $post_label;
}
}
}
return $results;
}
public function set_post_id( $post_id ) {
$this->post_id = $post_id;
}
/**
* Get loader.
*
* @return Editor_Loader_Interface
*/
private function get_loader() {
if ( ! $this->loader ) {
$this->loader = Editor_Loader_Factory::create();
$this->loader->init();
}
return $this->loader;
}
/**
* Get elements presets.
*
* @return array
*/
public function get_elements_presets() {
$element_types = Plugin::$instance->elements_manager->get_element_types();
$presets = [];
foreach ( $element_types as $el_type => $element ) {
$this->check_element_for_presets( $element, $el_type, $presets );
}
return $presets;
}
/**
* @return void
*/
private function check_element_for_presets( $element, $el_type, &$presets ) {
$element_presets = $element->get_panel_presets();
if ( empty( $element_presets ) ) {
return;
}
foreach ( $element_presets as $key => $preset ) {
$this->maybe_add_preset( $el_type, $preset, $key, $presets );
}
}
/**
* @return void
*/
private function maybe_add_preset( $el_type, $preset, $key, &$presets ) {
if ( $this->is_valid_preset( $el_type, $preset ) ) {
$presets[ $key ] = $preset;
}
}
/**
* @return boolean
*/
private function is_valid_preset( $el_type, $preset ) {
return isset( $preset['replacements']['custom']['originalWidget'] )
&& $el_type === $preset['replacements']['custom']['originalWidget'];
}
}
editor/data/globals/endpoints/base.php 0000644 00000003200 14720700033 0014003 0 ustar 00 '[\w]+',
];
$this->register_item_route( \WP_REST_Server::READABLE, $args );
$this->register_item_route( \WP_REST_Server::CREATABLE, $args );
$this->register_item_route( \WP_REST_Server::DELETABLE, $args );
}
public function get_items( $request ) {
return $this->get_kit_items();
}
/**
* @inheritDoc
* @throws \Elementor\Data\V2\Base\Exceptions\Error_404
*/
public function get_item( $id, $request ) {
$items = $this->get_kit_items();
if ( ! isset( $items[ $id ] ) ) {
throw new Error_404( esc_html__( 'The Global value you are trying to use is not available.', 'elementor' ),
'global_not_found'
);
}
return $items[ $id ];
}
public function create_item( $id, $request ) {
$item = $request->get_body_params();
if ( ! isset( $item['title'] ) ) {
return new Data_Exception( esc_html__( 'Invalid title', 'elementor' ), 'invalid_title' );
}
$kit = Plugin::$instance->kits_manager->get_active_kit();
$item['id'] = $id;
$db_item = $this->convert_db_format( $item );
$kit->add_repeater_row( 'custom_' . $this->get_name(), $db_item );
return $item;
}
abstract protected function get_kit_items();
/**
* @param array $item frontend format.
* @return array backend format.
*/
abstract protected function convert_db_format( $item );
}
editor/data/globals/endpoints/typography.php 0000644 00000003123 14720700033 0015303 0 ustar 00 kits_manager->get_active_kit_for_frontend();
// Use raw settings that doesn't have default values.
$kit_raw_settings = $kit->get_data( 'settings' );
if ( isset( $kit_raw_settings['system_typography'] ) ) {
$system_items = $kit_raw_settings['system_typography'];
} else {
// Get default items, but without empty defaults.
$control = $kit->get_controls( 'system_typography' );
$system_items = $control['default'];
}
$custom_items = $kit->get_settings( 'custom_typography' );
if ( ! $custom_items ) {
$custom_items = [];
}
$items = array_merge( $system_items, $custom_items );
foreach ( $items as $index => &$item ) {
foreach ( $item as $setting => $value ) {
$new_setting = str_replace( 'styles_', '', $setting, $count );
if ( $count ) {
$item[ $new_setting ] = $value;
unset( $item[ $setting ] );
}
}
$id = $item['_id'];
$result[ $id ] = [
'title' => $item['title'],
'id' => $id,
];
unset( $item['_id'], $item['title'] );
$result[ $id ]['value'] = $item;
}
return $result;
}
protected function convert_db_format( $item ) {
$db_format = [
'_id' => $item['id'],
'title' => $item['title'],
];
$db_format = array_merge( $item['value'], $db_format );
return $db_format;
}
}
editor/data/globals/endpoints/colors.php 0000644 00000001777 14720700033 0014413 0 ustar 00 kits_manager->get_active_kit_for_frontend();
$system_items = $kit->get_settings_for_display( 'system_colors' );
$custom_items = $kit->get_settings_for_display( 'custom_colors' );
if ( ! $system_items ) {
$system_items = [];
}
if ( ! $custom_items ) {
$custom_items = [];
}
$items = array_merge( $system_items, $custom_items );
foreach ( $items as $index => $item ) {
$id = $item['_id'];
$result[ $id ] = [
'id' => $id,
'title' => $item['title'],
'value' => $item['color'],
];
}
return $result;
}
protected function convert_db_format( $item ) {
return [
'_id' => $item['id'],
'title' => $item['title'],
'color' => $item['value'],
];
}
}
editor/data/globals/controller.php 0000644 00000002023 14720700033 0013253 0 ustar 00 register_endpoint( new Endpoints\Colors( $this ) );
$this->register_endpoint( new Endpoints\Typography( $this ) );
}
public function get_collection_params() {
// Does not have 'get_items' args (OPTIONS).
// Maybe TODO: try `$this->get_index_endpoint()->get_collection_params()`.
return [];
}
public function get_permission_callback( $request ) {
// Allow internal get global values. (e.g render global.css for a visitor)
if ( 'GET' === $request->get_method() && Plugin::$instance->data_manager_v2->is_internal() ) {
return true;
}
return current_user_can( 'edit_posts' );
}
protected function register_index_endpoint() {
$this->register_endpoint( new Endpoint\Index\AllChildren( $this ) );
}
}
editor/promotion.php 0000644 00000002711 14720700033 0010566 0 ustar 00 get_promotion_data(),
'elementor/editor/promotion/get_elements_promotion',
'action_button',
'url'
);
}
/**
* @return array
*/
private function get_action_button_content(): array {
$has_pro = Utils::has_pro();
return $has_pro ? [
'text' => __( 'Connect & Activate', 'elementor' ),
'url' => admin_url( 'admin.php?page=elementor-license' ),
] : [
'text' => __( 'Upgrade Now', 'elementor' ),
'url' => 'https://go.elementor.com/go-pro-%s',
];
}
/**
* @return string
*/
private function get_promotion_url(): string {
return Utils::has_pro()
? admin_url( 'admin.php?page=elementor-license' )
: 'https://go.elementor.com/go-pro-%s';
}
/**
* @return array
*/
private function get_promotion_data(): array {
return [
/* translators: %s: Widget title. */
'title' => __( '%s Widget', 'elementor' ),
/* translators: %s: Widget title. */
'content' => __(
'Use %s widget and dozens more pro features to extend your toolbox and build sites faster and better.',
'elementor'
),
'action_button' => $this->get_action_button_content(),
];
}
}
editor/loader/editor-base-loader.php 0000644 00000013200 14720700033 0013443 0 ustar 00 config = $config;
$this->assets_config_provider = $assets_config_provider;
}
/**
* @return void
*/
public function register_scripts() {
$assets_url = $this->config->get( 'assets_url' );
$min_suffix = $this->config->get( 'min_suffix' );
wp_register_script(
'elementor-editor-modules',
"{$assets_url}js/editor-modules{$min_suffix}.js",
[ 'elementor-common-modules' ],
ELEMENTOR_VERSION,
true
);
wp_register_script(
'elementor-editor-document',
"{$assets_url}js/editor-document{$min_suffix}.js",
[ 'elementor-common-modules' ],
ELEMENTOR_VERSION,
true
);
wp_register_script(
'perfect-scrollbar',
"{$assets_url}lib/perfect-scrollbar/js/perfect-scrollbar{$min_suffix}.js",
[],
'1.4.0',
true
);
wp_register_script(
'jquery-easing',
"{$assets_url}lib/jquery-easing/jquery-easing{$min_suffix}.js",
[ 'jquery' ],
'1.3.2',
true
);
wp_register_script(
'nprogress',
"{$assets_url}lib/nprogress/nprogress{$min_suffix}.js",
[],
'0.2.0',
true
);
wp_register_script(
'tipsy',
"{$assets_url}lib/tipsy/tipsy{$min_suffix}.js",
[ 'jquery' ],
'1.0.0',
true
);
wp_register_script(
'jquery-elementor-select2',
"{$assets_url}lib/e-select2/js/e-select2.full{$min_suffix}.js",
[ 'jquery' ],
'4.0.6-rc.1',
true
);
wp_register_script(
'flatpickr',
"{$assets_url}lib/flatpickr/flatpickr{$min_suffix}.js",
[ 'jquery' ],
'4.6.13',
true
);
wp_register_script(
'ace',
'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ace.js',
[],
'1.2.5',
true
);
wp_register_script(
'ace-language-tools',
'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ext-language_tools.js',
[ 'ace' ],
'1.2.5',
true
);
wp_register_script(
'jquery-hover-intent',
"{$assets_url}lib/jquery-hover-intent/jquery-hover-intent{$min_suffix}.js",
[],
'1.0.0',
true
);
wp_register_script(
'nouislider',
"{$assets_url}lib/nouislider/nouislider{$min_suffix}.js",
[],
'13.0.0',
true
);
wp_register_script(
'pickr',
"{$assets_url}lib/pickr/pickr.min.js",
[],
'1.8.2',
true
);
wp_register_script(
'elementor-editor',
"{$assets_url}js/editor{$min_suffix}.js",
[
'elementor-common',
'elementor-editor-modules',
'elementor-editor-document',
'wp-auth-check',
'jquery-ui-sortable',
'jquery-ui-resizable',
'perfect-scrollbar',
'nprogress',
'tipsy',
'imagesloaded',
'heartbeat',
'jquery-elementor-select2',
'flatpickr',
'ace',
'ace-language-tools',
'jquery-hover-intent',
'nouislider',
'pickr',
'react',
'react-dom',
],
ELEMENTOR_VERSION,
true
);
wp_set_script_translations( 'elementor-editor', 'elementor' );
wp_register_script(
'elementor-responsive-bar',
"{$assets_url}js/responsive-bar{$min_suffix}.js",
[ 'elementor-editor' ],
ELEMENTOR_VERSION,
true
);
wp_set_script_translations( 'elementor-responsive-bar', 'elementor' );
}
/**
* @return void
*/
public function enqueue_scripts() {
wp_enqueue_script( 'elementor-responsive-bar' );
}
/**
* @return void
*/
public function register_styles() {
$assets_url = $this->config->get( 'assets_url' );
$min_suffix = $this->config->get( 'min_suffix' );
$direction_suffix = $this->config->get( 'direction_suffix' );
wp_register_style(
'font-awesome',
"{$assets_url}lib/font-awesome/css/font-awesome{$min_suffix}.css",
[],
'4.7.0'
);
wp_register_style(
'elementor-select2',
"{$assets_url}lib/e-select2/css/e-select2{$min_suffix}.css",
[],
'4.0.6-rc.1'
);
wp_register_style(
'google-font-roboto',
'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700',
[],
ELEMENTOR_VERSION
);
wp_register_style(
'flatpickr',
"{$assets_url}lib/flatpickr/flatpickr{$min_suffix}.css",
[],
'4.6.13'
);
wp_register_style(
'pickr',
"{$assets_url}lib/pickr/themes/monolith.min.css",
[],
'1.8.2'
);
wp_register_style(
'elementor-editor',
"{$assets_url}css/editor{$direction_suffix}{$min_suffix}.css",
[
'elementor-common',
'elementor-select2',
'elementor-icons',
'wp-auth-check',
'google-font-roboto',
'flatpickr',
'pickr',
],
ELEMENTOR_VERSION
);
wp_register_style(
'elementor-responsive-bar',
"{$assets_url}css/responsive-bar{$min_suffix}.css",
[],
ELEMENTOR_VERSION
);
}
/**
* @return void
*/
public function enqueue_styles() {
wp_enqueue_style( 'elementor-editor' );
wp_enqueue_style( 'elementor-responsive-bar' );
}
/**
* @return void
*/
public function register_additional_templates() {
$templates = [
'global',
'panel',
'panel-elements',
'repeater',
'templates',
'navigator',
'hotkeys',
'responsive-bar',
];
$templates = apply_filters( 'elementor/editor/templates', $templates );
foreach ( $templates as $template ) {
Plugin::$instance->common->add_template( ELEMENTOR_PATH . "includes/editor-templates/{$template}.php" );
}
}
}
editor/loader/common/editor-common-scripts-settings.php 0000644 00000016552 14720700033 0017405 0 ustar 00 documents->get_doc_or_auto_save( Plugin::$instance->editor->get_post_id() );
$kits_manager = Plugin::$instance->kits_manager;
$page_title_selector = $kits_manager->get_current_settings( 'page_title_selector' );
$page_title_selector .= ', .elementor-page-title .elementor-heading-title';
$client_env = [
'initial_document' => $document->get_config(),
'version' => ELEMENTOR_VERSION,
'home_url' => home_url(),
'admin_settings_url' => admin_url( 'admin.php?page=' . Home_Module::get_elementor_settings_page_id() ),
'admin_tools_url' => admin_url( 'admin.php?page=' . Tools::PAGE_ID ),
'admin_apps_url' => admin_url( 'admin.php?page=' . AppsModule::PAGE_ID ),
'autosave_interval' => AUTOSAVE_INTERVAL,
'tabs' => Plugin::$instance->controls_manager->get_tabs(),
'controls' => Plugin::$instance->controls_manager->get_controls_data(),
'elements' => Plugin::$instance->elements_manager->get_element_types_config(),
'globals' => [
'defaults_enabled' => [
'colors' => $kits_manager->is_custom_colors_enabled(),
'typography' => $kits_manager->is_custom_typography_enabled(),
],
],
'icons' => [
'libraries' => Icons_Manager::get_icon_manager_tabs_config(),
'goProURL' => 'https://go.elementor.com/go-pro-icon-library/',
],
'fa4_to_fa5_mapping_url' => ELEMENTOR_ASSETS_URL . 'lib/font-awesome/migration/mapping.js',
'settings' => $settings,
'wp_editor' => static::get_wp_editor_config(),
'settings_page_link' => Settings::get_url(),
'tools_page_link' => Tools::get_url(),
'tools_page_nonce' => wp_create_nonce( 'tools-page-from-editor' ),
'elementor_site' => 'https://go.elementor.com/about-elementor/',
'docs_elementor_site' => 'https://go.elementor.com/docs/',
'help_the_content_url' => 'https://go.elementor.com/the-content-missing/',
'help_flexbox_bc_url' => 'https://go.elementor.com/flexbox-layout-bc/',
'elementPromotionURL' => 'https://go.elementor.com/go-pro-%s',
'dynamicPromotionURL' => 'https://go.elementor.com/go-pro-dynamic-tag',
'additional_shapes' => Shapes::get_additional_shapes_for_config(),
'user' => [
'restrictions' => Plugin::$instance->role_manager->get_user_restrictions_array(),
'is_administrator' => current_user_can( 'manage_options' ),
'introduction' => User::get_introduction_meta(),
'dismissed_editor_notices' => User::get_dismissed_editor_notices(),
'locale' => get_user_locale(),
],
'preview' => [
'help_preview_error_url' => 'https://go.elementor.com/preview-not-loaded/',
'help_preview_http_error_url' => 'https://go.elementor.com/preview-not-loaded/#permissions',
'help_preview_http_error_500_url' => 'https://go.elementor.com/500-error/',
'debug_data' => Loading_Inspection_Manager::instance()->run_inspections(),
],
'locale' => get_locale(),
'rich_editing_enabled' => filter_var( get_user_meta( get_current_user_id(), 'rich_editing', true ), FILTER_VALIDATE_BOOLEAN ),
'page_title_selector' => $page_title_selector,
'tinymceHasCustomConfig' => class_exists( 'Tinymce_Advanced' ) || class_exists( 'Advanced_Editor_Tools' ),
'inlineEditing' => Plugin::$instance->widgets_manager->get_inline_editing_config(),
'dynamicTags' => Plugin::$instance->dynamic_tags->get_config(),
'ui' => [
'defaultGenericFonts' => $kits_manager->get_current_settings( 'default_generic_fonts' ),
],
// Empty array for BC to avoid errors.
'i18n' => [],
// 'responsive' contains the custom breakpoints config introduced in Elementor v3.2.0
'responsive' => [
'breakpoints' => Plugin::$instance->breakpoints->get_breakpoints_config(),
'icons_map' => Plugin::$instance->breakpoints->get_responsive_icons_classes_map(),
],
'promotion' => [
'elements' => Plugin::$instance->editor->promotion->get_elements_promotion(),
],
'editor_events' => EditorEventsModule::get_editor_events_config(),
'promotions' => [
'notes' => Filtered_Promotions_Manager::get_filtered_promotion_data(
[ 'upgrade_url' => 'https://go.elementor.com/go-pro-notes/' ],
'elementor/panel/notes/custom_promotion',
'upgrade_url'
),
],
'fontVariableRanges' => Group_Control_Typography::get_font_variable_ranges(),
];
if ( ! Utils::has_pro() && current_user_can( 'manage_options' ) ) {
$client_env['promotionWidgets'] = Api::get_promotion_widgets();
}
if ( Plugin::$instance->experiments->is_feature_active( 'container_grid' ) ) {
$client_env['elementsPresets'] = Plugin::$instance->editor->get_elements_presets();
}
static::bc_move_document_filters();
/**
* Localize editor settings.
*
* Filters the editor localized settings.
*
* @since 1.0.0
*
* @param array $client_env Editor configuration.
* @param int $post_id The ID of the current post being edited.
*/
return apply_filters( 'elementor/editor/localize_settings', $client_env );
}
private static function bc_move_document_filters() {
global $wp_filter;
$old_tag = 'elementor/editor/localize_settings';
$new_tag = 'elementor/document/config';
if ( ! has_filter( $old_tag ) ) {
return;
}
foreach ( $wp_filter[ $old_tag ] as $priority => $filters ) {
foreach ( $filters as $filter_id => $filter_args ) {
if ( 2 === $filter_args['accepted_args'] ) {
remove_filter( $old_tag, $filter_id, $priority );
add_filter( $new_tag, $filter_args['function'], $priority, 2 );
}
}
}
}
/**
* Get WordPress editor config.
*
* Config the default WordPress editor with custom settings for Elementor use.
*
* @since 1.9.0
* @access private
*/
private static function get_wp_editor_config() {
// Remove all TinyMCE plugins.
remove_all_filters( 'mce_buttons', 10 );
remove_all_filters( 'mce_external_plugins', 10 );
if ( ! class_exists( '\_WP_Editors', false ) ) {
require ABSPATH . WPINC . '/class-wp-editor.php';
}
// WordPress 4.8 and higher
if ( method_exists( '\_WP_Editors', 'print_tinymce_scripts' ) ) {
\_WP_Editors::print_default_editor_scripts();
\_WP_Editors::print_tinymce_scripts();
}
ob_start();
wp_editor(
'%%EDITORCONTENT%%',
'elementorwpeditor',
[
'editor_class' => 'elementor-wp-editor',
'editor_height' => 250,
'drag_drop_upload' => true,
]
);
$config = ob_get_clean();
// Don't call \_WP_Editors methods again
remove_action( 'admin_print_footer_scripts', [ '_WP_Editors', 'editor_js' ], 50 );
remove_action( 'admin_print_footer_scripts', [ '_WP_Editors', 'print_default_editor_scripts' ], 45 );
\_WP_Editors::editor_js();
return $config;
}
}
editor/loader/v1/templates/editor-body-v1.view.php 0000644 00000003102 14720700033 0016043 0 ustar 00 editor->notice_bar->get_notice();
?>
render();
} // IFrame will be created here by the Javascript later. ?>
editor/loader/v1/editor-v1-loader.php 0000644 00000004073 14720700033 0013415 0 ustar 00 assets_config_provider->load( $package );
}
}
/**
* @return void
*/
public function register_scripts() {
parent::register_scripts();
$assets_url = $this->config->get( 'assets_url' );
$min_suffix = $this->config->get( 'min_suffix' );
foreach ( $this->assets_config_provider->all() as $package => $config ) {
wp_register_script(
$config['handle'],
"{$assets_url}js/packages/{$package}/{$package}{$min_suffix}.js",
$config['deps'],
ELEMENTOR_VERSION,
true
);
}
wp_register_script(
'elementor-editor-loader-v1',
"{$assets_url}js/editor-loader-v1{$min_suffix}.js",
[ 'elementor-editor' ],
ELEMENTOR_VERSION,
true
);
}
/**
* @return void
*/
public function enqueue_scripts() {
parent::enqueue_scripts();
// Must be last.
wp_enqueue_script( 'elementor-editor-loader-v1' );
Utils::print_js_config(
'elementor-editor',
'ElementorConfig',
Editor_Common_Scripts_Settings::get()
);
}
/**
* @return void
*/
public function print_root_template() {
// Exposing the path for the view part to render the body of the editor template.
$body_file_path = __DIR__ . '/templates/editor-body-v1.view.php';
include ELEMENTOR_PATH . 'includes/editor-templates/editor-wrapper.php';
}
/**
* @return void
*/
public function register_additional_templates() {
parent::register_additional_templates();
Plugin::$instance->common->add_template( ELEMENTOR_PATH . 'includes/editor-templates/responsive-bar.php' );
}
}
editor/loader/v1/js/editor-loader-v1.js 0000644 00000000032 14720700033 0013645 0 ustar 00 window.elementor.start();
editor/loader/editor-loader-factory.php 0000644 00000002450 14720700033 0014205 0 ustar 00 ELEMENTOR_ASSETS_URL,
'min_suffix' => ( Utils::is_script_debug() || Utils::is_elementor_tests() ) ? '' : '.min',
'direction_suffix' => is_rtl() ? '-rtl' : '',
] );
$assets_config_provider = ( new Assets_Config_Provider() )
->set_path_resolver( function ( $name ) {
return ELEMENTOR_ASSETS_PATH . "js/packages/{$name}/{$name}.asset.php";
} );
if ( static::should_use_v2_loader() ) {
return new Editor_V2_Loader( $config, $assets_config_provider );
}
return new Editor_V1_Loader( $config, $assets_config_provider );
}
/**
* If there are v2 packages enqueued, we should use the V2 loader.
*
* @return bool
*/
private static function should_use_v2_loader() {
return ! empty( Editor_V2_Loader::get_packages_to_enqueue() );
}
}
editor/loader/v2/scss/editor-v2-app-bar-overrides.scss 0000644 00000002142 14720700033 0016625 0 ustar 00 /**
* Here should be only styles that related to the Editor v1, and should be overridden when using the Editor v2.
*/
body {
--editor-v2-top-bar-height: 48px;
}
#elementor-editor-wrapper {
height: calc(100vh - var(--editor-v2-top-bar-height));
}
body.elementor-navigator-docked #elementor-navigator {
height: calc(100% - var(--editor-v2-top-bar-height));
top: var(--editor-v2-top-bar-height);
}
.elementor-panel #elementor-panel-header-menu-button,
.elementor-panel #elementor-panel-header-add-button,
.elementor-panel #elementor-panel-footer {
display: none;
}
.elementor-panel #elementor-panel-header {
font-weight: 700;
background-color: var( --e-a-bg-default );
color: var( --e-a-color-txt-accent );
border-block-end: var( --e-a-border );
height: 48px;
}
// Make the MCE full-screen work properly with the top bar.
.elementor-control-type-wysiwyg .mce-fullscreen {
inset: var(--editor-v2-top-bar-height) 0 0 0;
& > .mce-container-body {
display: flex;
flex-direction: column;
height: 100%;
& > .mce-edit-area {
flex-grow: 1;
& > iframe {
height: 100% !important;
}
}
}
}
editor/loader/v2/templates/editor-body-v2.view.php 0000644 00000003160 14720700033 0016051 0 ustar 00 editor->notice_bar->get_notice();
?>
render();
} // IFrame will be created here by the Javascript later. ?>
editor/loader/v2/editor-v2-loader.php 0000644 00000011646 14720700033 0013423 0 ustar 00 [
'elementor-web-cli',
],
'wp-media' => [
'media-models',
],
];
/**
* @return void
*/
public function init() {
$packages = array_merge( $this->get_packages_to_enqueue(), self::LIBS );
$packages_with_app = array_merge( $packages, [ self::APP_PACKAGE ] );
foreach ( $packages_with_app as $package ) {
$this->assets_config_provider->load( $package );
}
do_action( 'elementor/editor/v2/init' );
}
/**
* @return void
*/
public function register_scripts() {
parent::register_scripts();
$assets_url = $this->config->get( 'assets_url' );
$min_suffix = $this->config->get( 'min_suffix' );
foreach ( $this->assets_config_provider->all() as $package => $config ) {
if ( self::ENV_PACKAGE === $package ) {
wp_register_script(
'elementor-editor-environment-v2',
"{$assets_url}js/editor-environment-v2{$min_suffix}.js",
[ $config['handle'] ],
ELEMENTOR_VERSION,
true
);
}
if ( static::APP_PACKAGE === $package ) {
wp_register_script(
'elementor-editor-loader-v2',
"{$assets_url}js/editor-loader-v2{$min_suffix}.js",
[ 'elementor-editor', $config['handle'] ],
ELEMENTOR_VERSION,
true
);
}
$additional_deps = self::ADDITIONAL_DEPS[ $package ] ?? [];
$deps = array_merge( $config['deps'], $additional_deps );
wp_register_script(
$config['handle'],
"{$assets_url}js/packages/{$package}/{$package}{$min_suffix}.js",
$deps,
ELEMENTOR_VERSION,
true
);
}
$packages_handles = $this->assets_config_provider->pluck( 'handle' )->all();
Assets_Translation_Loader::for_handles( $packages_handles, 'elementor' );
do_action( 'elementor/editor/v2/scripts/register' );
}
/**
* @return void
*/
public function enqueue_scripts() {
do_action( 'elementor/editor/v2/scripts/enqueue/before' );
parent::enqueue_scripts();
wp_enqueue_script( 'elementor-editor-environment-v2' );
$env_config = $this->assets_config_provider->get( self::ENV_PACKAGE );
if ( $env_config ) {
$client_env = apply_filters( 'elementor/editor/v2/scripts/env', [] );
Utils::print_js_config(
$env_config['handle'],
'elementorEditorV2Env',
$client_env
);
}
$packages_with_app = array_merge( $this->get_packages_to_enqueue(), [ self::APP_PACKAGE ] );
foreach ( $this->assets_config_provider->only( $packages_with_app ) as $config ) {
wp_enqueue_script( $config['handle'] );
}
do_action( 'elementor/editor/v2/scripts/enqueue' );
Utils::print_js_config(
'elementor-editor',
'ElementorConfig',
Editor_Common_Scripts_Settings::get()
);
// Must be last.
wp_enqueue_script( 'elementor-editor-loader-v2' );
do_action( 'elementor/editor/v2/scripts/enqueue/after' );
}
/**
* @return void
*/
public function register_styles() {
parent::register_styles();
$assets_url = $this->config->get( 'assets_url' );
$min_suffix = $this->config->get( 'min_suffix' );
foreach ( $this->get_styles() as $style ) {
wp_register_style(
"elementor-{$style}",
"{$assets_url}css/{$style}{$min_suffix}.css",
[ 'elementor-editor' ],
ELEMENTOR_VERSION
);
}
do_action( 'elementor/editor/v2/styles/register' );
}
/**
* @return void
*/
public function enqueue_styles() {
parent::enqueue_styles();
foreach ( $this->get_styles() as $style ) {
wp_enqueue_style( "elementor-{$style}" );
}
do_action( 'elementor/editor/v2/styles/enqueue' );
}
/**
* @return void
*/
public function print_root_template() {
// Exposing the path for the view part to render the body of the editor template.
$body_file_path = __DIR__ . '/templates/editor-body-v2.view.php';
include ELEMENTOR_PATH . 'includes/editor-templates/editor-wrapper.php';
}
public static function get_packages_to_enqueue() : array {
return apply_filters( 'elementor/editor/v2/packages', [] );
}
private function get_styles() : array {
$styles = apply_filters( 'elementor/editor/v2/styles', [] );
return Collection::make( $styles )
->unique()
->all();
}
}
editor/loader/v2/js/editor-environment-v2.js 0000644 00000000251 14720700033 0014750 0 ustar 00 if ( ! window.elementorV2?.env ) {
throw new Error( 'The "@elementor/env" package was not loaded.' );
}
window.elementorV2.env.initEnv( window.elementorEditorV2Env );
editor/loader/v2/js/editor-loader-v2.js 0000644 00000000625 14720700033 0013657 0 ustar 00 window.__elementorEditorV1LoadingPromise = new Promise( ( resolve ) => {
window.addEventListener( 'elementor/init', () => {
resolve();
}, { once: true } );
} );
window.elementor.start();
if ( ! window.elementorV2?.editor ) {
throw new Error( 'The "@elementor/editor" package was not loaded.' );
}
window.elementorV2
.editor
.init(
document.getElementById( 'elementor-editor-wrapper-v2' ),
);
editor/loader/editor-loader-interface.php 0000644 00000002162 14720700033 0014476 0 ustar 00 get_install_time() > strtotime( '-1 days' ) ) {
return [];
}
$upgrade_url = 'https://go.elementor.com/go-pro-editor-notice-bar/';
$config = [
'description' => $this->get_description(),
'upgrade_text' => $this->get_upgrade_text(),
'upgrade_url' => $upgrade_url,
];
$config = Filtered_Promotions_Manager::get_filtered_promotion_data( $config, 'elementor/notice-bar/custom_promotion', 'upgrade_url' );
return [
'muted_period' => 14,
'option_key' => '_elementor_editor_upgrade_notice_dismissed',
'message' => $config['description'] ?? $this->get_description(),
'action_title' => $config['upgrade_text'] ?? $this->get_upgrade_text(),
'action_url' => $config['upgrade_url'] ?? $upgrade_url,
];
}
public function get_upgrade_text() {
return esc_html__( 'Upgrade Now', 'elementor' );
}
public function get_description() {
return esc_html__( 'Unleash the full power of Elementor\'s features and web creation tools.', 'elementor' );
}
final public function get_notice() {
if ( ! $this->has_access_to_notice() ) {
return null;
}
$settings = $this->get_settings();
if ( empty( $settings['option_key'] ) ) {
return null;
}
$dismissed_time = get_option( $settings['option_key'] );
if ( $dismissed_time ) {
if ( $dismissed_time > strtotime( '-' . $settings['muted_period'] . ' days' ) ) {
return null;
}
$this->set_notice_dismissed();
}
return $this;
}
protected function render_action( $type ) {
$settings = $this->get_settings();
// TODO: Make the API better. The bad naming is because of BC.
$prefix_map = [
'primary' => '',
'secondary' => 'secondary_',
];
$prefix = $prefix_map[ $type ];
$action_title = "{$prefix}action_title";
$action_url = "{$prefix}action_url";
$action_message = "{$prefix}message";
$action_target = "{$prefix}action_target";
if ( empty( $settings[ $action_title ] ) || empty( $settings[ $action_url ] ) || empty( $settings[ $action_message ] ) ) {
return;
}
?>
">
get_settings();
$icon = empty( $settings['icon'] )
? 'eicon-elementor-square'
: esc_attr( $settings['icon'] );
?>
render_action( 'primary' );
$this->render_action( 'secondary' );
?>
has_access_to_notice() ) {
throw new \Exception( 'Access denied' );
}
update_option( $this->get_settings( 'option_key' ), time() );
}
public function register_ajax_actions( Ajax $ajax ) {
$ajax->register_ajax_action( 'notice_bar_dismiss', [ $this, 'set_notice_dismissed' ] );
}
private function has_access_to_notice() {
return current_user_can( 'manage_options' );
}
}
common/modules/event-tracker/module.php 0000644 00000001642 14720700033 0014253 0 ustar 00 Tracker::is_allow_track(),
];
}
public function __construct() {
// Initialize Events Database Table
$this->add_component( 'events-db', new DB() );
// Handle User Data Deletion/Export requests.
new Personal_Data();
Plugin::$instance->data_manager_v2->register_controller( new Controller() );
}
}
common/modules/event-tracker/data/controller.php 0000644 00000003513 14720700033 0016061 0 ustar 00 index_endpoint->register_items_route( \WP_REST_Server::CREATABLE, [
'event_data' => [
'description' => 'All the recorded event data in JSON format',
'type' => 'object',
'required' => true,
],
] );
}
/**
* Get Permissions Callback
*
* This endpoint should only accept POST requests, and currently we only track site administrator actions.
*
* @since 3.6.0
*
* @param \WP_REST_Request $request
* @return bool
*/
public function get_permission_callback( $request ) {
if ( WP_REST_Server::CREATABLE !== $request->get_method() ) {
return false;
}
return current_user_can( 'manage_options' );
}
/**
* Create Items
*
* Receives a request for adding an event data entry into the database. If the request contains event data, this
* method initiates creation of a database entry with the event data in the Events DB table.
*
* @since 3.6.0
*
* @param \WP_REST_Request $request
* @return bool
*/
public function create_items( $request ) {
$request_body = $request->get_json_params();
if ( empty( $request_body['event_data'] ) ) {
return false;
}
/** @var Events_DB_Manager $event_tracker_db_manager */
$event_tracker_db_manager = Plugin::$instance->common
->get_component( 'event-tracker' )
->get_component( 'events-db' );
$event_tracker_db_manager->create_entry( $request_body['event_data'] );
return true;
}
}
common/modules/event-tracker/db.php 0000644 00000011405 14720700033 0013351 0 ustar 00 wpdb->prefix . self::TABLE_NAME;
}
/**
* Prepare Database for Entry
*
* The events database should have a limit of up to 1000 event entries stored daily.
* Before adding a new entry to the database, we make sure that the limit of 1000 events is not reached.
* If there are 1000 or more entries in the DB, we delete the earliest-inserted entry before inserting a new one.
*
* @since 3.6.0
*/
public function prepare_db_for_entry() {
$events = $this->get_event_ids_from_db();
if ( 1000 <= count( $events ) ) {
$event_ids = [];
foreach ( $events as $event ) {
$event_ids[] = $event->id;
}
// Sort the array by entry ID
array_multisort( $event_ids, SORT_ASC, $events );
// Delete the smallest ID (which is the earliest DB entry)
$this->wpdb->delete( $this->get_table_name(), [ 'ID' => $events[0]->id ] );
}
}
/**
* Create Entry
*
* Adds an event entry to the database.
*
* @since 3.6.0
*/
public function create_entry( $event_data ) {
$this->prepare_db_for_entry();
$connect = Plugin::$instance->common->get_component( 'connect' );
/** @var Library $library */
$library = $connect->get_apps()['library'];
if ( ! isset( $event_data['details'] ) ) {
$event_data['details'] = [];
}
if ( $library->is_connected() ) {
$user_connect_data = get_user_option( Common_App::OPTION_CONNECT_COMMON_DATA_KEY );
// Add the user's client ID to the event.
$event_data['details']['client_id'] = $user_connect_data['client_id'];
}
$event_data['details'] = json_encode( $event_data['details'] );
$entry = [
'event_data' => wp_json_encode( $event_data ),
'created_at' => $event_data['ts'],
];
$this->wpdb->insert( $this->get_table_name(), $entry );
}
/**
* Get Event IDs From DB
*
* Fetches the IDs of all events saved in the database.
*
* @since 3.6.0
*
* @return array|object|null
*/
public function get_event_ids_from_db() {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
return $this->wpdb->get_results( "SELECT id FROM {$this->get_table_name()}" );
}
/**
* Reset Table
*
* Empties the contents of the Events DB table.
*
* @since 3.6.0
*/
public static function reset_table() {
global $wpdb;
$table_name = $wpdb->prefix . self::TABLE_NAME;
// Delete all content of the table.
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( "TRUNCATE TABLE {$table_name}" );
}
/**
* Create Table
*
* Creates the `wp_e_events` database table.
*
* @since 3.6.0
*
* @param string $query to that looks for the Events table in the DB. Used for checking if table was created.
*/
private function create_table( $query ) {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$table_name = $this->get_table_name();
$charset_collate = $this->wpdb->get_charset_collate();
$e_events_table = "CREATE TABLE `{$table_name}` (
id bigint(20) unsigned auto_increment primary key,
event_data text null,
created_at datetime not null
) {$charset_collate};";
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$this->wpdb->query( $e_events_table );
// Check if table was created successfully.
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
if ( $this->wpdb->get_var( $query ) === $table_name ) {
update_option( self::DB_VERSION_OPTION_KEY, self::CURRENT_DB_VERSION, false );
}
}
/**
* Add Indexes
*
* Adds an index to the events table for the creation date column.
*
* @since 3.6.0
*/
private function add_indexes() {
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$this->wpdb->query( 'ALTER TABLE ' . $this->get_table_name() . '
ADD INDEX `created_at_index` (`created_at`)
' );
}
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
// Check if table exists. If not, create it.
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $this->get_table_name() ) );
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
if ( $wpdb->get_var( $query ) !== $this->get_table_name() ) {
$this->create_table( $query );
$this->add_indexes();
}
}
}
common/modules/event-tracker/personal-data.php 0000644 00000003711 14720700033 0015517 0 ustar 00 common
->get_component( 'event-tracker' )
->get_component( 'events-db' );
$events = $event_tracker_db_manager->get_event_ids_from_db();
$events_count = count( $events );
DB::reset_table();
// Validate table deleted
$updated_events = $event_tracker_db_manager->get_event_ids_from_db();
$updated_events_count = count( $updated_events );
return [
'items_removed' => $events_count - $updated_events_count,
'items_retained' => 0,
'messages' => [],
'done' => 0 === $updated_events_count,
];
}
/**
* Add eraser to the list of erasers.
*
* @param $erasers
*
* @return array[]
*/
private function add_eraser( $erasers ) {
return $erasers + [
self::WP_KEY => [
'eraser_friendly_name' => $this->get_title(),
'callback' => function () {
return $this->erase_data();
},
],
];
}
/**
* Personal_Data constructor.
*/
public function __construct() {
add_filter( 'wp_privacy_personal_data_erasers', function ( $exporters ) {
return $this->add_eraser( $exporters );
} );
}
}
common/modules/ajax/module.php 0000644 00000016235 14720700033 0012430 0 ustar 00 ajax_actions[ $tag ] = compact( 'tag', 'callback' );
}
/**
* Handle ajax request.
*
* Verify ajax nonce, and run all the registered actions for this request.
*
* Fired by `wp_ajax_elementor_ajax` action.
*
* @since 2.0.0
* @access public
*/
public function handle_ajax_request() {
if ( ! $this->verify_request_nonce() ) {
$this->add_response_data( false, esc_html__( 'Token Expired.', 'elementor' ) )
->send_error( Exceptions::UNAUTHORIZED );
}
$editor_post_id = 0;
if ( ! empty( $_REQUEST['editor_post_id'] ) ) {
$editor_post_id = absint( $_REQUEST['editor_post_id'] );
Plugin::$instance->db->switch_to_post( $editor_post_id );
}
/**
* Register ajax actions.
*
* Fires when an ajax request is received and verified.
*
* Used to register new ajax action handles.
*
* @since 2.0.0
*
* @param self $this An instance of ajax manager.
*/
do_action( 'elementor/ajax/register_actions', $this );
if ( ! empty( $_REQUEST['actions'] ) ) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, each action should sanitize its own data.
$this->requests = json_decode( wp_unslash( $_REQUEST['actions'] ), true );
}
foreach ( $this->requests as $id => $action_data ) {
$this->current_action_id = $id;
if ( ! isset( $this->ajax_actions[ $action_data['action'] ] ) ) {
$this->add_response_data( false, esc_html__( 'Action not found.', 'elementor' ), Exceptions::BAD_REQUEST );
continue;
}
if ( $editor_post_id ) {
$action_data['data']['editor_post_id'] = $editor_post_id;
}
try {
$data = $action_data['data'] ?? [];
$results = call_user_func( $this->ajax_actions[ $action_data['action'] ]['callback'], $data, $this );
if ( false === $results ) {
$this->add_response_data( false );
} else {
$this->add_response_data( true, $results );
}
} catch ( \Exception $e ) {
$this->add_response_data( false, $e->getMessage(), $e->getCode() );
}
}
$this->current_action_id = null;
$this->send_success();
}
/**
* Get current action data.
*
* Retrieve the data for the current ajax request.
*
* @since 2.0.1
* @access public
*
* @return bool|mixed Ajax request data if action exist, False otherwise.
*/
public function get_current_action_data() {
if ( ! $this->current_action_id ) {
return false;
}
return $this->requests[ $this->current_action_id ];
}
/**
* Create nonce.
*
* Creates a cryptographic token to
* give the user an access to Elementor ajax actions.
*
* @since 2.3.0
* @access public
*
* @return string The nonce token.
*/
public function create_nonce() {
return wp_create_nonce( self::NONCE_KEY );
}
/**
* Verify request nonce.
*
* Whether the request nonce verified or not.
*
* @since 2.3.0
* @access public
*
* @return bool True if request nonce verified, False otherwise.
*/
public function verify_request_nonce() {
return wp_verify_nonce( Utils::get_super_global_value( $_REQUEST, '_nonce' ), self::NONCE_KEY );
}
protected function get_init_settings() {
return [
'url' => admin_url( 'admin-ajax.php' ),
'nonce' => $this->create_nonce(),
];
}
/**
* Ajax success response.
*
* Send a JSON response data back to the ajax request, indicating success.
*
* @since 2.0.0
* @access protected
*/
private function send_success() {
$response = [
'success' => true,
'data' => [
'responses' => $this->response_data,
],
];
$json = wp_json_encode( $response );
while ( ob_get_status() ) {
ob_end_clean();
}
if ( function_exists( 'gzencode' ) ) {
$response = gzencode( $json );
header( 'Content-Type: application/json; charset=utf-8' );
header( 'Content-Encoding: gzip' );
header( 'Content-Length: ' . strlen( $response ) );
echo $response; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo $json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
wp_die( '', '', [ 'response' => null ] );
}
/**
* Ajax failure response.
*
* Send a JSON response data back to the ajax request, indicating failure.
*
* @since 2.0.0
* @access protected
*
* @param null $code
*/
private function send_error( $code = null ) {
wp_send_json_error( [
'responses' => $this->response_data,
], $code );
}
/**
* Add response data.
*
* Add new response data to the array of all the ajax requests.
*
* @since 2.0.0
* @access protected
*
* @param bool $success True if the requests returned successfully, False
* otherwise.
* @param mixed $data Optional. Response data. Default is null.
*
* @param int $code Optional. Response code. Default is 200.
*
* @return Module An instance of ajax manager.
*/
private function add_response_data( $success, $data = null, $code = 200 ) {
$this->response_data[ $this->current_action_id ] = [
'success' => $success,
'code' => $code,
'data' => $data,
];
return $this;
}
}
common/modules/finder/module.php 0000644 00000005063 14720700033 0012751 0 ustar 00 categories_manager = new Categories_Manager();
$this->add_template();
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
}
/**
* Get name.
*
* @since 2.3.0
* @access public
*
* @return string
*/
public function get_name() {
return 'finder';
}
/**
* Add template.
*
* @since 2.3.0
* @access public
*/
public function add_template() {
Plugin::$instance->common->add_template( __DIR__ . '/template.php' );
}
/**
* Register ajax actions.
*
* @since 2.3.0
* @access public
*
* @param Ajax $ajax
*/
public function register_ajax_actions( Ajax $ajax ) {
$ajax->register_ajax_action( 'finder_get_category_items', [ $this, 'ajax_get_category_items' ] );
}
/**
* Ajax get category items.
*
* @since 2.3.0
* @access public
*
* @param array $data
*
* @return array
*/
public function ajax_get_category_items( array $data ) {
if ( ! current_user_can( 'manage_options' ) ) {
throw new \Exception( 'Access denied.' );
}
$category = $this->categories_manager->get_categories( $data['category'] );
return $category->get_category_items( $data );
}
/**
* Get init settings.
*
* @since 2.3.0
* @access protected
*
* @return array
*/
protected function get_init_settings() {
$categories = $this->categories_manager->get_categories();
$categories_data = [];
foreach ( $categories as $category_name => $category ) {
$categories_data[ $category_name ] = array_merge( $category->get_settings(), [ 'name' => $category_name ] );
}
/**
* Finder categories.
*
* Filters the list of finder categories. This hook is used to manage Finder
* categories - to add new categories, remove and edit existing categories.
*
* @since 2.3.0
*
* @param array $categories_data A list of finder categories.
*/
$categories_data = apply_filters( 'elementor/finder/categories', $categories_data );
return [
'data' => $categories_data,
];
}
}
common/modules/finder/categories/tools.php 0000644 00000004054 14720700033 0014750 0 ustar 00 [
'title' => esc_html__( 'Tools', 'elementor' ),
'icon' => 'tools',
'url' => $tools_url,
'keywords' => [ 'tools', 'regenerate css', 'safe mode', 'debug bar', 'sync library', 'elementor' ],
],
'replace-url' => [
'title' => esc_html__( 'Replace URL', 'elementor' ),
'icon' => 'tools',
'url' => $tools_url . '#tab-replace_url',
'keywords' => [ 'tools', 'replace url', 'domain', 'elementor' ],
],
'maintenance-mode' => [
'title' => esc_html__( 'Maintenance Mode', 'elementor' ),
'icon' => 'tools',
'url' => $tools_url . '#tab-maintenance_mode',
'keywords' => [ 'tools', 'maintenance', 'coming soon', 'elementor' ],
],
'import-export' => [
'title' => esc_html__( 'Import Export', 'elementor' ),
'icon' => 'import-export',
'url' => $tools_url . '#tab-import-export-kit',
'keywords' => [ 'tools', 'import export', 'import', 'export', 'kit' ],
],
];
if ( ElementorTools::can_user_rollback_versions() ) {
$items['version-control'] = [
'title' => esc_html__( 'Version Control', 'elementor' ),
'icon' => 'time-line',
'url' => $tools_url . '#tab-versions',
'keywords' => [ 'tools', 'version', 'control', 'rollback', 'beta', 'elementor' ],
];
}
return $items;
}
}
common/modules/finder/categories/site.php 0000644 00000004046 14720700033 0014555 0 ustar 00 [
'title' => esc_html__( 'Homepage', 'elementor' ),
'url' => home_url(),
'icon' => 'home-heart',
'keywords' => [ 'home', 'page' ],
],
'wordpress-dashboard' => [
'title' => esc_html__( 'Dashboard', 'elementor' ),
'icon' => 'dashboard',
'url' => admin_url(),
'keywords' => [ 'dashboard', 'wordpress' ],
],
'wordpress-menus' => [
'title' => esc_html__( 'Menus', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'nav-menus.php' ),
'keywords' => [ 'menu', 'wordpress' ],
],
'wordpress-themes' => [
'title' => esc_html__( 'Themes', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'themes.php' ),
'keywords' => [ 'themes', 'wordpress' ],
],
'wordpress-customizer' => [
'title' => esc_html__( 'Customizer', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'customize.php' ),
'keywords' => [ 'customizer', 'wordpress' ],
],
'wordpress-plugins' => [
'title' => esc_html__( 'Plugins', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'plugins.php' ),
'keywords' => [ 'plugins', 'wordpress' ],
],
'wordpress-users' => [
'title' => esc_html__( 'Users', 'elementor' ),
'icon' => 'wordpress',
'url' => admin_url( 'users.php' ),
'keywords' => [ 'users', 'profile', 'wordpress' ],
],
];
}
}
common/modules/finder/categories/general.php 0000644 00000004450 14720700033 0015225 0 ustar 00 [
'title' => esc_html__( 'Saved Templates', 'elementor' ),
'icon' => 'library-save',
'url' => Source_Local::get_admin_url(),
'keywords' => [ 'template', 'section', 'page', 'library' ],
],
'system-info' => [
'title' => esc_html__( 'System Info', 'elementor' ),
'icon' => 'info-circle-o',
'url' => admin_url( 'admin.php?page=elementor-system-info' ),
'keywords' => [ 'system', 'info', 'environment', 'elementor' ],
],
'role-manager' => [
'title' => esc_html__( 'Role Manager', 'elementor' ),
'icon' => 'person',
'url' => Role_Manager::get_url(),
'keywords' => [ 'role', 'manager', 'user', 'elementor' ],
],
'knowledge-base' => [
'title' => esc_html__( 'Knowledge Base', 'elementor' ),
'url' => admin_url( 'admin.php?page=go_knowledge_base_site' ),
'keywords' => [ 'help', 'knowledge', 'docs', 'elementor' ],
],
'theme-builder' => [
'title' => esc_html__( 'Theme Builder', 'elementor' ),
'icon' => 'library-save',
'url' => Plugin::$instance->app->get_settings( 'menu_url' ),
'keywords' => [ 'template', 'header', 'footer', 'single', 'archive', 'search', '404', 'library' ],
],
'kit-library' => [
'title' => esc_html__( 'Kit Library', 'elementor' ),
'icon' => 'kit-parts',
'url' => Plugin::$instance->app->get_base_url() . '#/kit-library',
'keywords' => [ 'kit library', 'kit', 'library', 'site parts', 'parts', 'assets', 'templates' ],
],
];
}
}
common/modules/finder/categories/edit.php 0000644 00000005270 14720700033 0014536 0 ustar 00 false,
] );
$post_types[] = Source_Local::CPT;
$document_types = Plugin::$instance->documents->get_document_types( [
'is_editable' => true,
'show_in_finder' => true,
] );
$recently_edited_query_args = [
'no_found_rows' => true,
'post_type' => $post_types,
'post_status' => [ 'publish', 'draft', 'private', 'pending', 'future' ],
'posts_per_page' => '10',
'meta_query' => [
[
'key' => '_elementor_edit_mode',
'value' => 'builder',
],
[
'relation' => 'or',
[
'key' => Document::TYPE_META_KEY,
'compare' => 'NOT EXISTS',
],
[
'key' => Document::TYPE_META_KEY,
'value' => array_keys( $document_types ),
],
],
],
'orderby' => 'modified',
's' => $options['filter'],
];
$recently_edited_query = new \WP_Query( $recently_edited_query_args );
$items = [];
/** @var \WP_Post $post */
foreach ( $recently_edited_query->posts as $post ) {
$document = Plugin::$instance->documents->get( $post->ID );
if ( ! $document ) {
continue;
}
$is_template = Source_Local::CPT === $post->post_type;
$description = $document->get_title();
$icon = 'document-file';
if ( $is_template ) {
$description = esc_html__( 'Template', 'elementor' ) . ' / ' . $description;
$icon = 'post-title';
}
$items[] = [
'icon' => $icon,
'title' => esc_html( $post->post_title ),
'description' => $description,
'url' => $document->get_edit_url(),
'actions' => [
[
'name' => 'view',
'url' => $document->get_permalink(),
'icon' => 'preview-medium',
],
],
];
}
return $items;
}
}
common/modules/finder/categories/settings.php 0000644 00000004533 14720700033 0015452 0 ustar 00 [
'title' => esc_html__( 'General Settings', 'elementor' ),
'url' => ElementorSettings::get_settings_tab_url( 'general' ),
'keywords' => [ 'general', 'settings', 'elementor' ],
],
'integrations' => [
'title' => esc_html__( 'Integrations', 'elementor' ),
'url' => ElementorSettings::get_settings_tab_url( 'integrations' ),
'keywords' => [ 'integrations', 'settings', 'elementor' ],
],
'advanced' => [
'title' => esc_html__( 'Advanced', 'elementor' ),
'url' => ElementorSettings::get_settings_tab_url( 'advanced' ),
'keywords' => [ 'advanced', 'settings', 'elementor' ],
],
'performance' => [
'title' => esc_html__( 'Performance', 'elementor' ),
'url' => ElementorSettings::get_settings_tab_url( 'performance' ),
'keywords' => [ 'performance', 'settings', 'elementor' ],
],
'experiments' => [
'title' => esc_html__( 'Experiments', 'elementor' ),
'url' => ElementorSettings::get_settings_tab_url( 'experiments' ),
'keywords' => [ 'settings', 'elementor', 'experiments' ],
],
'features' => [
'title' => esc_html__( 'Features', 'elementor' ),
'url' => ElementorSettings::get_settings_tab_url( 'experiments' ),
'keywords' => [ 'settings', 'elementor', 'features' ],
],
'element-manager' => [
'title' => esc_html__( 'Element Manager', 'elementor' ),
'url' => admin_url( 'admin.php?page=' . ElementManagerModule::PAGE_ID ),
'keywords' => [ 'settings', 'elements', 'widgets', 'manager' ],
],
];
}
}
common/modules/finder/categories/create.php 0000644 00000005535 14720700033 0015060 0 ustar 00 documents->get_document_types();
// TODO: Remove - Support 'post' backwards compatibility - See `Documents_Manager::register_default_types()`.
unset( $registered_document_types['post'] );
$elementor_supported_post_types = array_flip( get_post_types_by_support( 'elementor' ) );
foreach ( $registered_document_types as $document_name => $document_class ) {
$document_properties = $document_class::get_properties();
if ( empty( $document_properties['show_in_finder'] ) ) {
continue;
}
if ( ! empty( $document_properties['cpt'] ) ) {
foreach ( $document_properties['cpt'] as $cpt ) {
unset( $elementor_supported_post_types[ $cpt ] );
}
}
$result[ $document_name ] = $this->create_item_url_by_document_class( $document_class );
}
foreach ( $elementor_supported_post_types as $post_type => $val ) {
$result[ $post_type ] = $this->create_item_url_by_post_type( $post_type );
}
return $result;
}
private function create_item_url_by_post_type( $post_type ) {
$post_type_object = get_post_type_object( $post_type );
// If there is an old post type from inactive plugins.
if ( ! $post_type_object ) {
return false;
}
return $this->get_create_new_template(
sprintf( __( 'Add New %s', 'elementor' ), $post_type_object->labels->singular_name ),
Plugin::$instance->documents->get_create_new_post_url( $post_type )
);
}
private function create_item_url_by_document_class( $document_class ) {
$result = $this->get_create_new_template(
$document_class::get_add_new_title(),
$document_class::get_create_url()
);
$lock_behavior = $document_class::get_lock_behavior_v2();
$is_locked = ! empty( $lock_behavior ) && $lock_behavior->is_locked();
if ( $is_locked ) {
$result['lock'] = $lock_behavior->get_config();
}
return $result;
}
private function get_create_new_template( $add_new_title, $url ) {
return [
'title' => $add_new_title,
'icon' => 'plus-circle-o',
'url' => $url,
'keywords' => [ $add_new_title, 'post', 'page', 'template', 'new', 'create' ],
];
}
}
common/modules/finder/template.php 0000644 00000003631 14720700033 0013276 0 ustar 00
common/modules/finder/categories-manager.php 0000644 00000007272 14720700033 0015225 0 ustar 00 modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
__METHOD__,
'3.5.0',
'register()'
);
$this->register( $category, $category_name );
}
/**
* Register finder category.
*
* @since 3.5.0
* @access public
*
* @param Base_Category $finder_category_instance An Instance of a category.
* @param string $finder_category_name A Category name. Deprecated parameter.
*
* @return void
*/
public function register( Base_Category $finder_category_instance, $finder_category_name = null ) {
// TODO: For BC. Remove in the future.
if ( $finder_category_name ) {
Plugin::instance()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_argument(
'$finder_category_name', '3.5.0'
);
} else {
$finder_category_name = $finder_category_instance->get_id();
}
$this->categories[ $finder_category_name ] = $finder_category_instance;
}
/**
* Unregister a finder category.
*
* @param string $finder_category_name - Category to unregister.
*
* @return void
* @since 3.6.0
* @access public
*/
public function unregister( $finder_category_name ) {
unset( $this->categories[ $finder_category_name ] );
}
/**
* Get categories.
*
* Retrieve the registered categories, or a specific category if the category name
* is provided as a parameter.
*
* @since 2.3.0
* @access public
*
* @param string $category Category name.
*
* @return Base_Category|Base_Category[]|null
*/
public function get_categories( $category = '' ) {
if ( ! $this->categories ) {
$this->init_categories();
}
if ( $category ) {
if ( isset( $this->categories[ $category ] ) ) {
return $this->categories[ $category ];
}
return null;
}
return $this->categories;
}
/**
* Init categories.
*
* Used to initialize the native finder categories.
*
* @since 2.3.0
* @access private
*/
private function init_categories() {
foreach ( $this->categories_list as $category_name ) {
$class_name = __NAMESPACE__ . '\Categories\\' . $category_name;
$this->register( new $class_name() );
}
/**
* Elementor Finder categories init.
*
* Fires after Elementor Finder initialize it's native categories.
*
* This hook should be used to add your own Finder categories.
*
* @since 2.3.0
* @deprecated 3.5.0 Use `elementor/finder/register` hook instead.
*
* @param Categories_Manager $this.
*/
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action(
'elementor/finder/categories/init',
[ $this ],
'3.5.0',
'elementor/finder/register'
);
/**
* Elementor Finder categories registration.
*
* Fires after Elementor Finder initialize it's native categories.
*
* This hook should be used to register your own Finder categories.
*
* @since 3.5.0
*
* @param Categories_Manager $this Finder Categories manager.
*/
do_action( 'elementor/finder/register', $this );
}
}
common/modules/finder/base-category.php 0000644 00000003126 14720700033 0014207 0 ustar 00 modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
get_class( $this ) . '::' . __FUNCTION__,
'3.5.0',
'This method will be replaced with an abstract method.'
);
return '';
}
/**
* Get category items.
*
* @since 2.3.0
* @abstract
* @access public
*
* @param array $options
*
* @return array
*/
abstract public function get_category_items( array $options = [] );
/**
* Is dynamic.
*
* Determine if the category is dynamic.
*
* @since 2.3.0
* @access public
*
* @return bool
*/
public function is_dynamic() {
return false;
}
/**
* Get init settings.
*
* @since 2.3.0
* @access protected
*
* @return array
*/
protected function get_init_settings() {
$settings = [
'title' => $this->get_title(),
'dynamic' => $this->is_dynamic(),
];
if ( ! $settings['dynamic'] ) {
$settings['items'] = $this->get_category_items();
}
return $settings;
}
}
common/modules/connect/module.php 0000644 00000013202 14720700033 0013125 0 ustar 00 registered_apps = [
'connect' => Connect::get_class_name(),
'library' => Library::get_class_name(),
];
// When using REST API the parent module is construct after the action 'elementor/init'
// so this part of code make sure to register the module "apps".
if ( did_action( 'elementor/init' ) ) {
$this->init();
} else {
// Note: The priority 11 is for allowing plugins to add their register callback on elementor init.
add_action( 'elementor/init', [ $this, 'init' ], 11 );
}
add_filter( 'elementor/tracker/send_tracking_data_params', function ( $params ) {
return $this->add_tracking_data( $params );
} );
}
/**
* Register default apps.
*
* Registers the default apps.
*
* @since 2.3.0
* @access public
*/
public function init() {
if ( is_admin() ) {
$this->admin_page = new Admin();
}
/**
* Register Elementor apps.
*
* Fires after Elementor registers the default apps.
*
* @since 2.3.0
*
* @param self $this The apps manager instance.
*/
do_action( 'elementor/connect/apps/register', $this );
foreach ( $this->registered_apps as $slug => $class ) {
$this->apps[ $slug ] = new $class();
}
}
/**
* Register app.
*
* Registers an app.
*
* @since 2.3.0
* @access public
*
* @param string $slug App slug.
* @param string $class App full class name.
*
* @return self The updated apps manager instance.
*/
public function register_app( $slug, $class ) {
$this->registered_apps[ $slug ] = $class;
return $this;
}
/**
* Get app instance.
*
* Retrieve the app instance.
*
* @since 2.3.0
* @access public
*
* @param $slug
*
* @return Base_App|null
*/
public function get_app( $slug ) {
if ( isset( $this->apps[ $slug ] ) ) {
return $this->apps[ $slug ];
}
return null;
}
/**
* @since 2.3.0
* @access public
* @return Base_App[]
*/
public function get_apps() {
return $this->apps;
}
/**
* @since 2.3.0
* @access public
*/
public function register_category( $slug, $args ) {
$this->categories[ $slug ] = $args;
return $this;
}
/**
* @since 2.3.0
* @access public
*/
public function get_categories() {
return $this->categories;
}
/**
* @param string $context Where this subscription plan should be shown.
*
* @return array
*/
public function get_subscription_plans( $context = '' ) {
$base_url = Utils::has_pro() ? 'https://my.elementor.com/upgrade-subscription' : 'https://elementor.com/pro';
$promotion_url = $base_url . '/?utm_source=' . $context . '&utm_medium=wp-dash&utm_campaign=gopro';
return [
static::ACCESS_TIER_FREE => [
'label' => null,
'promotion_url' => null,
'color' => null,
],
static::ACCESS_TIER_ESSENTIAL => [
'label' => 'Pro',
'promotion_url' => $promotion_url,
'color' => '#92003B',
],
static::ACCESS_TIER_ESSENTIAL_OCT_2023 => [
'label' => 'Advanced', // Should be the same label as "Advanced".
'promotion_url' => $promotion_url,
'color' => '#92003B',
],
static::ACCESS_TIER_ADVANCED => [
'label' => 'Advanced',
'promotion_url' => $promotion_url,
'color' => '#92003B',
],
static::ACCESS_TIER_EXPERT => [
'label' => 'Expert',
'promotion_url' => $promotion_url,
'color' => '#92003B',
],
static::ACCESS_TIER_AGENCY => [
'label' => 'Agency',
'promotion_url' => $promotion_url,
'color' => '#92003B',
],
];
}
private function add_tracking_data( $params ) {
$users = [];
$users_query = new WP_User_Query( [
'count_total' => false, // Disable SQL_CALC_FOUND_ROWS.
'meta_query' => [
'key' => Common_App::OPTION_CONNECT_COMMON_DATA_KEY,
'compare' => 'EXISTS',
],
] );
foreach ( $users_query->get_results() as $user ) {
$connect_common_data = get_user_option( Common_App::OPTION_CONNECT_COMMON_DATA_KEY, $user->ID );
if ( $connect_common_data ) {
$users [] = [
'id' => $user->ID,
'email' => $connect_common_data['user']->email,
'roles' => implode( ', ', $user->roles ),
];
}
}
$params['usages'][ $this->get_name() ] = [
'site_key' => get_option( Base_App::OPTION_CONNECT_SITE_KEY ),
'count' => count( $users ),
'users' => $users,
];
return $params;
}
}
common/modules/connect/apps/base-app.php 0000644 00000047652 14720700033 0014313 0 ustar 00 get_slug();
}
/**
* @since 2.3.0
* @access protected
* @abstract
*/
abstract protected function update_settings();
/**
* @since 2.3.0
* @access public
* @static
*/
public static function get_class_name() {
return get_called_class();
}
/**
* @access public
* @abstract
*/
public function render_admin_widget() {
// PHPCS - the method get_title return a plain string.
echo '' . $this->get_title() . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ( $this->is_connected() ) {
$remote_user = $this->get( 'user' );
$title = sprintf(
/* translators: %s: Remote user. */
esc_html__( 'Connected as %s', 'elementor' ),
'' . esc_html( $remote_user->email ) . ''
);
$label = esc_html__( 'Disconnect', 'elementor' );
$url = $this->get_admin_url( 'disconnect' );
$attr = '';
echo sprintf(
'%s %s',
// PHPCS - the variable $title is already escaped above.
$title, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
// PHPCS - the variable $attr is a plain string.
$attr, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
esc_attr( $url ),
esc_html( $label )
);
} else {
echo 'Not Connected';
}
echo '
';
$this->print_app_info();
if ( current_user_can( 'manage_options' ) ) {
printf( '', esc_url( $this->get_admin_url( 'reset' ) ), esc_html__( 'Reset Data', 'elementor' ) );
}
echo '
';
}
/**
* @since 2.3.0
* @access protected
*/
protected function get_option_name() {
return static::OPTION_NAME_PREFIX . $this->get_slug();
}
/**
* @since 2.3.0
* @access public
*/
public function admin_notice() {
$notices = $this->get( 'notices' );
if ( ! $notices ) {
return;
}
$this->print_notices( $notices );
$this->delete( 'notices' );
}
public function get_app_token_from_cli_token( $cli_token ) {
$response = $this->request( 'get_app_token_from_cli_token', [
'cli_token' => $cli_token,
] );
if ( is_wp_error( $response ) ) {
// PHPCS - the variable $response does not contain a user input value.
wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
// Use state as usual.
$_REQUEST['state'] = $this->get( 'state' );
$_REQUEST['code'] = $response->code;
}
/**
* @since 2.3.0
* @access public
*/
public function action_authorize() {
if ( $this->is_connected() ) {
$this->add_notice( esc_html__( 'Already connected.', 'elementor' ), 'info' );
$this->redirect_to_admin_page();
return;
}
$this->set_client_id();
$this->set_request_state();
$this->redirect_to_remote_authorize_url();
}
public function action_reset() {
if ( current_user_can( 'manage_options' ) ) {
delete_option( static::OPTION_CONNECT_SITE_KEY );
delete_option( 'elementor_remote_info_library' );
}
$this->redirect_to_admin_page();
}
/**
* @since 2.3.0
* @access public
*/
public function action_get_token() {
if ( $this->is_connected() ) {
$this->redirect_to_admin_page();
}
//phpcs:ignore WordPress.Security.NonceVerification.Recommended - The user as been authorized before in 'connect'.
$state = Utils::get_super_global_value( $_REQUEST, 'state' );
if ( $state !== $this->get( 'state' ) ) {
$this->add_notice( 'Get Token: Invalid Request.', 'error' );
$this->redirect_to_admin_page();
}
$response = $this->request( 'get_token', [
'grant_type' => 'authorization_code',
'code' => Utils::get_super_global_value( $_REQUEST, 'code' ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended
'redirect_uri' => rawurlencode( $this->get_admin_url( 'get_token' ) ),
'client_id' => $this->get( 'client_id' ),
] );
if ( is_wp_error( $response ) ) {
$notice = 'Cannot Get Token:' . $response->get_error_message();
$this->add_notice( $notice, 'error' );
$this->redirect_to_admin_page();
}
$this->delete( 'state' );
$this->set( (array) $response );
if ( ! empty( $response->data_share_opted_in ) && current_user_can( 'manage_options' ) ) {
Tracker::set_opt_in( true );
}
$this->after_connect();
// Add the notice *after* the method `after_connect`, so an app can redirect without the notice.
$this->add_notice( esc_html__( 'Connected successfully.', 'elementor' ) );
$this->redirect_to_admin_page();
}
/**
* @since 2.3.0
* @access public
*/
public function action_disconnect() {
if ( $this->is_connected() ) {
$this->disconnect();
$this->add_notice( esc_html__( 'Disconnected successfully.', 'elementor' ) );
}
$this->redirect_to_admin_page();
}
/**
* @since 2.8.0
* @access public
*/
public function action_reconnect() {
$this->disconnect();
$this->action_authorize();
}
/**
* @since 2.3.0
* @access public
*/
public function get_admin_url( $action, $params = [] ) {
$params = [
'app' => $this->get_slug(),
'action' => $action,
'nonce' => wp_create_nonce( $this->get_slug() . $action ),
] + $params;
$admin_url = Str::encode_idn_url( get_admin_url() );
$admin_url .= 'admin.php?page=' . Admin::PAGE_ID;
return add_query_arg( $params, $admin_url );
}
/**
* @since 2.3.0
* @access public
*/
public function is_connected() {
return (bool) $this->get( 'access_token' );
}
/**
* @since 2.3.0
* @access protected
*/
protected function init() {}
/**
* @since 2.3.0
* @access protected
*/
protected function init_data() {}
/**
* @since 2.3.0
* @access protected
*/
protected function after_connect() {}
/**
* @since 2.3.0
* @access public
*/
public function get( $key, $default = null ) {
$this->init_data();
return isset( $this->data[ $key ] ) ? $this->data[ $key ] : $default;
}
/**
* @since 2.3.0
* @access protected
*/
protected function set( $key, $value = null ) {
$this->init_data();
if ( is_array( $key ) ) {
$this->data = array_replace_recursive( $this->data, $key );
} else {
$this->data[ $key ] = $value;
}
$this->update_settings();
}
/**
* @since 2.3.0
* @access protected
*/
protected function delete( $key = null ) {
$this->init_data();
if ( $key ) {
unset( $this->data[ $key ] );
} else {
$this->data = [];
}
$this->update_settings();
}
/**
* @since 2.3.0
* @access protected
*/
protected function add( $key, $value, $default = '' ) {
$new_value = $this->get( $key, $default );
if ( is_array( $new_value ) ) {
$new_value[] = $value;
} elseif ( is_string( $new_value ) ) {
$new_value .= $value;
} elseif ( is_numeric( $new_value ) ) {
$new_value += $value;
}
$this->set( $key, $new_value );
}
/**
* @since 2.3.0
* @access protected
*/
protected function add_notice( $content, $type = 'success' ) {
$this->add( 'notices', compact( 'content', 'type' ), [] );
}
/**
* @param $action
* @param array $request_body
* @param false $as_array
*
* @return mixed|\WP_Error
*/
protected function request( $action, $request_body = [], $as_array = false ) {
$request_body = $this->get_connect_info() + $request_body;
return $this->http_request(
'POST',
$action,
[
'timeout' => 25,
'body' => $request_body,
'headers' => $this->is_connected() ?
[ 'X-Elementor-Signature' => $this->generate_signature( $request_body ) ] :
[],
],
[
'return_type' => $as_array ? static::HTTP_RETURN_TYPE_ARRAY : static::HTTP_RETURN_TYPE_OBJECT,
]
);
}
/**
* Get Base Connect Info
*
* Returns an array of connect info.
*
* @return array
*/
protected function get_base_connect_info() {
return [
'app' => $this->get_slug(),
'access_token' => $this->get( 'access_token' ),
'client_id' => $this->get( 'client_id' ),
'local_id' => get_current_user_id(),
'site_key' => $this->get_site_key(),
'home_url' => trailingslashit( home_url() ),
];
}
/**
* Get all the connect information
*
* @return array
*/
protected function get_connect_info() {
$connect_info = $this->get_base_connect_info();
$additional_info = [];
/**
* Additional connect info.
*
* Filters the connection information when connecting to Elementor servers.
* This hook can be used to add more information or add more data.
*
* @param array $additional_info Additional connecting information array.
* @param Base_App $this The base app instance.
*/
$additional_info = apply_filters( 'elementor/connect/additional-connect-info', $additional_info, $this );
return array_merge( $connect_info, $additional_info );
}
/**
* @param $endpoint
*
* @return array
*/
protected function generate_authentication_headers( $endpoint ) {
$connect_info = ( new Collection( $this->get_connect_info() ) )
->map_with_keys( function ( $value, $key ) {
// For bc `get_connect_info` returns the connect info with underscore,
// headers with underscore are not valid, so all the keys with underscore will be replaced to hyphen.
return [ str_replace( '_', '-', $key ) => $value ];
} )
->replace_recursive( [ 'endpoint' => $endpoint ] )
->sort_keys();
return $connect_info
->merge( [ 'X-Elementor-Signature' => $this->generate_signature( $connect_info->all() ) ] )
->all();
}
/**
* Send an http request
*
* @param $method
* @param $endpoint
* @param array $args
* @param array $options
*
* @return mixed|\WP_Error
*/
protected function http_request( $method, $endpoint, $args = [], $options = [] ) {
$options = wp_parse_args( $options, [
'return_type' => static::HTTP_RETURN_TYPE_OBJECT,
] );
$args = array_replace_recursive( [
'headers' => $this->is_connected() ? $this->generate_authentication_headers( $endpoint ) : [],
'method' => $method,
'timeout' => 10,
], $args );
$response = $this->http->request_with_fallback(
$this->get_generated_urls( $endpoint ),
$args
);
if ( is_wp_error( $response ) && empty( $options['with_error_data'] ) ) {
// PHPCS - the variable $response does not contain a user input value.
wp_die( $response, [ 'back_link' => true ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$body = wp_remote_retrieve_body( $response );
$response_code = (int) wp_remote_retrieve_response_code( $response );
if ( ! $response_code ) {
return new \WP_Error( 500, 'No Response' );
}
// Server sent a success message without content.
if ( 'null' === $body ) {
$body = true;
}
$body = json_decode( $body, static::HTTP_RETURN_TYPE_ARRAY === $options['return_type'] );
if ( false === $body ) {
return new \WP_Error( 422, 'Wrong Server Response' );
}
if ( 200 !== $response_code ) {
// In case $as_array = true.
$body = (object) $body;
$message = isset( $body->message ) ? $body->message : wp_remote_retrieve_response_message( $response );
$code = (int) ( isset( $body->code ) ? $body->code : $response_code );
if ( ! $code ) {
$code = $response_code;
}
if ( 401 === $code ) {
$this->delete();
$should_retry = ! in_array( $this->auth_mode, [ 'xhr', 'cli' ], true );
if ( $should_retry ) {
$this->action_authorize();
}
}
if ( isset( $options['with_error_data'] ) && true === $options['with_error_data'] ) {
return new \WP_Error( $code, $message, $body );
}
return new \WP_Error( $code, $message );
}
return $body;
}
/**
* Create a signature for the http request
*
* @param array $payload
*
* @return false|string
*/
private function generate_signature( $payload = [] ) {
return hash_hmac(
'sha256',
wp_json_encode( $payload, JSON_NUMERIC_CHECK ),
$this->get( 'access_token_secret' )
);
}
/**
* @since 2.3.0
* @access protected
*/
protected function get_api_url() {
return static::API_URL . '/' . $this->get_slug();
}
/**
* @since 2.3.0
* @access protected
*/
protected function get_remote_site_url() {
return static::SITE_URL . '/' . $this->get_slug();
}
/**
* @since 2.3.0
* @access protected
*/
protected function get_remote_authorize_url() {
$redirect_uri = $this->get_auth_redirect_uri();
$allowed_query_params_to_propagate = [
'utm_source',
'utm_medium',
'utm_campaign',
'utm_term',
'utm_content',
'source',
'screen_hint',
];
$query_params = ( new Collection( $_GET ) ) // phpcs:ignore
->only( $allowed_query_params_to_propagate )
->merge( [
'action' => 'authorize',
'response_type' => 'code',
'client_id' => $this->get( 'client_id' ),
'auth_secret' => $this->get( 'auth_secret' ),
'state' => $this->get( 'state' ),
'redirect_uri' => rawurlencode( $redirect_uri ),
'may_share_data' => current_user_can( 'manage_options' ) && ! Tracker::is_allow_track(),
'reconnect_nonce' => wp_create_nonce( $this->get_slug() . 'reconnect' ),
] );
return add_query_arg( $query_params->all(), $this->get_remote_site_url() );
}
/**
* @since 2.3.0
* @access protected
*/
protected function redirect_to_admin_page( $url = '' ) {
if ( ! $url ) {
$url = Admin::$url;
}
switch ( $this->auth_mode ) {
case 'popup':
$this->print_popup_close_script( $url );
break;
case 'cli':
$this->admin_notice();
die;
default:
wp_safe_redirect( $url );
die;
}
}
/**
* @since 2.3.0
* @access protected
*/
protected function set_client_id() {
$source = Utils::get_super_global_value( $_REQUEST, 'source' ) ?? ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
$response = $this->request(
'get_client_id',
[
'source' => esc_attr( $source ),
]
);
if ( is_wp_error( $response ) ) {
// PHPCS - the variable $response does not contain a user input value.
wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$this->set( 'client_id', $response->client_id );
$this->set( 'auth_secret', $response->auth_secret );
}
/**
* @since 2.3.0
* @access protected
*/
protected function set_request_state() {
$this->set( 'state', wp_generate_password( 12, false ) );
}
protected function get_popup_success_event_data() {
return [];
}
/**
* @since 2.3.0
* @access protected
*/
protected function print_popup_close_script( $url ) {
$data = $this->get_popup_success_event_data();
?>
is_connected() ) {
// Try update the server, but not needed to handle errors.
$this->request( 'disconnect' );
}
$this->delete();
}
/**
* @since 2.3.0
* @access protected
*/
public function get_site_key() {
$site_key = get_option( static::OPTION_CONNECT_SITE_KEY );
if ( ! $site_key ) {
$site_key = md5( uniqid( wp_generate_password() ) );
update_option( static::OPTION_CONNECT_SITE_KEY, $site_key );
}
return $site_key;
}
protected function redirect_to_remote_authorize_url() {
switch ( $this->auth_mode ) {
case 'cli':
$this->get_app_token_from_cli_token( Utils::get_super_global_value( $_REQUEST, 'token' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
return;
default:
wp_redirect( $this->get_remote_authorize_url() ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Safe redirect is used here.
die;
}
}
protected function get_auth_redirect_uri() {
$redirect_uri = $this->get_admin_url( 'get_token' );
switch ( $this->auth_mode ) {
case 'popup':
$redirect_uri = add_query_arg( [
'mode' => 'popup',
'callback_id' => esc_attr( Utils::get_super_global_value( $_REQUEST, 'callback_id' ) ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
], $redirect_uri );
break;
}
return $redirect_uri;
}
protected function print_notices( $notices ) {
switch ( $this->auth_mode ) {
case 'cli':
foreach ( $notices as $notice ) {
printf( '[%s] %s', wp_kses_post( $notice['type'] ), wp_kses_post( $notice['content'] ) );
}
break;
default:
/**
* @var Admin_Notices $admin_notices
*/
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
foreach ( $notices as $notice ) {
$options = [
'description' => wp_kses_post( wpautop( $notice['content'] ) ),
'type' => $notice['type'],
'icon' => false,
];
$admin_notices->print_admin_notice( $options );
}
}
}
protected function get_app_info() {
return [];
}
protected function print_app_info() {
$app_info = $this->get_app_info();
foreach ( $app_info as $key => $item ) {
if ( $item['value'] ) {
$status = 'Exist';
$color = 'green';
} else {
$status = 'Empty';
$color = 'red';
}
// PHPCS - the values of $item['label'], $color, $status are plain strings.
printf( '%s: %s
', $item['label'], $color, $status ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
private function get_generated_urls( $endpoint ) {
$base_urls = $this->get_api_url();
if ( ! is_array( $base_urls ) ) {
$base_urls = [ $base_urls ];
}
return array_map( function ( $base_url ) use ( $endpoint ) {
return trailingslashit( $base_url ) . $endpoint;
}, $base_urls );
}
private function init_auth_mode() {
$is_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
$is_ajax = wp_doing_ajax();
if ( $is_rest || $is_ajax ) {
// Set default to 'xhr' if rest or ajax request.
$this->set_auth_mode( 'xhr' );
}
$mode = Utils::get_super_global_value( $_REQUEST, 'mode' ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
if ( $mode ) {
$allowed_auth_modes = [
'popup',
];
if ( defined( 'WP_CLI' ) && WP_CLI ) {
$allowed_auth_modes[] = 'cli';
}
if ( in_array( $mode, $allowed_auth_modes, true ) ) {
$this->set_auth_mode( $mode );
}
}
}
public function set_auth_mode( $mode ) {
$this->auth_mode = $mode;
}
/**
* @since 2.3.0
* @access public
*/
public function __construct() {
add_action( 'admin_notices', [ $this, 'admin_notice' ] );
$this->init_auth_mode();
$this->http = new Http();
/**
* Allow extended apps to customize the __construct without call parent::__construct.
*/
$this->init();
}
}
common/modules/connect/apps/common-app.php 0000644 00000001614 14720700033 0014655 0 ustar 00 data = & self::$common_data;
}
public function action_reset() {
delete_user_option( get_current_user_id(), static::OPTION_CONNECT_COMMON_DATA_KEY );
parent::action_reset();
}
}
common/modules/connect/apps/library.php 0000644 00000007342 14720700033 0014257 0 ustar 00 is_connected() ) {
return new \WP_Error( '401', esc_html__( 'Connecting to the Library failed. Please try reloading the page and try again', 'elementor' ) );
}
$body_args = [
'id' => $id,
// Which API version is used.
'api_version' => ELEMENTOR_VERSION,
// Which language to return.
'site_lang' => get_bloginfo( 'language' ),
];
/**
* API: Template body args.
*
* Filters the body arguments send with the GET request when fetching the content.
*
* @since 1.0.0
*
* @param array $body_args Body arguments.
*/
$body_args = apply_filters( 'elementor/api/get_templates/body_args', $body_args );
$template_content = $this->request( 'get_template_content', $body_args, true );
if ( is_wp_error( $template_content ) && 401 === $template_content->get_error_code() ) {
// Normalize 401 message
return new \WP_Error( 401, esc_html__( 'Connecting to the Library failed. Please try reloading the page and try again', 'elementor' ) );
}
return $template_content;
}
public function localize_settings( $settings ) {
$is_connected = $this->is_connected();
/** @var ConnectModule $connect */
$connect = Plugin::$instance->common->get_component( 'connect' );
return array_replace_recursive( $settings, [
'library_connect' => [
'is_connected' => $is_connected,
'subscription_plans' => $connect->get_subscription_plans( 'template-library' ),
// TODO: Remove `base_access_level`.
'base_access_level' => ConnectModule::ACCESS_LEVEL_CORE,
'base_access_tier' => ConnectModule::ACCESS_TIER_FREE,
'current_access_level' => ConnectModule::ACCESS_LEVEL_CORE,
'current_access_tier' => ConnectModule::ACCESS_TIER_FREE,
],
] );
}
public function library_connect_popup_seen() {
User::set_introduction_viewed( [
'introductionKey' => 'library_connect',
] );
}
/**
* @param \Elementor\Core\Common\Modules\Ajax\Module $ajax_manager
*/
public function register_ajax_actions( $ajax_manager ) {
$ajax_manager->register_ajax_action( 'library_connect_popup_seen', [ $this, 'library_connect_popup_seen' ] );
}
/**
* After Connect
*
* After Connecting to the library, re-fetch the library data to get it up to date.
*
* @since 3.7.0
*/
protected function after_connect() {
Api::get_library_data( true );
}
protected function get_app_info() {
return [
'user_common_data' => [
'label' => 'User Common Data',
'value' => get_user_option( $this->get_option_name(), get_current_user_id() ),
],
'connect_site_key' => [
'label' => 'Site Key',
'value' => get_option( self::OPTION_CONNECT_SITE_KEY ),
],
'remote_info_library' => [
'label' => 'Remote Library Info',
'value' => get_option( 'elementor_remote_info_library' ),
],
];
}
protected function get_popup_success_event_data() {
return [
'access_level' => ConnectModule::ACCESS_LEVEL_CORE,
'access_tier' => ConnectModule::ACCESS_TIER_FREE,
];
}
protected function init() {
add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
add_filter( 'elementor/common/localize_settings', [ $this, 'localize_settings' ] );
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
}
}
common/modules/connect/apps/connect.php 0000644 00000000673 14720700033 0014244 0 ustar 00 get_option_name(), $this->data );
}
/**
* @since 2.3.0
* @access protected
*/
protected function init_data() {
$this->data = get_user_option( $this->get_option_name() );
if ( ! $this->data ) {
$this->data = [];
}
}
}
common/modules/connect/admin.php 0000644 00000003645 14720700033 0012742 0 ustar 00 register( static::PAGE_ID, new Connect_Menu_Item() );
}
/**
* @since 2.3.0
* @access public
*/
public function on_load_page() {
if ( isset( $_GET['action'], $_GET['app'] ) ) {
$manager = Plugin::$instance->common->get_component( 'connect' );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$app_slug = Utils::get_super_global_value( $_GET, 'app' );
$app = $manager->get_app( $app_slug );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = Utils::get_super_global_value( $_GET, 'action' );
$nonce_action = $app_slug . $action;
if ( ! $app ) {
wp_die( 'Unknown app: ' . esc_attr( $app_slug ) );
}
if ( ! wp_verify_nonce( Utils::get_super_global_value( $_GET, 'nonce' ), $nonce_action ) ) {
wp_die( 'Invalid Nonce', 'Invalid Nonce', [
'back_link' => true,
] );
}
$method = 'action_' . $action;
if ( method_exists( $app, $method ) ) {
call_user_func( [ $app, $method ] );
}
}
}
/**
* @since 2.3.0
* @access public
*/
public function __construct() {
self::$url = admin_url( 'admin.php?page=' . self::PAGE_ID );
add_action( 'elementor/admin/menu/register', [ $this, 'register_admin_menu' ] );
add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) {
if ( ! empty( $hooks[ static::PAGE_ID ] ) ) {
add_action( 'load-' . $hooks[ static::PAGE_ID ], [ $this, 'on_load_page' ] );
}
}, 10, 2 );
}
}
common/modules/connect/connect-menu-item.php 0000644 00000002236 14720700033 0015174 0 ustar 00 common->get_component( 'connect' )->get_apps();
?>
';
$app->render_admin_widget();
echo '
';
}
?>
add_default_templates();
add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'register_scripts' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] );
add_action( 'elementor/editor/before_enqueue_styles', [ $this, 'register_styles' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'register_styles' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'register_styles' ], 9 );
add_action( 'elementor/editor/footer', [ $this, 'print_templates' ] );
add_action( 'admin_footer', [ $this, 'print_templates' ] );
add_action( 'wp_footer', [ $this, 'print_templates' ] );
}
/**
* Init components
*
* Initializing common components.
*
* @since 2.3.0
* @access public
*/
public function init_components() {
$this->add_component( 'ajax', new Ajax() );
if ( current_user_can( 'manage_options' ) ) {
if ( ! is_customize_preview() ) {
$this->add_component( 'finder', new Finder() );
}
}
$this->add_component( 'connect', new Connect() );
$this->add_component( 'event-tracker', new Event_Tracker() );
}
/**
* Get name.
*
* Retrieve the app name.
*
* @since 2.3.0
* @access public
*
* @return string Common app name.
*/
public function get_name() {
return 'common';
}
/**
* Register scripts.
*
* Register common scripts.
*
* @since 2.3.0
* @access public
*/
public function register_scripts() {
wp_register_script(
'elementor-common-modules',
$this->get_js_assets_url( 'common-modules' ),
[],
ELEMENTOR_VERSION,
true
);
wp_register_script(
'backbone-marionette',
$this->get_js_assets_url( 'backbone.marionette', 'assets/lib/backbone/' ),
[
'backbone',
],
'2.4.5.e1',
true
);
wp_register_script(
'backbone-radio',
$this->get_js_assets_url( 'backbone.radio', 'assets/lib/backbone/' ),
[
'backbone',
],
'1.0.4',
true
);
wp_register_script(
'elementor-dialog',
$this->get_js_assets_url( 'dialog', 'assets/lib/dialog/' ),
[
'jquery-ui-position',
],
'4.9.0',
true
);
wp_enqueue_script(
'elementor-common',
$this->get_js_assets_url( 'common' ),
[
'jquery',
'jquery-ui-draggable',
'backbone-marionette',
'backbone-radio',
'elementor-common-modules',
'elementor-web-cli',
'elementor-dialog',
'wp-api-request',
'elementor-dev-tools',
],
ELEMENTOR_VERSION,
true
);
wp_set_script_translations( 'elementor-common', 'elementor' );
$this->print_config();
// Used for external plugins.
do_action( 'elementor/common/after_register_scripts', $this );
}
/**
* Register styles.
*
* Register common styles.
*
* @since 2.3.0
* @access public
*/
public function register_styles() {
wp_register_style(
'elementor-icons',
$this->get_css_assets_url( 'elementor-icons', 'assets/lib/eicons/css/' ),
[],
Icons_Manager::ELEMENTOR_ICONS_VERSION
);
wp_enqueue_style(
'elementor-common',
$this->get_css_assets_url( 'common', null, 'default', true ),
[
'elementor-icons',
],
ELEMENTOR_VERSION
);
wp_enqueue_style(
'e-theme-ui-light',
$this->get_css_assets_url( 'theme-light' ),
[],
ELEMENTOR_VERSION
);
}
/**
* Add template.
*
* @since 2.3.0
* @access public
*
* @param string $template Can be either a link to template file or template
* HTML content.
* @param string $type Optional. Whether to handle the template as path
* or text. Default is `path`.
*/
public function add_template( $template, $type = 'path' ) {
if ( 'path' === $type ) {
ob_start();
include $template;
$template = ob_get_clean();
}
$this->templates[] = $template;
}
/**
* Print Templates
*
* Prints all registered templates.
*
* @since 2.3.0
* @access public
*/
public function print_templates() {
foreach ( $this->templates as $template ) {
echo $template; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
/**
* Get init settings.
*
* Define the default/initial settings of the common app.
*
* @since 2.3.0
* @access protected
*
* @return array
*/
protected function get_init_settings() {
$active_experimental_features = Plugin::$instance->experiments->get_active_features();
$active_experimental_features = array_fill_keys( array_keys( $active_experimental_features ), true );
$config = [
'version' => ELEMENTOR_VERSION,
'isRTL' => is_rtl(),
'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
'isElementorDebug' => ( defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG ),
'activeModules' => array_keys( $this->get_components() ),
'experimentalFeatures' => $active_experimental_features,
'urls' => [
'assets' => ELEMENTOR_ASSETS_URL,
'rest' => get_rest_url(),
],
'filesUpload' => [
'unfilteredFiles' => Uploads_Manager::are_unfiltered_uploads_enabled(),
],
];
/**
* Localize common settings.
*
* Filters the editor localized settings.
*
* @since 1.0.0
*
* @param array $config Common configuration.
*/
return apply_filters( 'elementor/common/localize_settings', $config );
}
/**
* Add default templates.
*
* Register common app default templates.
* @since 2.3.0
* @access private
*/
private function add_default_templates() {
$default_templates = [
'includes/editor-templates/library-layout.php',
];
foreach ( $default_templates as $template ) {
$this->add_template( ELEMENTOR_PATH . $template );
}
}
}
frontend/render-modes/render-mode-base.php 0000644 00000004147 14720700033 0014573 0 ustar 00 post_id = intval( $post_id );
}
/**
* Returns the key name of the class.
*
* @return string
* @throws \Exception
*/
public static function get_name() {
throw new \Exception( 'You must implements `get_name` static method in ' . static::class );
}
/**
* @param $post_id
*
* @return string
* @throws \Exception
*/
public static function get_url( $post_id ) {
return Render_Mode_Manager::get_base_url( $post_id, static::get_name() );
}
/**
* Runs before the render, by default load scripts and styles.
*/
public function prepare_render() {
add_action( 'wp_enqueue_scripts', function () {
$this->enqueue_scripts();
}, static::ENQUEUE_SCRIPTS_PRIORITY );
add_action( 'wp_enqueue_scripts', function () {
$this->enqueue_styles();
}, static::ENQUEUE_STYLES_PRIORITY );
}
/**
* By default do not do anything.
*/
protected function enqueue_scripts() {
//
}
/**
* By default do not do anything.
*/
protected function enqueue_styles() {
//
}
/**
* Check if the current user has permissions for the current render mode.
*
* @return bool
*/
public function get_permissions_callback() {
return $this->get_document()->is_editable_by_current_user();
}
/**
* Checks if the current render mode is static render, By default returns false.
*
* @return bool
*/
public function is_static() {
return false;
}
/**
* @return Document
*/
public function get_document() {
if ( ! $this->document ) {
$this->document = Plugin::$instance->documents->get( $this->post_id );
}
return $this->document;
}
}
frontend/render-modes/render-mode-normal.php 0000644 00000000632 14720700033 0015144 0 ustar 00 $post_id,
self::QUERY_STRING_PARAM_NAME => $render_mode_name,
self::QUERY_STRING_NONCE_PARAM_NAME => wp_create_nonce( self::get_nonce_action( $post_id ) ),
'ver' => time(),
], get_permalink( $post_id ) );
}
/**
* @param $post_id
*
* @return string
*/
public static function get_nonce_action( $post_id ) {
return str_replace( '{post_id}', $post_id, self::NONCE_ACTION_PATTERN );
}
/**
* Register a new render mode into the render mode manager.
*
* @param $class_name
*
* @return $this
* @throws \Exception
*/
public function register_render_mode( $class_name ) {
if ( ! is_subclass_of( $class_name, Render_Mode_Base::class ) ) {
throw new \Exception( "'{$class_name}' must extend 'Render_Mode_Base'." );
}
$this->render_modes[ $class_name::get_name() ] = $class_name;
return $this;
}
/**
* Get the current render mode.
*
* @return Render_Mode_Base
*/
public function get_current() {
return $this->current;
}
/**
* @param Render_Mode_Base $render_mode
*
* @return $this
*/
private function set_current( Render_Mode_Base $render_mode ) {
$this->current = $render_mode;
return $this;
}
/**
* Set render mode.
*
* @return $this
*/
private function choose_render_mode() {
$post_id = null;
$key = null;
$nonce = null;
if ( isset( $_GET[ self::QUERY_STRING_POST_ID ] ) ) {
$post_id = $_GET[ self::QUERY_STRING_POST_ID ]; // phpcs:ignore -- Nonce will be checked next line.
}
if ( isset( $_GET[ self::QUERY_STRING_NONCE_PARAM_NAME ] ) ) {
$nonce = $_GET[ self::QUERY_STRING_NONCE_PARAM_NAME ]; // phpcs:ignore -- Nonce will be checked next line.
}
if ( isset( $_GET[ self::QUERY_STRING_PARAM_NAME ] ) ) {
$key = $_GET[ self::QUERY_STRING_PARAM_NAME ]; // phpcs:ignore -- Nonce will be checked next line.
}
if (
$post_id &&
$nonce &&
wp_verify_nonce( $nonce, self::get_nonce_action( $post_id ) ) &&
$key &&
array_key_exists( $key, $this->render_modes )
) {
$this->set_current( new $this->render_modes[ $key ]( $post_id ) );
} else {
$this->set_current( new Render_Mode_Normal( $post_id ) );
}
return $this;
}
/**
* Add actions base on the current render.
*
* @throws \Requests_Exception_HTTP_403
* @throws Status403
*/
private function add_current_actions() {
if ( ! $this->current->get_permissions_callback() ) {
// WP >= 6.2-alpha
if ( class_exists( '\WpOrg\Requests\Exception\Http\Status403' ) ) {
throw new \WpOrg\Requests\Exception\Http\Status403();
} else {
throw new \Requests_Exception_HTTP_403();
}
}
// Run when 'template-redirect' actually because the the class is instantiate when 'template-redirect' run.
$this->current->prepare_render();
}
/**
* Render_Mode_Manager constructor.
*
* @throws \Exception
*/
public function __construct() {
$this->register_render_mode( Render_Mode_Normal::class );
do_action( 'elementor/frontend/render_mode/register', $this );
$this->choose_render_mode();
$this->add_current_actions();
}
}
frontend/performance.php 0000644 00000001435 14720700033 0011374 0 ustar 00 preview->is_preview_mode()
);
}
return static::$is_frontend;
}
public static function is_optimized_control_loading_feature_enabled(): bool {
return Plugin::$instance->experiments->is_feature_active( 'e_optimized_control_loading' );
}
}
isolation/wordpress-adapter.php 0000644 00000002271 14720700033 0012722 0 ustar 00 wordpress_adapter = $wordpress_adapter;
}
public function is_plugin_installed( $plugin_path ): bool {
$installed_plugins = $this->wordpress_adapter->get_plugins();
return isset( $installed_plugins[ $plugin_path ] );
}
public function get_install_plugin_url( $plugin_path ): string {
$slug = dirname( $plugin_path );
$admin_base_url = $this->wordpress_adapter->self_admin_url( 'update.php' );
$admin_url = add_query_arg( [
'action' => 'install-plugin',
'plugin' => $slug,
], $admin_base_url );
return $this->wordpress_adapter->wp_nonce_url( $admin_url, 'install-plugin_' . $slug );
}
public function get_activate_plugin_url( $plugin_path ): string {
$admin_base_url = $this->wordpress_adapter->self_admin_url( 'plugins.php' );
$admin_url = add_query_arg( [
'action' => 'activate',
'plugin' => $plugin_path,
'plugin_status' => 'all',
'paged' => 1,
's' => '',
], $admin_base_url );
return $this->wordpress_adapter->wp_nonce_url( $admin_url, 'activate-plugin_' . $plugin_path );
}
}
isolation/plugin-status-adapter-interface.php 0000644 00000000433 14720700033 0015445 0 ustar 00 get_role_manager_advanced_options();
return static::$advanced_options;
}
/**
* @since 2.0.0
* @access protected
*/
protected function get_page_title() {
return esc_html__( 'Role Manager', 'elementor' );
}
/**
* @since 2.0.0
* @access public
*/
public function register_admin_menu( Admin_Menu_Manager $admin_menu ) {
$admin_menu->register( static::PAGE_ID, new Role_Manager_Menu_Item( $this ) );
}
/**
* @since 2.0.0
* @access protected
*/
protected function create_tabs() {
$validation_class = 'Elementor\Settings_Validations';
return [
'general' => [
'label' => esc_html__( 'General', 'elementor' ),
'sections' => [
'tools' => [
'fields' => [
'exclude_user_roles' => [
'label' => esc_html__( 'Exclude Roles', 'elementor' ),
'field_args' => [
'type' => 'checkbox_list_roles',
'exclude' => [ 'super_admin', 'administrator' ],
],
'setting_args' => [
'sanitize_callback' => [ $validation_class, 'checkbox_list' ],
],
],
self::ROLE_MANAGER_ADVANCED => [
'field_args' => [
'type' => 'raw_html',
'html' => '',
],
'setting_args' => [
'sanitize_callback' => [ $this, 'save_advanced_options' ],
],
],
],
],
],
],
];
}
public function save_advanced_options( $input ) {
return $input;
}
/**
* @since 2.0.0
* @access public
*/
public function display_settings_page() {
$this->get_tabs();
?>
get_role_manager_options();
}
?>
get_user_advanced_options();
$checked = isset( $advanced_options[ $role_slug ] ) ? $advanced_options[ $role_slug ] : [];
?>
get_user_advanced_options();
$checked = isset( $advanced_options[ $role_slug ] ) ? $advanced_options[ $role_slug ] : [];
?>
get_go_pro_link_content();
?>
esc_html__( 'Want to give access only to content?', 'elementor' ),
'upgrade_url' => esc_url( $upgrade_url ),
'upgrade_text' => esc_html__( 'Upgrade', 'elementor' ),
];
return Filtered_Promotions_Manager::get_filtered_promotion_data( $promotion, 'elementor/role/custom_promotion', 'upgrade_url' );
}
/**
* @since 2.0.0
* @access public
*/
public function get_user_restrictions_array() {
$user = wp_get_current_user();
$user_roles = $user->roles;
$options = $this->get_user_restrictions();
$restrictions = [];
if ( empty( $options ) ) {
return $restrictions;
}
foreach ( $user_roles as $role ) {
if ( ! isset( $options[ $role ] ) ) {
continue;
}
$restrictions = array_merge( $restrictions, $options[ $role ] );
}
return array_unique( $restrictions );
}
/**
* @since 2.0.0
* @access private
*/
private function get_user_restrictions() {
static $restrictions = false;
if ( ! $restrictions ) {
$restrictions = [];
/**
* Editor user restrictions.
*
* Filters the user restrictions in the editor.
*
* @since 2.0.0
*
* @param array $restrictions User restrictions.
*/
$restrictions = apply_filters( 'elementor/editor/user/restrictions', $restrictions );
}
return $restrictions;
}
/**
* @since 2.0.0
* @access public
*
* @param $capability
*
* @return bool
*/
public function user_can( $capability ) {
$options = $this->get_user_restrictions_array();
if ( in_array( $capability, $options, true ) ) {
return false;
}
return true;
}
/**
* @since 2.0.0
* @access public
*/
public function __construct() {
parent::__construct();
add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu ) {
$this->register_admin_menu( $admin_menu );
}, Settings::ADMIN_MENU_PRIORITY + 10 );
add_action( 'elementor/role/restrictions/controls', [ $this, 'add_json_enable_control' ] );
add_action( 'elementor/role/restrictions/controls', [ $this, 'add_custom_html_enable_control' ] );
add_action( 'elementor/role/restrictions/controls', [ $this, 'get_go_pro_link_html' ] );
add_filter( 'elementor/editor/user/restrictions', [ $this, 'get_role_manager_advanced_options' ] );
}
}
role-manager/role-manager-menu-item.php 0000644 00000001527 14720700033 0014076 0 ustar 00 role_manager = $role_manager;
}
public function is_visible() {
return true;
}
public function get_parent_slug() {
return Settings::PAGE_ID;
}
public function get_label() {
return esc_html__( 'Role Manager', 'elementor' );
}
public function get_page_title() {
return esc_html__( 'Role Manager', 'elementor' );
}
public function get_capability() {
return 'manage_options';
}
public function render() {
$this->role_manager->display_settings_page();
}
}
logger/loggers/base.php 0000644 00000004520 14720700033 0011105 0 ustar 00 create_item( $item, $type, $args );
}
$this->save_log( $item );
}
public function info( $message, $args = [] ) {
$this->log( $message, self::LEVEL_INFO, $args );
}
public function notice( $message, $args = [] ) {
$this->log( $message, self::LEVEL_NOTICE, $args );
}
public function warning( $message, $args = [] ) {
$this->log( $message, self::LEVEL_WARNING, $args );
}
public function error( $message, $args = [] ) {
$this->log( $message, self::LEVEL_ERROR, $args );
}
/**
* @param string $message
* @param string $type
* @param array $args
*
* @return Log_Item_Interface
*/
private function create_item( $message, $type, $args = [] ) {
$args['message'] = $message;
$args['type'] = $type;
$item = new Log_Item( $args );
return $item;
}
public function get_formatted_log_entries( $max_entries, $table = true ) {
$entries = $this->get_log();
if ( empty( $entries ) ) {
return [
'All' => [
'total_count' => 0,
'count' => 0,
'entries' => '',
],
];
}
$sorted_entries = [];
$open_tag = $table ? '' : '';
$close_tab = $table ? ' |
' : PHP_EOL;
$format = $table ? 'html' : 'raw';
foreach ( $entries as $entry ) {
/** @var Log_Item $entry */
$sorted_entries[ $entry->get_name() ][] = $open_tag . $entry->format( $format ) . $close_tab;
}
$formatted_entries = [];
foreach ( $sorted_entries as $key => $sorted_entry ) {
$formatted_entries[ $key ]['total_count'] = count( $sorted_entry );
$formatted_entries[ $key ]['count'] = count( $sorted_entry );
$sorted_entry = array_slice( $sorted_entry, -$max_entries );
$formatted_entries[ $key ]['count'] = count( $sorted_entry );
$formatted_entries[ $key ]['entries'] = implode( $sorted_entry );
}
return $formatted_entries;
}
}
logger/loggers/db.php 0000644 00000002006 14720700033 0010555 0 ustar 00 maybe_truncate_log();
$id = $item->get_fingerprint();
if ( empty( $log[ $id ] ) ) {
$log[ $id ] = $item;
}
$log[ $id ]->increase_times( $item );
update_option( self::LOG_NAME, $log, 'no' );
}
public function clear() {
delete_option( self::LOG_NAME );
}
private function maybe_truncate_log() {
/** @var Log_Item[] $log */
$log = $this->get_log();
if ( Log_Item::MAX_LOG_ENTRIES < count( $log ) ) {
$log = array_slice( $log, -Log_Item::MAX_LOG_ENTRIES );
}
return $log;
}
public function get_log() {
// Clear cache.
wp_cache_delete( self::LOG_NAME, 'options' );
$log = get_option( self::LOG_NAME, [] );
// In case the DB log is corrupted.
if ( ! is_array( $log ) ) {
$log = [];
}
return $log;
}
}
logger/loggers/logger-interface.php 0000644 00000002364 14720700033 0013414 0 ustar 00 in format
*
* @return array [ 'key' => [ 'total_count' => int, 'count' => int, 'entries' => Log_Item[] ] ]
*/
public function get_formatted_log_entries( $max_entries, $table = true );
}
logger/log-reporter.php 0000644 00000006005 14720700033 0011152 0 ustar 00 '',
];
}
public function print_html_label( $log_label ) {
$title = $this->get_title();
if ( empty( $_GET[ self::CLEAR_LOG_ACTION ] ) ) { // phpcs:ignore -- nonce validation is not require here.
$nonce = wp_create_nonce( self::CLEAR_LOG_ACTION );
$url = add_query_arg( [
self::CLEAR_LOG_ACTION => 1,
'_wpnonce' => $nonce,
] );
$title .= '' . esc_html__( 'Clear Log', 'elementor' ) . '';
$title .= '';
}
parent::print_html_label( $title );
}
public function get_log_entries() {
/** @var \Elementor\Core\Logger\Manager $manager */
$manager = Manager::instance();
/** @var \Elementor\Core\Logger\Loggers\Db $logger */
$logger = $manager->get_logger( 'db' );
if ( ! empty( $_GET[ self::CLEAR_LOG_ACTION ] ) ) {
$nonce = Utils::get_super_global_value( $_GET, '_wpnonce' );
if ( ! wp_verify_nonce( $nonce, self::CLEAR_LOG_ACTION ) ) {
wp_die( 'Invalid Nonce', 'Invalid Nonce', [
'back_link' => true,
] );
}
$logger->clear();
}
$log_string = 'No entries to display';
$log_entries = $logger->get_formatted_log_entries( self::MAX_ENTRIES, false );
if ( ! empty( $log_entries ) ) {
$entries_string = '';
foreach ( $log_entries as $key => $log_entry ) {
if ( $log_entry['count'] ) {
$entries_string .= '' . sprintf( '%s: showing %s of %s', $key, $log_entry['count'], $log_entry['total_count'] ) . '
';
$entries_string .= '' . $log_entry['entries'] . '
';
}
}
if ( ! empty( $entries_string ) ) {
$log_string = $entries_string;
}
}
return [
'value' => $log_string,
];
}
public function get_raw_log_entries() {
$log_string = 'No entries to display';
/** @var \Elementor\Core\Logger\Manager $manager */
$manager = Manager::instance();
$logger = $manager->get_logger();
$log_entries = $logger->get_formatted_log_entries( self::MAX_ENTRIES, false );
if ( ! empty( $log_entries ) ) {
$entries_string = PHP_EOL;
foreach ( $log_entries as $key => $log_entry ) {
if ( $log_entry['count'] ) {
$entries_string .= sprintf( '%s: showing %s of %s', $key, $log_entry['count'], $log_entry['total_count'] ) . $log_entry['entries'] . PHP_EOL;
}
}
if ( ! empty( $entries_string ) ) {
$log_string = $entries_string;
}
}
return [
'value' => $log_string,
];
}
}
logger/manager.php 0000644 00000015661 14720700033 0010153 0 ustar 00 get_log_type_from_php_error( $last_error['type'] );
$last_error['trace'] = true;
$item = new PHP( $last_error );
$this->get_logger()->log( $item );
if ( $should_exit ) {
exit;
}
}
public function rest_error_handler( $error_number, $error_message, $error_file, $error_line ) {
// Temporary solution until all PHP notices will be fixed in the core and pro.
if ( Utils::is_wp_cli() ) {
return null;
}
$error = new \WP_Error( $error_number, $error_message, [
'type' => $error_number,
'message' => $error_message,
'file' => $error_file,
'line' => $error_line,
] );
if ( ! Utils::is_elementor_path( $error_file ) ) {
// Do execute PHP internal error handler.
return false;
}
$is_an_error = in_array( // It can be notice or warning
$error_number,
[ E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR ],
true
);
$error_data = $error->get_error_data();
// TODO: This part should be modular, temporary hard-coded.
// Notify $e.data.
if ( $is_an_error && ! headers_sent() ) {
header( 'Content-Type: application/json; charset=UTF-8' );
http_response_code( 500 );
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
echo wp_json_encode( $error_data );
} else {
echo wp_json_encode( [
'message' => 'Server error, see Elementor => System Info',
] );
}
}
$this->shutdown( $error_data, $is_an_error );
}
public function register_error_handler() {
set_error_handler( [ $this, 'rest_error_handler' ], E_ALL );
}
public function add_system_info_report() {
System_Info::add_report(
'log', [
'file_name' => __DIR__ . '/log-reporter.php',
'class_name' => __NAMESPACE__ . '\Log_Reporter',
]
);
}
/**
* Javascript log.
*
* Log Elementor errors and save them in the database.
*
* Fired by `wp_ajax_elementor_js_log` action.
*
*/
public function js_log() {
/** @var Module $ajax */
$ajax = Plugin::$instance->common->get_component( 'ajax' );
// PHPCS ignore is added throughout this method because nonce verification happens in the $ajax->verify_request_nonce() method.
if ( ! $ajax->verify_request_nonce() || empty( $_POST['data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
wp_send_json_error();
}
if ( ! current_user_can( Editor::EDITING_CAPABILITY ) ) {
wp_send_json_error( 'Permission denied' );
}
// PHPCS - See comment above.
$data = Utils::get_super_global_value( $_POST, 'data' ) ?? []; // phpcs:ignore WordPress.Security.NonceVerification.Missing
array_walk_recursive( $data, function( &$value ) {
$value = sanitize_text_field( $value );
} );
// PHPCS - See comment above.
foreach ( $data as $error ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
$error['type'] = Logger_Interface::LEVEL_ERROR;
if ( ! empty( $error['customFields'] ) ) {
$error['meta'] = $error['customFields'];
}
$item = new JS( $error );
$this->get_logger()->log( $item );
}
wp_send_json_success();
}
public function register_logger( $name, $class ) {
$this->loggers[ $name ] = $class;
}
public function set_default_logger( $name ) {
if ( ! empty( $this->loggers[ $name ] ) ) {
$this->default_logger = $name;
}
}
public function register_default_loggers() {
$this->register_logger( 'db', 'Elementor\Core\Logger\Loggers\Db' );
$this->set_default_logger( 'db' );
}
/**
* @param string $name
*
* @return Logger_Interface
*/
public function get_logger( $name = '' ) {
$this->register_loggers();
if ( empty( $name ) || ! isset( $this->loggers[ $name ] ) ) {
$name = $this->default_logger;
}
if ( ! $this->get_component( $name ) ) {
$this->add_component( $name, new $this->loggers[ $name ]() );
}
return $this->get_component( $name );
}
/**
* @param string $message
* @param array $args
*
* @return void
*/
public function log( $message, $args = [] ) {
$this->get_logger()->log( $message, $args );
}
/**
* @param string $message
* @param array $args
*
* @return void
*/
public function info( $message, $args = [] ) {
$this->get_logger()->info( $message, $args );
}
/**
* @param string $message
* @param array $args
*
* @return void
*/
public function notice( $message, $args = [] ) {
$this->get_logger()->notice( $message, $args );
}
/**
* @param string $message
* @param array $args
*
* @return void
*/
public function warning( $message, $args = [] ) {
$this->get_logger()->warning( $message, $args );
}
/**
* @param string $message
* @param array $args
*
* @return void
*/
public function error( $message, $args = [] ) {
$this->get_logger()->error( $message, $args );
}
private function get_log_type_from_php_error( $type ) {
$error_map = [
E_CORE_ERROR => Logger_Interface::LEVEL_ERROR,
E_ERROR => Logger_Interface::LEVEL_ERROR,
E_USER_ERROR => Logger_Interface::LEVEL_ERROR,
E_COMPILE_ERROR => Logger_Interface::LEVEL_ERROR,
E_RECOVERABLE_ERROR => Logger_Interface::LEVEL_ERROR,
E_PARSE => Logger_Interface::LEVEL_ERROR,
E_STRICT => Logger_Interface::LEVEL_ERROR,
E_WARNING => Logger_Interface::LEVEL_WARNING,
E_USER_WARNING => Logger_Interface::LEVEL_WARNING,
E_CORE_WARNING => Logger_Interface::LEVEL_WARNING,
E_COMPILE_WARNING => Logger_Interface::LEVEL_WARNING,
E_NOTICE => Logger_Interface::LEVEL_NOTICE,
E_USER_NOTICE => Logger_Interface::LEVEL_NOTICE,
E_DEPRECATED => Logger_Interface::LEVEL_NOTICE,
E_USER_DEPRECATED => Logger_Interface::LEVEL_NOTICE,
];
return isset( $error_map[ $type ] ) ? $error_map[ $type ] : Logger_Interface::LEVEL_ERROR;
}
private function register_loggers() {
if ( ! did_action( 'elementor/loggers/register' ) ) {
do_action( 'elementor/loggers/register', $this );
}
}
public function __construct() {
register_shutdown_function( [ $this, 'shutdown' ] );
add_action( 'admin_init', [ $this, 'add_system_info_report' ], 80 );
add_action( 'wp_ajax_elementor_js_log', [ $this, 'js_log' ] );
add_action( 'elementor/loggers/register', [ $this, 'register_default_loggers' ] );
}
}
logger/items/log-item-interface.php 0000644 00000002307 14720700033 0013326 0 ustar 00 file = empty( $args['file'] ) ? '' : $args['file'];
$this->line = empty( $args['line'] ) ? '' : $args['line'];
}
#[\ReturnTypeWillChange]
public function jsonSerialize() {
$json_arr = parent::jsonSerialize();
$json_arr['file'] = $this->file;
$json_arr['line'] = $this->line;
return $json_arr;
}
public function deserialize( $properties ) {
parent::deserialize( $properties );
$this->file = ! empty( $properties['file'] ) && is_string( $properties['file'] ) ? $properties['file'] : '';
$this->line = ! empty( $properties['line'] ) && is_string( $properties['line'] ) ? $properties['line'] : '';
}
public function get_name() {
return 'File';
}
}
logger/items/base.php 0000644 00000012275 14720700033 0010572 0 ustar 00 date = current_time( 'mysql' );
$this->message = ! empty( $args['message'] ) ? esc_html( $args['message'] ) : '';
$this->type = ! empty( $args['type'] ) ? $args['type'] : 'info';
$this->meta = ! empty( $args['meta'] ) ? $args['meta'] : [];
$this->args = $args;
$this->set_trace();
}
public function __get( $name ) {
if ( property_exists( $this, $name ) ) {
return $this->{$name};
}
return '';
}
public function __toString() {
$vars = get_object_vars( $this );
return strtr( static::FORMAT, $vars );
}
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return [
'class' => get_class( $this ),
'item' => [
'date' => $this->date,
'message' => $this->message,
'type' => $this->type,
'meta' => $this->meta,
'times' => $this->times,
'times_dates' => $this->times_dates,
'args' => $this->args,
],
];
}
public function deserialize( $properties ) {
$this->date = ! empty( $properties['date'] ) && is_string( $properties['date'] ) ? $properties['date'] : '';
$this->message = ! empty( $properties['message'] ) && is_string( $properties['message'] ) ? $properties['message'] : '';
$this->type = ! empty( $properties['type'] ) && is_string( $properties['type'] ) ? $properties['type'] : '';
$this->meta = ! empty( $properties['meta'] ) && is_array( $properties['meta'] ) ? $properties['meta'] : [];
$this->times = ! empty( $properties['times'] ) && is_string( $properties['times'] ) ? $properties['times'] : '';
$this->times_dates = ! empty( $properties['times_dates'] ) && is_array( $properties['times_dates'] ) ? $properties['times_dates'] : [];
$this->args = ! empty( $properties['args'] ) && is_array( $properties['args'] ) ? $properties['args'] : [];
}
/**
* @return Log_Item_Interface | null
*/
public static function from_json( $str ) {
$obj = json_decode( $str, true );
if ( ! array_key_exists( 'class', $obj ) ) {
return null;
}
$class = $obj['class'];
if ( class_exists( $class ) ) {
/** @var Base $item */
$item = new $class( $obj['item']['message'] );
$item->deserialize( $obj['item'] );
return $item;
}
return null;
}
public function to_formatted_string( $output_format = 'html' ) {
$vars = get_object_vars( $this );
$format = static::FORMAT;
if ( 'html' === $output_format ) {
$format = str_replace( 'message', 'message', static::FORMAT );
}
if ( empty( $vars['meta'] ) ) {
$format = str_replace( '[meta]', '', $format );
} else {
$vars['meta'] = stripslashes( var_export( $vars['meta'], true ) ); // @codingStandardsIgnoreLine
}
return strtr( $format, $vars );
}
public function get_fingerprint() {
$unique_key = $this->type . $this->message . var_export( $this->meta, true ); // @codingStandardsIgnoreLine
//info messages are not be aggregated:
if ( 'info' === $this->type ) {
$unique_key .= $this->date;
}
return md5( $unique_key );
}
public function increase_times( $item, $truncate = true ) {
$this->times++;
$this->times_dates[] = $item->date;
if ( $truncate && ( self::MAX_LOG_ENTRIES < count( $this->times_dates ) ) ) {
$this->times_dates = array_slice( $this->times_dates, -self::MAX_LOG_ENTRIES );
}
}
public function format( $format = 'html' ) {
$trace = $this->format_trace();
if ( empty( $trace ) ) {
return $this->to_formatted_string( $format );
}
$copy = clone $this;
$copy->meta['trace'] = $trace;
return $copy->to_formatted_string( $format );
}
public function get_name() {
return 'Log';
}
private function format_trace() {
$trace = empty( $this->meta['trace'] ) ? '' : $this->meta['trace'];
if ( is_string( $trace ) ) {
return $trace;
}
$trace_str = '';
foreach ( $trace as $key => $trace_line ) {
$format = static::TRACE_FORMAT;
$trace_line['key'] = $key;
if ( empty( $trace_line['file'] ) ) {
$format = str_replace( 'file(line): ', '', $format );
}
$trace_str .= PHP_EOL . strtr( $format, $trace_line );
$trace_str .= empty( $trace_line['args'] ) ? '' : var_export( $trace_line['args'], true ); // @codingStandardsIgnoreLine
}
return $trace_str . PHP_EOL;
}
private function set_trace() {
if ( ! empty( $this->args['trace'] ) && true === $this->args['trace'] ) {
$limit = empty( $this->args['trace_limit'] ) ? static::TRACE_LIMIT : $this->args['trace_limit'];
$stack = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // @codingStandardsIgnoreLine
while ( ! empty( $stack ) && ! empty( $stack[0]['file'] ) && ( false !== strpos( $stack[0]['file'], 'core' . DIRECTORY_SEPARATOR . 'logger' ) ) ) {
array_shift( $stack );
}
$this->meta['trace'] = array_slice( $stack, 0, $limit );
return;
}
if ( is_array( $this->args ) ) {
unset( $this->args['trace'] );
}
}
}
logger/items/js.php 0000644 00000001536 14720700033 0010272 0 ustar 00 column = $args['column'];
$this->file = $args['url'];
$this->date = gmdate( 'Y-m-d H:i:s', $args['timestamp'] );
}
#[\ReturnTypeWillChange]
public function jsonSerialize() {
$json_arr = parent::jsonSerialize();
$json_arr['column'] = $this->column;
return $json_arr;
}
public function deserialize( $properties ) {
parent::deserialize( $properties );
$this->column = ! empty( $properties['column'] ) && is_string( $properties['column'] ) ? $properties['column'] : '';
}
public function get_name() {
return 'JS';
}
}
logger/items/php.php 0000644 00000000412 14720700033 0010435 0 ustar 00 $this->init_styles(),
'scripts' => [],
];
$this->assets = $assets;
}
private function init_styles(): array {
$styles = [
// TODO: Remove the 'e-animations' registration in v3.26.0 [ED-15471].
'e-animations' => [
'src' => $this->get_css_assets_url( 'animations', 'assets/lib/animations/', true ),
'version' => ELEMENTOR_VERSION,
'dependencies' => [],
],
'e-shapes' => [
'src' => $this->get_css_assets_url( 'shapes', 'assets/css/conditionals/' ),
'version' => ELEMENTOR_VERSION,
'dependencies' => [],
],
'e-swiper' => [
'src' => $this->get_css_assets_url( 'e-swiper', 'assets/css/conditionals/' ),
'version' => ELEMENTOR_VERSION,
'dependencies' => [ 'swiper' ],
],
'swiper' => [
'src' => $this->get_css_assets_url( 'swiper', $this->getSwiperPath() ),
'version' => $this->getSwiperVersion(),
'dependencies' => [],
],
];
return array_merge( $styles, $this->get_animation_styles() );
}
private function getSwiperPath(): string {
return Plugin::$instance->experiments->is_feature_active( 'e_swiper_latest' ) ? 'assets/lib/swiper/v8/css/' : 'assets/lib/swiper/css/';
}
private function getSwiperVersion(): string {
return Plugin::$instance->experiments->is_feature_active( 'e_swiper_latest' ) ? '8.4.5' : '5.3.6';
}
private function get_animations(): array {
$grouped_animations = Control_Animation::get_animations();
$grouped_animations['hover'] = Control_Hover_Animation::get_animations();
$exit_animations = Control_Exit_Animation::get_animations();
$grouped_animations = array_merge( $grouped_animations, $exit_animations );
$animations = [];
foreach ( $grouped_animations as $group_label => $group ) {
foreach ( $group as $animation_key => $animation_label ) {
$animations[ $animation_key ] = $group_label;
}
}
return $animations;
}
private function get_animation_styles(): array {
$animations = $this->get_animations();
$styles = [];
foreach ( $animations as $animation => $group_label ) {
$style_prefix = 'hover' === $group_label ? 'e-animation-' : '';
$styles[ 'e-animation-' . $animation ] = [
'src' => $this->get_css_assets_url( $style_prefix . $animation, 'assets/lib/animations/styles/' ),
'version' => ELEMENTOR_VERSION,
'dependencies' => [],
];
}
return $styles;
}
public function get_assets(): array {
if ( ! $this->assets ) {
$this->init_assets();
}
return $this->assets;
}
/**
* @param array $assets {
* @type array 'styles'
* @type array 'scripts'
* }
*/
public function enable_assets( array $assets_data ): void {
if ( ! $this->assets ) {
$this->init_assets();
}
foreach ( $assets_data as $assets_type => $assets_list ) {
foreach ( $assets_list as $asset_name ) {
$this->assets[ $assets_type ][ $asset_name ]['enabled'] = true;
if ( 'scripts' === $assets_type ) {
wp_enqueue_script( $asset_name );
} else {
wp_enqueue_style( $asset_name );
}
}
}
}
/**
* @param array $assets {
* @type array 'styles'
* @type array 'scripts'
* }
*/
public function add_assets( array $assets ): void {
if ( ! $this->assets ) {
$this->init_assets();
}
$this->assets = array_replace_recursive( $this->assets, $assets );
}
/**
* @deprecated 3.22.0
*/
public function enqueue_assets(): void {
$assets = $this->get_assets();
$is_preview_mode = Plugin::$instance->preview->is_preview_mode();
foreach ( $assets as $assets_type => $assets_type_data ) {
foreach ( $assets_type_data as $asset_name => $asset_data ) {
if ( empty( $asset_data['src'] ) ) {
continue;
}
if ( ! empty( $asset_data['enabled'] ) || $is_preview_mode ) {
if ( 'scripts' === $assets_type ) {
wp_enqueue_script( $asset_name, $asset_data['src'], $asset_data['dependencies'], $asset_data['version'], true );
} else {
// TODO: Remove the 'e-animations' registration in v3.26.0 [ED-15471].
if ( $this->skip_animations_style( $asset_name ) ) {
continue;
}
wp_enqueue_style( $asset_name, $asset_data['src'], $asset_data['dependencies'], $asset_data['version'] );
}
}
}
}
}
// TODO: Remove the 'e-animations' registration in v3.26.0 [ED-15471].
private function skip_animations_style( $asset_name ): bool {
$is_preview = Plugin::$instance->preview->is_preview_mode();
return $is_preview && 'e-animations' === $asset_name;
}
private function register_assets(): void {
$assets = $this->get_assets();
foreach ( $assets as $assets_type => $assets_type_data ) {
foreach ( $assets_type_data as $asset_name => $asset_data ) {
if ( 'scripts' === $assets_type ) {
wp_register_script( $asset_name, $asset_data['src'], $asset_data['dependencies'], $asset_data['version'], true );
} else {
wp_register_style( $asset_name, $asset_data['src'], $asset_data['dependencies'], $asset_data['version'] );
}
}
}
}
public function __construct() {
parent::__construct();
$this->register_assets();
}
}
page-assets/data-managers/widgets-css.php 0000644 00000001150 14720700033 0014422 0 ustar 00 get_file_data( 'exists' );
$widget_css = '';
if ( $asset_css_file_exists ) {
$asset_url = $this->get_config_data( 'file_url' );
$widget_css = sprintf( '', $asset_url );
}
return $widget_css;
}
}
page-assets/data-managers/base.php 0000644 00000016043 14720700033 0013107 0 ustar 00 assets_config['key'];
}
/**
* Get Relative Version.
*
* The asset data will be re-evaluated according the version number.
*
* @since 3.3.0
* @access protected
*
* @return string
*/
protected function get_version() {
return $this->assets_config['version'];
}
/**
* Get Asset Path.
*
* The asset data will be extracted from the file path.
*
* @since 3.3.0
* @access protected
*
* @return string
*/
protected function get_file_path() {
return $this->assets_config['file_path'];
}
/**
* Get Config Data.
*
* Holds a unique data relevant for the specific assets category type.
*
* @since 3.3.0
* @access protected
*
* @return string|array
*/
protected function get_config_data( $key = '' ) {
if ( isset( $this->assets_config['data'] ) ) {
if ( $key ) {
if ( isset( $this->assets_config['data'][ $key ] ) ) {
return $this->assets_config['data'][ $key ];
}
return '';
}
return $this->assets_config['data'];
}
return [];
}
/**
* Set Asset Data.
*
* Responsible for setting the current asset data.
*
* @since 3.3.0
* @access protected
*
* @return void
*/
protected function set_asset_data( $asset_key ) {
if ( ! isset( $this->assets_data[ $asset_key ] ) ) {
$this->assets_data[ $asset_key ] = [];
}
$this->assets_data[ $asset_key ]['content'] = $this->get_asset_content();
$this->assets_data[ $asset_key ]['version'] = $this->get_version();
$this->save_asset_data( $asset_key );
}
/**
* Save Asset Data.
*
* Responsible for saving the asset data in the DB.
*
* @since 3.3.0
* @access protected
*
* @param string $asset_key
*
* @return void
*/
protected function save_asset_data( $asset_key ) {
$assets_data = $this->get_saved_assets_data();
$content_type = $this->content_type;
$assets_category = $this->assets_category;
$assets_data[ $content_type ][ $assets_category ][ $asset_key ] = $this->assets_data[ $asset_key ];
update_option( self::ASSETS_DATA_KEY, $assets_data );
}
/**
* Is Asset Version Changed.
*
* Responsible for comparing the saved asset data version to the current relative version.
*
* @since 3.3.0
* @access protected
*
* @param string $asset_key
*
* @return boolean
*/
protected function is_asset_version_changed( $version ) {
return $this->get_version() !== $version;
}
/**
* Get File Data.
*
* Getting a file content or size.
*
* @since 3.3.0
* @access protected
*
* @param string $data_type (exists|content|size)
* @param string $file_key - In case that the same file data is needed for multiple assets (like a JSON file), the file data key should be the same for all shared assets to make sure that the file is being read only once.
*
* @return string|number
*/
protected function get_file_data( $data_type, $file_key = '' ) {
$asset_key = $file_key ? $file_key : $this->get_key();
if ( isset( $this->files_data[ $asset_key ][ $data_type ] ) ) {
return $this->files_data[ $asset_key ][ $data_type ];
}
if ( ! isset( $this->files_data[ $asset_key ] ) ) {
$this->files_data[ $asset_key ] = [];
}
$asset_path = $this->get_file_path();
if ( 'exists' === $data_type ) {
$data = file_exists( $asset_path );
} elseif ( 'content' === $data_type ) {
$data = Utils::file_get_contents( $asset_path );
if ( ! $data ) {
$data = '';
}
} elseif ( 'size' === $data_type ) {
$data = file_exists( $asset_path ) ? filesize( $asset_path ) : 0;
}
$this->files_data[ $asset_key ][ $data_type ] = $data;
return $data;
}
/**
* Get Saved Assets Data.
*
* Getting the assets data from the DB.
*
* @since 3.3.0
* @access protected
*
* @return array
*/
protected function get_saved_assets_data() {
$assets_data = get_option( self::ASSETS_DATA_KEY, [] );
$content_type = $this->content_type;
$assets_category = $this->assets_category;
if ( ! isset( $assets_data[ $content_type ] ) ) {
$assets_data[ $content_type ] = [];
}
if ( ! isset( $assets_data[ $content_type ][ $assets_category ] ) ) {
$assets_data[ $content_type ][ $assets_category ] = [];
}
return $assets_data;
}
/**
* Get Config.
*
* Getting the assets data config.
*
* @since 3.5.0
* @access protected
*
* @return array
*/
protected function get_config( $data ) {
return [];
}
/**
* Init Asset Data.
*
* Initialize the asset data and handles the asset content updates when needed.
*
* @since 3.3.0
* @access public
*
* @param array $config {
* @type string 'key'
* @type string 'version'
* @type string 'file_path'
* @type array 'data'
* }
*
* @return void
*/
public function init_asset_data( $config ) {
$this->assets_config = $config;
$asset_key = $config['key'];
$asset_data = isset( $this->assets_data[ $asset_key ] ) ? $this->assets_data[ $asset_key ] : [];
if ( ! $asset_data || $this->is_asset_version_changed( $asset_data['version'] ) ) {
$this->set_asset_data( $asset_key );
}
}
/**
* Get Asset Data From Config.
*
* Getting the asset data content from config.
*
* @since 3.3.0
* @access public
*
* @param array $config {
* @type string 'key'
* @type string 'version'
* @type string 'file_path'
* @type array 'data'
* }
*
* @return mixed
*/
public function get_asset_data_from_config( array $config ) {
$this->init_asset_data( $config );
$asset_key = $config['key'];
return $this->assets_data[ $asset_key ]['content'];
}
/**
* Get Asset Data.
*
* Getting the asset data content.
*
* @since 3.5.0
* @access public
*
* @param array $data
*
* @return mixed
*/
public function get_asset_data( array $data ) {
$config = $this->get_config( $data );
return $this->get_asset_data_from_config( $config );
}
public function __construct() {
$assets_data = $this->get_saved_assets_data();
$content_type = $this->content_type;
$assets_category = $this->assets_category;
$this->assets_data = $assets_data[ $content_type ][ $assets_category ];
}
}
page-assets/data-managers/font-icon-svg/base.php 0000644 00000000673 14720700033 0015602 0 ustar 00 $icon_key,
'version' => self::LIBRARY_CURRENT_VERSION,
'file_path' => ELEMENTOR_ASSETS_PATH . 'lib/font-awesome/json/' . $icon_file_name . '.json',
'data' => [
'icon_data' => [
'name' => $icon_name,
'library' => $icon['library'],
],
],
];
}
protected function get_asset_content() {
$icon_data = $this->get_config_data( 'icon_data' );
$file_data = json_decode( $this->get_file_data( 'content', $icon_data['library'] ), true );
$icon_name = $icon_data['name'];
$svg_data = $file_data['icons'][ $icon_name ];
return [
'width' => $svg_data[0],
'height' => $svg_data[1],
'path' => $svg_data[4],
'key' => $this->get_key(),
];
}
}
page-assets/data-managers/font-icon-svg/manager.php 0000644 00000002064 14720700033 0016276 0 ustar 00 [
'regex' => '/^fa-/',
'manager' => new Font_Awesome(),
],
'eicons' => [
'regex' => '/^eicons$/',
'manager' => new E_Icons(),
],
];
}
return self::$data;
}
public static function get_font_icon_svg_data( $icon ) {
$data = self::get_data();
$font_family = $icon['font_family'];
$font_family_manager = $data[ $font_family ]['manager'];
return $font_family_manager->get_asset_data( $icon );
}
public static function get_font_family( $icon_library ) {
foreach ( self::get_data() as $family => $data ) {
if ( preg_match( $data['regex'], $icon_library ) ) {
return $family;
}
}
return '';
}
}
page-assets/data-managers/font-icon-svg/e-icons.php 0000644 00000002011 14720700033 0016211 0 ustar 00 $icon['value'],
'version' => self::LIBRARY_CURRENT_VERSION,
'file_path' => ELEMENTOR_ASSETS_PATH . 'lib/eicons/eicons.json',
'data' => [
'icon_data' => [
'name' => $icon['value'],
'library' => $icon['library'],
],
],
];
}
protected function get_asset_content() {
$icon_data = $this->get_config_data( 'icon_data' );
$file_data = json_decode( $this->get_file_data( 'content', $icon_data['library'] ), true );
$icon_name = str_replace( 'eicon-', '', $icon_data['name'] );
$svg_data = $file_data[ $icon_name ];
return [
'width' => $svg_data['width'],
'height' => $svg_data['height'],
'path' => $svg_data['path'],
'key' => $this->get_key(),
];
}
}
page-assets/data-managers/responsive-widgets.php 0000644 00000001153 14720700033 0016032 0 ustar 00 get_file_data( 'content' );
if ( $data ) {
$data = json_decode( $data, true );
}
return $data;
}
}
files/css/post-preview.php 0000644 00000002375 14720700033 0011616 0 ustar 00 post_id_for_data = $post_id;
$parent_id = wp_get_post_parent_id( $post_id );
parent::__construct( $parent_id );
}
protected function get_post_id_for_data() {
return $this->post_id_for_data;
}
/**
* Get file handle ID.
*
* Retrieve the handle ID for the previewed post CSS file.
*
* @since 1.9.0
* @access protected
*
* @return string CSS file handle ID.
*/
protected function get_file_handle_id() {
return 'elementor-preview-' . $this->get_post_id_for_data();
}
}
files/css/global-css.php 0000644 00000007670 14720700033 0011203 0 ustar 00 render_schemes_and_globals_css();
}
/**
* Get inline dependency.
*
* Retrieve the name of the stylesheet used by `wp_add_inline_style()`.
*
* @since 1.2.0
* @access protected
*
* @return string Name of the stylesheet.
*/
protected function get_inline_dependency() {
return 'elementor-frontend';
}
/**
* Is update required.
*
* Whether the CSS requires an update. When there are new schemes or settings
* updates.
*
* @since 1.2.0
* @access protected
*
* @return bool True if the CSS requires an update, False otherwise.
*/
protected function is_update_required() {
return $this->get_meta( 'time' ) < get_option( Settings::UPDATE_TIME_FIELD );
}
/**
* Render schemes CSS.
*
* Parse the CSS for all the widgets and all the scheme controls.
*
* @since 1.2.0
* @access private
*/
private function render_schemes_and_globals_css() {
$elementor = Plugin::$instance;
/** @var Manager $module */
$kits_manager = Plugin::$instance->kits_manager;
$custom_colors_enabled = $kits_manager->is_custom_colors_enabled();
$custom_typography_enabled = $kits_manager->is_custom_typography_enabled();
// If both default colors and typography are disabled, there is no need to render schemes and default global css.
if ( ! $custom_colors_enabled && ! $custom_typography_enabled ) {
return;
}
foreach ( $elementor->widgets_manager->get_widget_types() as $widget ) {
$controls = $widget->get_controls();
$global_controls = [];
$global_values['__globals__'] = [];
foreach ( $controls as $control ) {
$is_color_control = 'color' === $control['type'];
$is_typography_control = isset( $control['groupType'] ) && 'typography' === $control['groupType'];
// If it is a color/typography control and default colors/typography are disabled,
// don't add the default CSS.
if ( ( $is_color_control && ! $custom_colors_enabled ) || ( $is_typography_control && ! $custom_typography_enabled ) ) {
continue;
}
$global_control = $control;
// Handle group controls that don't have a default global property.
if ( ! empty( $control['groupType'] ) ) {
$global_control = $controls[ $control['groupPrefix'] . $control['groupType'] ];
}
// If the control has a default global defined, add it to the globals array
// that is used in add_control_rules.
if ( ! empty( $control['global']['default'] ) ) {
$global_values['__globals__'][ $control['name'] ] = $global_control['global']['default'];
}
if ( ! empty( $global_control['global']['default'] ) ) {
$global_controls[] = $control;
}
}
foreach ( $global_controls as $control ) {
$this->add_control_rules( $control, $controls, function( $control ) {}, [ '{{WRAPPER}}' ], [ '.elementor-widget-' . $widget->get_name() ], $global_values );
}
}
}
}
files/css/base.php 0000644 00000066175 14720700033 0010074 0 ustar 00 update_file();
$meta = $this->get_meta();
$meta['time'] = time();
$content = $this->get_content();
if ( empty( $content ) ) {
$meta['status'] = self::CSS_STATUS_EMPTY;
$meta['css'] = '';
} else {
$use_external_file = $this->use_external_file();
if ( $use_external_file ) {
$meta['status'] = self::CSS_STATUS_FILE;
} else {
$meta['status'] = self::CSS_STATUS_INLINE;
$meta['css'] = $content;
}
}
$meta['dynamic_elements_ids'] = $this->dynamic_elements_ids;
$this->update_meta( $meta );
}
/**
* @since 2.1.0
* @access public
*/
public function write() {
if ( $this->use_external_file() ) {
parent::write();
}
}
/**
* @since 3.0.0
* @access public
*/
public function delete() {
if ( $this->use_external_file() ) {
parent::delete();
} else {
$this->delete_meta();
}
}
/**
* Get Responsive Control Duplication Mode
*
* @since 3.4.0
*
* @return string
*/
protected function get_responsive_control_duplication_mode() {
return 'on';
}
/**
* Enqueue CSS.
*
* Either enqueue the CSS file in Elementor or add inline style.
*
* This method is also responsible for loading the fonts.
*
* @since 1.2.0
* @access public
*/
public function enqueue() {
$handle_id = $this->get_file_handle_id();
if ( isset( self::$printed[ $handle_id ] ) ) {
return;
}
self::$printed[ $handle_id ] = true;
$meta = $this->get_meta();
if ( self::CSS_STATUS_EMPTY === $meta['status'] ) {
return;
}
/**
* Enqueue CSS file.
*
* Fires before enqueuing a CSS file.
*
* @param Base $this The current CSS file.
*/
do_action( 'elementor/css-file/before_enqueue', $this );
// First time after clear cache and etc.
if ( '' === $meta['status'] || $this->is_update_required() ) {
$this->update();
$meta = $this->get_meta();
}
if ( self::CSS_STATUS_INLINE === $meta['status'] ) {
$dep = $this->get_inline_dependency();
// If the dependency has already been printed ( like a template in footer )
if ( wp_styles()->query( $dep, 'done' ) ) {
printf( '', $this->get_file_handle_id(), $meta['css'] ); // XSS ok.
} else {
wp_add_inline_style( $dep, $meta['css'] );
}
} elseif ( self::CSS_STATUS_FILE === $meta['status'] ) { // Re-check if it's not empty after CSS update.
wp_enqueue_style( $this->get_file_handle_id(), $this->get_url(), $this->get_enqueue_dependencies(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
}
// Handle fonts.
if ( ! empty( $meta['fonts'] ) ) {
foreach ( $meta['fonts'] as $font ) {
Plugin::$instance->frontend->enqueue_font( $font );
}
}
if ( ! empty( $meta['icons'] ) ) {
$icons_types = Icons_Manager::get_icon_manager_tabs();
foreach ( $meta['icons'] as $icon_font ) {
if ( ! isset( $icons_types[ $icon_font ] ) ) {
continue;
}
Plugin::$instance->frontend->enqueue_font( $icon_font );
}
}
$name = $this->get_name();
/**
* Enqueue CSS file.
*
* Fires when CSS file is enqueued on Elementor.
*
* The dynamic portion of the hook name, `$name`, refers to the CSS file name.
*
* @since 2.0.0
*
* @param Base $this The current CSS file.
*/
do_action( "elementor/css-file/{$name}/enqueue", $this );
/**
* Enqueue CSS file.
*
* Fires after enqueuing a CSS file.
*
* @param Base $this The current CSS file.
*/
do_action( 'elementor/css-file/after_enqueue', $this );
}
/**
* Print CSS.
*
* Output the final CSS inside the `'; // XSS ok.
Plugin::$instance->frontend->print_fonts_links();
}
/**
* Add control rules.
*
* Parse the CSS for all the elements inside any given control.
*
* This method recursively renders the CSS for all the selectors in the control.
*
* @since 1.2.0
* @access public
*
* @param array $control The controls.
* @param array $controls_stack The controls stack.
* @param callable $value_callback Callback function for the value.
* @param array $placeholders Placeholders.
* @param array $replacements Replacements.
* @param array $values Global Values.
*/
public function add_control_rules( array $control, array $controls_stack, callable $value_callback, array $placeholders, array $replacements, array $values = [] ) {
if ( empty( $control['selectors'] ) ) {
return;
}
$control_global_key = $control['name'];
if ( ! empty( $control['groupType'] ) ) {
$control_global_key = $control['groupPrefix'] . $control['groupType'];
}
$global_values = [];
$global_key = '';
if ( ! empty( $values['__globals__'] ) ) {
$global_values = $values['__globals__'];
}
if ( ! empty( $global_values[ $control_global_key ] ) ) {
$global_key = $global_values[ $control_global_key ];
}
if ( ! $global_key ) {
$value = call_user_func( $value_callback, $control );
if ( null === $value ) {
return;
}
}
$stylesheet = $this->get_stylesheet();
$control = apply_filters( 'elementor/files/css/selectors', $control, $value ?? [], $this );
foreach ( $control['selectors'] as $selector => $css_property ) {
$output_css_property = '';
if ( $global_key ) {
$selector_global_value = $this->get_selector_global_value( $control, $global_key );
if ( $selector_global_value ) {
$output_css_property = preg_replace( '/(:)[^;]+(;?)/', '$1' . $selector_global_value . '$2', $css_property );
}
} else {
try {
if ( $this->unit_has_custom_selector( $control, $value ) ) {
$css_property = $control['unit_selectors_dictionary'][ $value['unit'] ];
}
$output_css_property = preg_replace_callback( '/{{(?:([^.}]+)\.)?([^}| ]*)(?: *\|\| *(?:([^.}]+)\.)?([^}| ]*) *)*}}/', function( $matches ) use ( $control, $value_callback, $controls_stack, $value, $css_property ) {
$external_control_missing = $matches[1] && ! isset( $controls_stack[ $matches[1] ] );
$parsed_value = '';
$value = apply_filters( 'elementor/files/css/property', $value, $css_property, $matches, $control );
if ( ! $external_control_missing ) {
$parsed_value = $this->parse_property_placeholder( $control, $value, $controls_stack, $value_callback, $matches[2], $matches[1] );
}
if ( '' === $parsed_value ) {
if ( isset( $matches[4] ) ) {
$parsed_value = $matches[4];
$is_string_value = preg_match( '/^([\'"])(.*)\1$/', $parsed_value, $string_matches );
if ( $is_string_value ) {
$parsed_value = $string_matches[2];
} elseif ( ! is_numeric( $parsed_value ) ) {
if ( $matches[3] && ! isset( $controls_stack[ $matches[3] ] ) ) {
return '';
}
$parsed_value = $this->parse_property_placeholder( $control, $value, $controls_stack, $value_callback, $matches[4], $matches[3] );
}
}
if ( '' === $parsed_value ) {
if ( $external_control_missing ) {
return '';
}
throw new \Exception();
}
}
if ( '__EMPTY__' === $parsed_value ) {
$parsed_value = '';
}
return $parsed_value;
}, $css_property );
} catch ( \Exception $e ) {
return;
}
}
if ( ! $output_css_property ) {
continue;
}
$device_pattern = '/^(?:\([^\)]+\)){1,2}/';
preg_match( $device_pattern, $selector, $device_rules );
$query = [];
if ( $device_rules ) {
$selector = preg_replace( $device_pattern, '', $selector );
preg_match_all( '/\(([^)]+)\)/', $device_rules[0], $pure_device_rules );
$pure_device_rules = $pure_device_rules[1];
foreach ( $pure_device_rules as $device_rule ) {
if ( Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP === $device_rule ) {
continue;
}
$device = preg_replace( '/\+$/', '', $device_rule );
$endpoint = $device === $device_rule ? 'max' : 'min';
$query[ $endpoint ] = $device;
}
}
$parsed_selector = str_replace( $placeholders, $replacements, $selector );
if ( ! $query && ! empty( $control['responsive'] ) ) {
$query = array_intersect_key( $control['responsive'], array_flip( [ 'min', 'max' ] ) );
if ( ! empty( $query['max'] ) && Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP === $query['max'] ) {
unset( $query['max'] );
}
}
$stylesheet->add_rules( $parsed_selector, $output_css_property, $query );
}
}
protected function unit_has_custom_selector( $control, $value ) {
return isset( $control['unit_selectors_dictionary'] ) && isset( $control['unit_selectors_dictionary'][ $value['unit'] ] );
}
/**
* @param array $control
* @param mixed $value
* @param array $controls_stack
* @param callable $value_callback
* @param string $placeholder
* @param string $parser_control_name
*
* @return string
*/
public function parse_property_placeholder( array $control, $value, array $controls_stack, $value_callback, $placeholder, $parser_control_name = null ) {
if ( $parser_control_name ) {
// If both the processed control and the control name found in the placeholder are responsive
if ( ! empty( $control['responsive'] ) && ! empty( $controls_stack[ $parser_control_name ]['responsive'] ) ) {
$device_suffix = Controls_Manager::get_responsive_control_device_suffix( $control );
$control = $controls_stack[ $parser_control_name . $device_suffix ] ?? $controls_stack[ $parser_control_name ];
} else {
$control = $controls_stack[ $parser_control_name ];
}
$value = call_user_func( $value_callback, $control );
}
// If the control value is empty, check for global default. `0` (integer, string) are falsy but are valid values.
if ( empty( $value ) && '0' !== $value && 0 !== $value ) {
$value = $this->get_control_global_default_value( $control );
}
if ( Controls_Manager::FONT === $control['type'] ) {
$this->fonts[] = $value;
}
/** @var Base_Data_Control $control_obj */
$control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
return (string) $control_obj->get_style_value( $placeholder, $value, $control );
}
/**
* Get the fonts.
*
* Retrieve the list of fonts.
*
* @since 1.9.0
* @access public
*
* @return array Fonts.
*/
public function get_fonts() {
return $this->fonts;
}
/**
* Get stylesheet.
*
* Retrieve the CSS file stylesheet instance.
*
* @since 1.2.0
* @access public
*
* @return Stylesheet The stylesheet object.
*/
public function get_stylesheet() {
if ( ! $this->stylesheet_obj ) {
$this->init_stylesheet();
}
return $this->stylesheet_obj;
}
/**
* Add controls stack style rules.
*
* Parse the CSS for all the elements inside any given controls stack.
*
* This method recursively renders the CSS for all the child elements in the stack.
*
* @since 1.6.0
* @access public
*
* @param Controls_Stack $controls_stack The controls stack.
* @param array $controls Controls array.
* @param array $values Values array.
* @param array $placeholders Placeholders.
* @param array $replacements Replacements.
* @param array $all_controls All controls.
*/
public function add_controls_stack_style_rules( Controls_Stack $controls_stack, array $controls, array $values, array $placeholders, array $replacements, array $all_controls = null ) {
if ( ! $all_controls ) {
$all_controls = $controls_stack->get_controls();
}
$parsed_dynamic_settings = $controls_stack->parse_dynamic_settings( $values, $controls );
foreach ( $controls as $control ) {
if ( ! empty( $control['style_fields'] ) ) {
$this->add_repeater_control_style_rules( $controls_stack, $control, $values[ $control['name'] ], $placeholders, $replacements );
}
if ( ! empty( $control[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ) ) {
$this->add_dynamic_control_style_rules( $control, $control[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] );
}
if ( Controls_Manager::ICONS === $control['type'] ) {
$this->icons_fonts[] = $values[ $control['name'] ]['library'];
}
if ( ! empty( $parsed_dynamic_settings[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ) ) {
// Dynamic CSS should not be added to the CSS files.
// Instead it's handled by \Elementor\Core\DynamicTags\Dynamic_CSS
// and printed in a style tag.
unset( $parsed_dynamic_settings[ $control['name'] ] );
$this->dynamic_elements_ids[] = $controls_stack->get_id();
continue;
}
if ( empty( $control['selectors'] ) ) {
continue;
}
$this->add_control_style_rules( $control, $parsed_dynamic_settings, $all_controls, $placeholders, $replacements );
}
}
/**
* Get file handle ID.
*
* Retrieve the file handle ID.
*
* @since 1.2.0
* @access protected
* @abstract
*
* @return string CSS file handle ID.
*/
abstract protected function get_file_handle_id();
/**
* Render CSS.
*
* Parse the CSS.
*
* @since 1.2.0
* @access protected
* @abstract
*/
abstract protected function render_css();
protected function get_default_meta() {
return array_merge( parent::get_default_meta(), [
'fonts' => array_unique( $this->fonts ),
'icons' => array_unique( $this->icons_fonts ),
'dynamic_elements_ids' => [],
'status' => '',
] );
}
/**
* Get enqueue dependencies.
*
* Retrieve the name of the stylesheet used by `wp_enqueue_style()`.
*
* @since 1.2.0
* @access protected
*
* @return array Name of the stylesheet.
*/
protected function get_enqueue_dependencies() {
return [];
}
/**
* Get inline dependency.
*
* Retrieve the name of the stylesheet used by `wp_add_inline_style()`.
*
* @since 1.2.0
* @access protected
*
* @return string Name of the stylesheet.
*/
protected function get_inline_dependency() {
return '';
}
/**
* Is update required.
*
* Whether the CSS requires an update. When there are new schemes or settings
* updates.
*
* @since 1.2.0
* @access protected
*
* @return bool True if the CSS requires an update, False otherwise.
*/
protected function is_update_required() {
return false;
}
/**
* Parse CSS.
*
* Parsing the CSS file.
*
* @since 1.2.0
* @access protected
*/
protected function parse_content() {
Performance::set_use_style_controls( true );
$initial_responsive_controls_duplication_mode = Plugin::$instance->breakpoints->get_responsive_control_duplication_mode();
Plugin::$instance->breakpoints->set_responsive_control_duplication_mode( $this->get_responsive_control_duplication_mode() );
$this->render_css();
$name = $this->get_name();
/**
* Parse CSS file.
*
* Fires when CSS file is parsed on Elementor.
*
* The dynamic portion of the hook name, `$name`, refers to the CSS file name.
*
* @since 2.0.0
*
* @param Base $this The current CSS file.
*/
do_action( "elementor/css-file/{$name}/parse", $this );
Plugin::$instance->breakpoints->set_responsive_control_duplication_mode( $initial_responsive_controls_duplication_mode );
Performance::set_use_style_controls( false );
return $this->get_stylesheet()->__toString();
}
/**
* Add control style rules.
*
* Register new style rules for the control.
*
* @since 1.6.0
* @access private
*
* @param array $control The control.
* @param array $values Values array.
* @param array $controls The controls stack.
* @param array $placeholders Placeholders.
* @param array $replacements Replacements.
*/
protected function add_control_style_rules( array $control, array $values, array $controls, array $placeholders, array $replacements ) {
$this->add_control_rules(
$control, $controls, function( $control ) use ( $values ) {
return $this->get_style_control_value( $control, $values );
}, $placeholders, $replacements, $values
);
}
/**
* Get Control Global Default Value
*
* If the control has a global default value, and the corresponding global default setting is enabled, this method
* fetches and returns the global default value. Otherwise, it returns null.
*
* @since 3.7.0
* @access private
*
* @param $control
* @return string|null
*/
private function get_control_global_default_value( $control ) {
if ( empty( $control['global']['default'] ) ) {
return null;
}
// If the control value is empty, and the control has a global default set, fetch the global value and use it.
$global_enabled = false;
if ( 'color' === $control['type'] ) {
$global_enabled = Plugin::$instance->kits_manager->is_custom_colors_enabled();
} elseif ( isset( $control['groupType'] ) && 'typography' === $control['groupType'] ) {
$global_enabled = Plugin::$instance->kits_manager->is_custom_typography_enabled();
}
$value = null;
// Only apply the global default if Global Colors are enabled.
if ( $global_enabled ) {
$value = $this->get_selector_global_value( $control, $control['global']['default'] );
}
return $value;
}
/**
* Get style control value.
*
* Retrieve the value of the style control for any give control and values.
*
* It will retrieve the control name and return the style value.
*
* @since 1.6.0
* @access private
*
* @param array $control The control.
* @param array $values Values array.
*
* @return mixed Style control value.
*/
private function get_style_control_value( array $control, array $values ) {
if ( ! empty( $values['__globals__'][ $control['name'] ] ) ) {
// When the control itself has no global value, but it refers to another control global value
return $this->get_selector_global_value( $control, $values['__globals__'][ $control['name'] ] );
}
$value = $values[ $control['name'] ];
if ( isset( $control['selectors_dictionary'][ $value ] ) ) {
$value = $control['selectors_dictionary'][ $value ];
}
if ( ! is_numeric( $value ) && ! is_float( $value ) && empty( $value ) ) {
return null;
}
return $value;
}
/**
* Init stylesheet.
*
* Initialize CSS file stylesheet by creating a new `Stylesheet` object and register new
* breakpoints for the stylesheet.
*
* @since 1.2.0
* @access private
*/
private function init_stylesheet() {
$this->stylesheet_obj = new Stylesheet();
$active_breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
foreach ( $active_breakpoints as $breakpoint_name => $breakpoint ) {
$this->stylesheet_obj->add_device( $breakpoint_name, $breakpoint->get_value() );
}
}
/**
* Add repeater control style rules.
*
* Register new style rules for the repeater control.
*
* @since 2.0.0
* @access private
*
* @param Controls_Stack $controls_stack The control stack.
* @param array $repeater_control The repeater control.
* @param array $repeater_values Repeater values array.
* @param array $placeholders Placeholders.
* @param array $replacements Replacements.
*/
protected function add_repeater_control_style_rules( Controls_Stack $controls_stack, array $repeater_control, array $repeater_values, array $placeholders, array $replacements ) {
$placeholders = array_merge( $placeholders, [ '{{CURRENT_ITEM}}' ] );
foreach ( $repeater_control['style_fields'] as $index => $item ) {
$this->add_controls_stack_style_rules(
$controls_stack,
$item,
$repeater_values[ $index ],
$placeholders,
array_merge( $replacements, [ '.elementor-repeater-item-' . $repeater_values[ $index ]['_id'] ] ),
$repeater_control['fields']
);
}
}
/**
* Add dynamic control style rules.
*
* Register new style rules for the dynamic control.
*
* @since 2.0.0
* @access private
*
* @param array $control The control.
* @param string $value The value.
*/
protected function add_dynamic_control_style_rules( array $control, $value ) {
Plugin::$instance->dynamic_tags->parse_tags_text( $value, $control, function( $id, $name, $settings ) {
$tag = Plugin::$instance->dynamic_tags->create_tag( $id, $name, $settings );
if ( ! $tag instanceof Tag ) {
return;
}
$this->add_controls_stack_style_rules( $tag, $this->get_style_controls( $tag ), $tag->get_active_settings(), [ '{{WRAPPER}}' ], [ '#elementor-tag-' . $id ] );
} );
}
private function get_selector_global_value( $control, $global_key ) {
$data = Plugin::$instance->data_manager_v2->run( $global_key );
if ( empty( $data['value'] ) ) {
return null;
}
$global_args = explode( '?id=', $global_key );
$id = $global_args[1];
if ( ! empty( $control['groupType'] ) ) {
$strings_to_replace = [ $control['groupPrefix'] ];
$active_breakpoint_keys = array_keys( Plugin::$instance->breakpoints->get_active_breakpoints() );
foreach ( $active_breakpoint_keys as $breakpoint ) {
$strings_to_replace[] = '_' . $breakpoint;
}
$property_name = str_replace( $strings_to_replace, '', $control['name'] );
// TODO: This check won't retrieve the proper answer for array values (multiple controls).
if ( empty( $data['value'][ Global_Typography::TYPOGRAPHY_GROUP_PREFIX . $property_name ] ) ) {
return null;
}
$property_name = str_replace( '_', '-', $property_name );
$value = "var( --e-global-$control[groupType]-$id-$property_name )";
if ( $control['groupPrefix'] . 'font_family' === $control['name'] ) {
$default_generic_fonts = Plugin::$instance->kits_manager->get_current_settings( 'default_generic_fonts' );
if ( $default_generic_fonts ) {
$value .= ", $default_generic_fonts";
}
}
} else {
$value = "var( --e-global-$control[type]-$id )";
}
return $value;
}
final protected function get_active_controls( Controls_Stack $controls_stack, array $controls = null, array $settings = null ) {
if ( ! $controls ) {
$controls = $controls_stack->get_controls();
}
if ( ! $settings ) {
$settings = $controls_stack->get_controls_settings();
}
if ( $this->is_global_parsing_supported() ) {
$settings = $this->parse_global_settings( $settings, $controls );
}
$active_controls = array_reduce(
array_keys( $controls ), function( $active_controls, $control_key ) use ( $controls_stack, $controls, $settings ) {
$control = $controls[ $control_key ];
if ( $controls_stack->is_control_visible( $control, $settings, $controls ) ) {
$active_controls[ $control_key ] = $control;
}
return $active_controls;
}, []
);
return $active_controls;
}
final public function get_style_controls( Controls_Stack $controls_stack, array $controls = null, array $settings = null ) {
$controls = $this->get_active_controls( $controls_stack, $controls, $settings );
$style_controls = [];
foreach ( $controls as $control_name => $control ) {
$control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
if ( ! $control_obj instanceof Base_Data_Control ) {
continue;
}
$control = array_merge( $control_obj->get_settings(), $control );
if ( $control_obj instanceof Control_Repeater ) {
$style_fields = [];
foreach ( $controls_stack->get_settings( $control_name ) as $item ) {
$style_fields[] = $this->get_style_controls( $controls_stack, $control['fields'], $item );
}
$control['style_fields'] = $style_fields;
}
if ( ! empty( $control['selectors'] ) || ! empty( $control['dynamic'] ) || $this->is_global_control( $controls_stack, $control_name, $controls ) || ! empty( $control['style_fields'] ) ) {
$style_controls[ $control_name ] = $control;
}
}
return $style_controls;
}
private function parse_global_settings( array $settings, array $controls ) {
foreach ( $controls as $control ) {
$control_name = $control['name'];
$control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
if ( ! $control_obj instanceof Base_Data_Control ) {
continue;
}
if ( $control_obj instanceof Control_Repeater ) {
foreach ( $settings[ $control_name ] as & $field ) {
$field = $this->parse_global_settings( $field, $control['fields'] );
}
continue;
}
if ( empty( $control['global']['active'] ) ) {
continue;
}
if ( empty( $settings['__globals__'][ $control_name ] ) ) {
continue;
}
$settings[ $control_name ] = 'global';
}
return $settings;
}
private function is_global_control( Controls_Stack $controls_stack, $control_name, $controls ) {
$control = $controls[ $control_name ];
$control_global_key = $control_name;
if ( ! empty( $control['groupType'] ) ) {
$control_global_key = $control['groupPrefix'] . $control['groupType'];
}
if ( empty( $controls[ $control_global_key ]['global']['active'] ) ) {
return false;
}
$globals = $controls_stack->get_settings( '__globals__' );
return ! empty( $globals[ $control_global_key ] );
}
}
files/css/post-local-cache.php 0000644 00000001366 14720700033 0012267 0 ustar 00 meta_cache;
}
protected function delete_meta() {
$this->meta_cache = [];
}
protected function update_meta( $meta ) {
$this->meta_cache = $meta;
}
protected function get_data() {
$document = Plugin::$instance->documents->get( $this->get_post_id_for_data() );
return $document ? $document->get_elements_data() : [];
}
}
files/css/post.php 0000644 00000017050 14720700033 0010133 0 ustar 00 post_id = $post_id;
parent::__construct( static::FILE_PREFIX . $post_id . '.css' );
}
/**
* Get CSS file name.
*
* Retrieve the CSS file name.
*
* @since 1.6.0
* @access public
*
* @return string CSS file name.
*/
public function get_name() {
return 'post';
}
/**
* Get post ID.
*
* Retrieve the ID of current post.
*
* @since 1.2.0
* @access public
*
* @return int Post ID.
*/
public function get_post_id() {
return $this->post_id;
}
/**
* Get unique element selector.
*
* Retrieve the unique selector for any given element.
*
* @since 1.2.0
* @access public
*
* @param Element_Base $element The element.
*
* @return string Unique element selector.
*/
public function get_element_unique_selector( Element_Base $element ) {
return '.elementor-' . $this->post_id . ' .elementor-element' . $element->get_unique_selector();
}
/**
* Load meta data.
*
* Retrieve the post CSS file meta data.
*
* @since 1.2.0
* @access protected
*
* @return array Post CSS file meta data.
*/
protected function load_meta() {
return get_post_meta( $this->post_id, static::META_KEY, true );
}
/**
* Update meta data.
*
* Update the global CSS file meta data.
*
* @since 1.2.0
* @access protected
*
* @param array $meta New meta data.
*/
protected function update_meta( $meta ) {
update_post_meta( $this->post_id, static::META_KEY, $meta );
}
/**
* Delete meta.
*
* Delete the file meta data.
*
* @since 2.1.0
* @access protected
*/
protected function delete_meta() {
delete_post_meta( $this->post_id, static::META_KEY );
}
public function write() {
parent::write();
if ( ! empty( $this->additional_style_dependencies ) ) {
$meta = $this->get_meta();
$meta['additional_style_dependencies'] = $this->additional_style_dependencies;
$this->update_meta( $meta );
}
}
/**
* Get post data.
*
* Retrieve raw post data from the database.
*
* @since 1.9.0
* @access protected
*
* @return array Post data.
*/
protected function get_data() {
$document = Plugin::$instance->documents->get( $this->post_id );
return $document ? $document->get_elements_data() : [];
}
/**
* Render CSS.
*
* Parse the CSS for all the elements.
*
* @since 1.2.0
* @access protected
*/
protected function render_css() {
$data = $this->get_data();
if ( ! empty( $data ) ) {
foreach ( $data as $element_data ) {
$element = Plugin::$instance->elements_manager->create_element_instance( $element_data );
if ( ! $element ) {
continue;
}
$this->render_styles( $element );
}
}
}
/**
* Enqueue CSS.
*
* Enqueue the post CSS file in Elementor.
*
* This method ensures that the post was actually built with elementor before
* enqueueing the post CSS file.
*
* @since 1.2.2
* @access public
*/
public function enqueue() {
$document = Plugin::$instance->documents->get( $this->post_id );
if ( ! $document || ! $document->is_built_with_elementor() ) {
return;
}
parent::enqueue();
}
/**
* Add controls-stack style rules.
*
* Parse the CSS for all the elements inside any given controls stack.
*
* This method recursively renders the CSS for all the child elements in the stack.
*
* @since 1.6.0
* @access public
*
* @param Controls_Stack $controls_stack The controls stack.
* @param array $controls Controls array.
* @param array $values Values array.
* @param array $placeholders Placeholders.
* @param array $replacements Replacements.
* @param array $all_controls All controls.
*/
public function add_controls_stack_style_rules( Controls_Stack $controls_stack, array $controls, array $values, array $placeholders, array $replacements, array $all_controls = null ) {
parent::add_controls_stack_style_rules( $controls_stack, $controls, $values, $placeholders, $replacements, $all_controls );
if ( $controls_stack instanceof Element_Base ) {
foreach ( $controls_stack->get_children() as $child_element ) {
$this->render_styles( $child_element );
}
}
}
/**
* Get enqueue dependencies.
*
* Retrieve the name of the stylesheet used by `wp_enqueue_style()`.
*
* @since 1.2.0
* @access protected
*
* @return array Name of the stylesheet.
*/
protected function get_enqueue_dependencies() {
$enqueue_dependencies = [ 'elementor-frontend' ];
$additional_style_dependencies = $this->get_meta( 'additional_style_dependencies' );
if ( ! empty( $additional_style_dependencies ) ) {
$enqueue_dependencies = array_merge( $enqueue_dependencies, $additional_style_dependencies );
}
return $enqueue_dependencies;
}
/**
* Get inline dependency.
*
* Retrieve the name of the stylesheet used by `wp_add_inline_style()`.
*
* @since 1.2.0
* @access protected
*
* @return string Name of the stylesheet.
*/
protected function get_inline_dependency() {
return 'elementor-frontend';
}
/**
* Get file handle ID.
*
* Retrieve the handle ID for the post CSS file.
*
* @since 1.2.0
* @access protected
*
* @return string CSS file handle ID.
*/
protected function get_file_handle_id() {
return 'elementor-post-' . $this->post_id;
}
/**
* Render styles.
*
* Parse the CSS for any given element.
*
* @since 1.2.0
* @access protected
*
* @param Element_Base $element The element.
*/
protected function render_styles( Element_Base $element ) {
/**
* Before element parse CSS.
*
* Fires before the CSS of the element is parsed.
*
* @since 1.2.0
*
* @param Post $this The post CSS file.
* @param Element_Base $element The element.
*/
do_action( 'elementor/element/before_parse_css', $this, $element );
$element_settings = $element->get_settings();
$this->add_controls_stack_style_rules( $element, $this->get_style_controls( $element, null, $element->get_parsed_dynamic_settings() ), $element_settings, [ '{{ID}}', '{{WRAPPER}}' ], [ $element->get_id(), $this->get_element_unique_selector( $element ) ] );
$element_style_depend = $element->get_style_depends();
if ( ! empty( $element_style_depend ) ) {
$this->additional_style_dependencies = array_unique( array_merge( $this->additional_style_dependencies, $element_style_depend ) );
}
/**
* After element parse CSS.
*
* Fires after the CSS of the element is parsed.
*
* @since 1.2.0
*
* @param Post $this The post CSS file.
* @param Element_Base $element The element.
*/
do_action( 'elementor/element/parse_css', $this, $element );
}
}
files/file-types/svg.php 0000644 00000013230 14720700033 0011232 0 ustar 00 sanitize_file( $filename );
}
/**
* Validate File
*
* @since 3.3.0
* @access public
*
* @param $file
* @return bool|\WP_Error
*/
public function validate_file( $file ) {
if ( ! $this->sanitize_svg( $file['tmp_name'] ) ) {
return new \WP_Error( Exceptions::FORBIDDEN, esc_html__( 'This file is not allowed for security reasons.', 'elementor' ) );
}
return true;
}
/**
* Sanitizer
*
* @since 3.5.0
* @access public
*
* @param $content
* @return bool|string
*/
public function sanitizer( $content ) {
return ( new SVG_Sanitizer() )->sanitize( $content );
}
/**
* WP Prepare Attachment For J
*
* Runs on the `wp_prepare_attachment_for_js` filter.
*
* @since 3.5.0
* @access public
*
* @param $attachment_data
* @param $attachment
* @param $meta
*
* @return mixed
*/
public function wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta ) {
if ( 'image' !== $attachment_data['type'] || 'svg+xml' !== $attachment_data['subtype'] || ! class_exists( 'SimpleXMLElement' ) ) {
return $attachment_data;
}
$svg = self::get_inline_svg( $attachment->ID );
if ( ! $svg ) {
return $attachment_data;
}
try {
$svg = new \SimpleXMLElement( $svg );
} catch ( \Exception $e ) {
return $attachment_data;
}
$src = $attachment_data['url'];
$width = (int) $svg['width'];
$height = (int) $svg['height'];
// Media Gallery
$attachment_data['image'] = compact( 'src', 'width', 'height' );
$attachment_data['thumb'] = compact( 'src', 'width', 'height' );
// Single Details of Image
$attachment_data['sizes']['full'] = [
'height' => $height,
'width' => $width,
'url' => $src,
'orientation' => $height > $width ? 'portrait' : 'landscape',
];
return $attachment_data;
}
/**
* Set Svg Meta Data
*
* Adds dimensions metadata to uploaded SVG files, since WordPress doesn't do it.
*
* @since 3.5.0
* @access public
*
* @return mixed
*/
public function set_svg_meta_data( $data, $id ) {
$attachment = get_post( $id ); // Filter makes sure that the post is an attachment.
$mime_type = $attachment->post_mime_type;
// If the attachment is an svg
if ( 'image/svg+xml' === $mime_type ) {
// If the svg metadata are empty or the width is empty or the height is empty.
// then get the attributes from xml.
if ( empty( $data ) || empty( $data['width'] ) || empty( $data['height'] ) ) {
$attachment = wp_get_attachment_url( $id );
$xml = simplexml_load_file( $attachment );
if ( ! empty( $xml ) ) {
$attr = $xml->attributes();
$view_box = explode( ' ', $attr->viewBox );// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$data['width'] = isset( $attr->width ) && preg_match( '/\d+/', $attr->width, $value ) ? (int) $value[0] : ( 4 === count( $view_box ) ? (int) $view_box[2] : null );
$data['height'] = isset( $attr->height ) && preg_match( '/\d+/', $attr->height, $value ) ? (int) $value[0] : ( 4 === count( $view_box ) ? (int) $view_box[3] : null );
}
}
}
return $data;
}
/**
* Delete Meta Cache
*
* Deletes the Inline SVG post meta entry.
*
* @since 3.5.0
* @access public
*/
public function delete_meta_cache() {
delete_post_meta_by_key( self::META_KEY );
}
/**
* File Sanitizer Can Run
*
* Checks if the classes required for the file sanitizer are in memory.
*
* @since 3.5.0
* @access public
* @static
*
* @return bool
*/
public static function file_sanitizer_can_run() {
return class_exists( 'DOMDocument' ) && class_exists( 'SimpleXMLElement' );
}
/**
* Get Inline SVG
*
* @since 3.5.0
* @access public
* @static
*
* @param $attachment_id
* @return bool|mixed|string
*/
public static function get_inline_svg( $attachment_id ) {
$svg = get_post_meta( $attachment_id, self::META_KEY, true );
if ( ! empty( $svg ) ) {
$valid_svg = ( new SVG_Sanitizer() )->sanitize( $svg );
return ( false === $valid_svg ) ? '' : $valid_svg;
}
$attachment_file = get_attached_file( $attachment_id );
if ( ! file_exists( $attachment_file ) ) {
return '';
}
$svg = Utils::file_get_contents( $attachment_file );
$valid_svg = ( new SVG_Sanitizer() )->sanitize( $svg );
if ( false === $valid_svg ) {
return '';
}
if ( ! empty( $valid_svg ) ) {
update_post_meta( $attachment_id, self::META_KEY, $valid_svg );
}
return $valid_svg;
}
public function __construct() {
add_filter( 'wp_update_attachment_metadata', [ $this, 'set_svg_meta_data' ], 10, 2 );
add_filter( 'wp_prepare_attachment_for_js', [ $this, 'wp_prepare_attachment_for_js' ], 10, 3 );
add_action( 'elementor/core/files/clear_cache', [ $this, 'delete_meta_cache' ] );
}
}
files/file-types/base.php 0000644 00000002503 14720700033 0011346 0 ustar 00 uploads_manager->create_unique_dir();
}
$zip->open( $file_path );
// if an array of allowed file types is provided, get the filtered file list to extract.
$allowed_files = $allowed_file_types ? $this->get_allowed_files( $zip, $allowed_file_types ) : null;
$zip->extractTo( $extraction_directory, $allowed_files );
$zip->close();
return [
'extraction_directory' => $extraction_directory,
'files' => $this->find_temp_files( $extraction_directory ),
];
}
/**
* Get Allowed Files
*
* Accepts a zipArchive instance and an array of allowed file types. Iterates over the zip archive's files and
* checks if their extensions are in the list of allowed file types. Returns an array containing all valid files.
*
* @since 3.3.0
*
* @param \ZipArchive $zip
* @param array $allowed_file_types
* @return array
*/
private function get_allowed_files( $zip, $allowed_file_types ) {
$allowed_files = [];
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
for ( $i = 0; $i < $zip->numFiles; $i++ ) {
$filename = $zip->getNameIndex( $i );
$extension = pathinfo( $filename, PATHINFO_EXTENSION );
// Skip files with transversal paths.
if ( strpos( $filename, '..' ) !== false ) {
continue;
}
if ( in_array( $extension, $allowed_file_types, true ) ) {
$allowed_files[] = $filename;
}
}
return $allowed_files;
}
/**
* Find temporary files.
*
* Recursively finds a list of temporary files from the extracted zip file.
*
* Example return data:
*
* [
* 0 => '/www/wp-content/uploads/elementor/tmp/5eb3a7a411d44/templates/block-2-col-marble-title.json',
* 1 => '/www/wp-content/uploads/elementor/tmp/5eb3a7a411d44/templates/block-2-col-text-and-photo.json',
* ]
*
* @since 2.9.8
* @access private
*
* @param string $temp_path - The temporary file path to scan for template files
*
* @return array An array of temporary files on the filesystem
*/
private function find_temp_files( $temp_path ) {
$file_names = [];
$possible_file_names = array_diff( scandir( $temp_path ), [ '.', '..' ] );
// Find nested files in the unzipped path. This happens for example when the user imports a Website Kit.
foreach ( $possible_file_names as $possible_file_name ) {
$full_possible_file_name = $temp_path . $possible_file_name;
if ( is_dir( $full_possible_file_name ) ) {
$file_names = array_merge( $file_names, $this->find_temp_files( $full_possible_file_name . '/' ) );
} else {
$file_names[] = $full_possible_file_name;
}
}
return $file_names;
}
}
files/base.php 0000644 00000013135 14720700033 0007270 0 ustar 00 files_manager->get( get_called_class(), func_get_args() );
}
/**
* @since 2.1.0
* @access public
*/
public function __construct( $file_name ) {
/**
* Elementor File Name
*
* Filters the File name
*
* @since 2.3.0
*
* @param string $file_name
* @param object $this The file instance, which inherits Elementor\Core\Files
*/
$file_name = apply_filters( 'elementor/files/file_name', $file_name, $this );
$this->set_file_name( $file_name );
$this->set_files_dir( static::DEFAULT_FILES_DIR );
$this->set_path();
}
/**
* @since 2.1.0
* @access public
*/
public function set_files_dir( $files_dir ) {
$this->files_dir = $files_dir;
}
/**
* @since 2.1.0
* @access public
*/
public function set_file_name( $file_name ) {
$this->file_name = $file_name;
}
/**
* @since 2.1.0
* @access public
*/
public function get_file_name() {
return $this->file_name;
}
/**
* @since 2.1.0
* @access public
*/
public function get_url() {
$url = set_url_scheme( self::get_base_uploads_url() . $this->files_dir . $this->file_name );
return add_query_arg( [ 'ver' => $this->get_meta( 'time' ) ], $url );
}
/**
* Get Path
*
* Returns the local path of the generated file.
*
* @since 3.5.0
* @access public
*
* @return string
*/
public function get_path() {
return set_url_scheme( self::get_base_uploads_dir() . $this->files_dir . $this->file_name );
}
/**
* @since 2.1.0
* @access public
*/
public function get_content() {
if ( ! $this->content ) {
$this->content = $this->parse_content();
}
return $this->content;
}
/**
* @since 2.1.0
* @access public
*/
public function update() {
$this->update_file();
$meta = $this->get_meta();
$meta['time'] = time();
$this->update_meta( $meta );
}
/**
* @since 2.1.0
* @access public
*/
public function update_file() {
$this->content = $this->parse_content();
if ( $this->content ) {
$this->write();
} else {
$this->delete();
}
}
/**
* @since 2.1.0
* @access public
*/
public function write() {
return file_put_contents( $this->path, $this->content );
}
/**
* @since 2.1.0
* @access public
*/
public function delete() {
if ( file_exists( $this->path ) ) {
unlink( $this->path );
}
$this->delete_meta();
}
/**
* Get meta data.
*
* Retrieve the CSS file meta data. Returns an array of all the data, or if
* custom property is given it will return the property value, or `null` if
* the property does not exist.
*
* @since 2.1.0
* @access public
*
* @param string $property Optional. Custom meta data property. Default is
* null.
*
* @return array|null An array of all the data, or if custom property is
* given it will return the property value, or `null` if
* the property does not exist.
*/
public function get_meta( $property = null ) {
$meta = array_merge( $this->get_default_meta(), (array) $this->load_meta() );
if ( $property ) {
return isset( $meta[ $property ] ) ? $meta[ $property ] : null;
}
return $meta;
}
/**
* @since 2.1.0
* @access protected
* @abstract
*/
abstract protected function parse_content();
/**
* Load meta.
*
* Retrieve the file meta data.
*
* @since 2.1.0
* @access protected
*/
protected function load_meta() {
return get_option( static::META_KEY );
}
/**
* Update meta.
*
* Update the file meta data.
*
* @since 2.1.0
* @access protected
*
* @param array $meta New meta data.
*/
protected function update_meta( $meta ) {
update_option( static::META_KEY, $meta );
}
/**
* Delete meta.
*
* Delete the file meta data.
*
* @since 2.1.0
* @access protected
*/
protected function delete_meta() {
delete_option( static::META_KEY );
}
/**
* @since 2.1.0
* @access protected
*/
protected function get_default_meta() {
return [
'time' => 0,
];
}
/**
* @since 2.1.0
* @access private
* @static
*/
private static function get_wp_uploads_dir() {
global $blog_id;
if ( empty( self::$wp_uploads_dir[ $blog_id ] ) ) {
self::$wp_uploads_dir[ $blog_id ] = wp_upload_dir( null, false );
}
return self::$wp_uploads_dir[ $blog_id ];
}
/**
* @since 2.1.0
* @access private
*/
private function set_path() {
$dir_path = self::get_base_uploads_dir() . $this->files_dir;
if ( ! is_dir( $dir_path ) ) {
wp_mkdir_p( $dir_path );
}
$this->path = $dir_path . $this->file_name;
}
}
files/uploads-manager.php 0000644 00000044242 14720700033 0011440 0 ustar 00 new Json(),
'zip' => new Zip(),
'svg' => new Svg(),
];
foreach ( $file_types as $file_type => $file_handler ) {
$this->file_type_handlers[ $file_type ] = $file_handler;
}
}
/**
* Extract and Validate Zip
*
* This method accepts a $file array (which minimally should include a 'tmp_name')
*
* @since 3.3.0
* @access public
*
* @param string $file_path
* @param array $allowed_file_types
* @return array|\WP_Error
*/
public function extract_and_validate_zip( $file_path, $allowed_file_types = null ) {
$result = [];
/** @var Zip $zip_handler - File Type */
$zip_handler = $this->file_type_handlers['zip'];
// Returns an array of file paths.
$extracted = $zip_handler->extract( $file_path, $allowed_file_types );
if ( is_wp_error( $extracted ) ) {
return $extracted;
}
// If there are no extracted file names, no files passed the extraction validation.
if ( empty( $extracted['files'] ) ) {
// TODO: Decide what to do if no files passed the extraction validation
return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT );
}
$result['extraction_directory'] = $extracted['extraction_directory'];
foreach ( $extracted['files'] as $extracted_file_path ) {
// Each file is an array with a 'name' (file path) property.
if ( ! is_wp_error( $this->validate_file( [ 'tmp_name' => $extracted_file_path ] ) ) ) {
$result['files'][] = $extracted_file_path;
}
}
return $result;
}
/**
* Handle Elementor Upload
*
* This method receives a $file array. If the received file is a Base64 string, the $file array should include a
* 'fileData' property containing the string, which is decoded and has its contents stored in a temporary file.
* If the $file parameter passed is a standard $file array, the 'name' and 'tmp_name' properties are used for
* validation.
*
* The file goes through validation; if it passes validation, the file is returned. Otherwise, an error is returned.
*
* @since 3.3.0
* @access public
*
* @param array $data
* @param array $allowed_file_extensions Optional. an array of file types that are allowed to pass validation for each
* upload.
* @return array|\WP_Error
*/
public function handle_elementor_upload( array $data, $allowed_file_extensions = null ) {
// If $file['fileData'] is set, it signals that the passed file is a Base64 string that needs to be decoded and
// saved to a temporary file.
if ( isset( $data['fileData'] ) ) {
$data = $this->save_base64_to_tmp_file( $data, $allowed_file_extensions );
}
if ( is_wp_error( $data ) ) {
return $data;
}
$validation_result = $this->validate_file( $data, $allowed_file_extensions );
if ( is_wp_error( $validation_result ) ) {
return $validation_result;
}
return $data;
}
/**
* are Unfiltered Uploads Enabled
*
* @since 3.5.0
* @access public
*
* @return bool
*/
final public static function are_unfiltered_uploads_enabled() {
$enabled = ! ! get_option( self::UNFILTERED_FILE_UPLOADS_KEY )
&& Svg::file_sanitizer_can_run()
&& User::is_current_user_can_upload_json();
/**
* Allow Unfiltered Files Upload.
*
* Determines whether to enable unfiltered file uploads.
*
* @since 3.0.0
*
* @param bool $enabled Whether upload is enabled or not.
*/
$enabled = apply_filters( 'elementor/files/allow_unfiltered_upload', $enabled );
return $enabled;
}
/**
* Handle Elementor WP Media Upload
*
* Runs on the 'wp_handle_upload_prefilter' filter.
*
* @since 3.2.0
* @access public
*
* @param $file
* @return mixed
*/
public function handle_elementor_wp_media_upload( $file ) {
// If it isn't a file uploaded by Elementor, we do not intervene.
if ( ! $this->is_elementor_wp_media_upload() ) {
return $file;
}
$result = $this->validate_file( $file );
if ( is_wp_error( $result ) ) {
$file['error'] = $result->get_error_message();
}
return $file;
}
/**
* Get File Type Handler
*
* Initialize the proper file type handler according to the file extension
* and assign it to the file type handlers array.
*
* @since 3.3.0
* @access public
*
* @param string|null $file_extension - file extension
* @return File_Type_Base[]|File_Type_Base
*/
public function get_file_type_handlers( $file_extension = null ) {
return self::get_items( $this->file_type_handlers, $file_extension );
}
/**
* Check filetype and ext
*
* A workaround for upload validation which relies on a PHP extension (fileinfo)
* with inconsistent reporting behaviour.
* ref: https://core.trac.wordpress.org/ticket/39550
* ref: https://core.trac.wordpress.org/ticket/40175
*
* @since 3.5.0
* @access public
*
* @param $data
* @param $file
* @param $filename
* @param $mimes
*
* @return mixed
*/
public function check_filetype_and_ext( $data, $file, $filename, $mimes ) {
if ( ! empty( $data['ext'] ) && ! empty( $data['type'] ) ) {
return $data;
}
$wp_file_type = wp_check_filetype( $filename, $mimes );
$file_type_handlers = $this->get_file_type_handlers();
if ( isset( $file_type_handlers[ $wp_file_type['ext'] ] ) ) {
$file_type_handler = $file_type_handlers[ $wp_file_type['ext'] ];
$data['ext'] = $file_type_handler->get_file_extension();
$data['type'] = $file_type_handler->get_mime_type();
}
return $data;
}
/**
* Remove File Or Directory
*
* Directory is deleted recursively with all of its contents (subdirectories and files).
*
* @since 3.3.0
* @access public
*
* @param string $path
*/
public function remove_file_or_dir( $path ) {
if ( is_dir( $path ) ) {
$this->remove_directory_with_files( $path );
} elseif ( is_file( $path ) ) {
unlink( $path );
}
}
/**
* Create Temp File
*
* Create a random temporary file.
*
* @since 3.3.0
* @access public
*
* @param string $file_content
* @param string $file_name
* @return string|\WP_Error
*/
public function create_temp_file( $file_content, $file_name ) {
$file_name = str_replace( ' ', '', sanitize_file_name( $file_name ) );
if ( empty( $file_name ) ) {
return new \WP_Error( 'invalid_file_name', esc_html__( 'Invalid file name.', 'elementor' ) );
}
$temp_filename = $this->create_unique_dir() . $file_name;
/**
* Temp File Path
*
* Allows modifying the full path of the temporary file.
*
* @since 3.7.0
*
* @param string full path to file
*/
$temp_filename = apply_filters( 'elementor/files/temp-file-path', $temp_filename );
file_put_contents( $temp_filename, $file_content ); // phpcs:ignore
return $temp_filename;
}
/**
* Get Temp Directory
*
* Get the temporary files directory path. If the directory does not exist, this method creates it.
*
* @since 3.3.0
* @access public
*
* @return string $temp_dir
*/
public function get_temp_dir() {
if ( ! $this->temp_dir ) {
$wp_upload_dir = wp_upload_dir();
$temp_dir = implode( DIRECTORY_SEPARATOR, [ $wp_upload_dir['basedir'], 'elementor', 'tmp' ] ) . DIRECTORY_SEPARATOR;
/**
* Temp File Path
*
* Allows modifying the full path of the temporary file.
*
* @since 3.7.0
*
* @param string temporary directory
*/
$this->temp_dir = apply_filters( 'elementor/files/temp-dir', $temp_dir );
if ( ! is_dir( $this->temp_dir ) ) {
wp_mkdir_p( $this->temp_dir );
}
}
return $this->temp_dir;
}
/**
* Create Unique Temp Dir
*
* Create a unique temporary directory
*
* @since 3.3.0
* @access public
*
* @return string the new directory path
*/
public function create_unique_dir() {
$unique_dir_path = $this->get_temp_dir() . uniqid() . DIRECTORY_SEPARATOR;
wp_mkdir_p( $unique_dir_path );
return $unique_dir_path;
}
/**
* Register Ajax Actions
*
* Runs on the 'elementor/ajax/register_actions' hook. Receives the AJAX module as a parameter and registers
* callbacks for specified action IDs.
*
* @since 3.5.0
* @access public
*
* @param Ajax $ajax
*/
public function register_ajax_actions( Ajax $ajax ) {
$ajax->register_ajax_action( 'enable_unfiltered_files_upload', [ $this, 'enable_unfiltered_files_upload' ] );
}
/**
* Set Unfiltered Files Upload
*
* @since 3.5.0
* @access public
*/
public function enable_unfiltered_files_upload() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
update_option( self::UNFILTERED_FILE_UPLOADS_KEY, 1 );
}
/**
* Support Unfiltered File Uploads
*
* When uploading a file within Elementor, this method adds the registered
* file types to WordPress' allowed mimes list. This will only happen if the user allowed unfiltered file uploads
* in Elementor's settings in the admin dashboard.
*
* @since 3.5.0
* @access public
*
* @param array $allowed_mimes
* @return array allowed mime types
*/
final public function support_unfiltered_elementor_file_uploads( $allowed_mimes ) {
if ( $this->is_elementor_upload() && $this->are_unfiltered_uploads_enabled() ) {
foreach ( $this->file_type_handlers as $file_type_handler ) {
$allowed_mimes[ $file_type_handler->get_file_extension() ] = $file_type_handler->get_mime_type();
}
}
return $allowed_mimes;
}
/**
* Set Elementor Upload State
*
* @since 3.5.0
* @access public
*
* @param $state
*/
public function set_elementor_upload_state( $state ) {
$this->is_elementor_upload = $state;
}
/**
* Is Elementor Upload
*
* This method checks if the current session includes a request to upload files made via Elementor.
*
* @since 3.5.0
* @access private
*
* @return bool
*/
private function is_elementor_upload() {
return $this->is_elementor_upload || $this->is_elementor_media_upload() || $this->is_elementor_wp_media_upload();
}
/**
* Is Elementor Media Upload
*
* Checks whether the current request includes uploading files via Elementor which are not destined for the Media
* Library.
*
* @since 3.5.0
* @access public
*
* @return bool
*/
public function is_elementor_media_upload() {
// Sometimes `uploadTypeCaller` passed as a GET parameter when using the WP Media Library REST API, where the
// whole request body is occupied by the uploaded file.
return isset( $_REQUEST['uploadTypeCaller'] ) && 'elementor-media-upload' === $_REQUEST['uploadTypeCaller']; // phpcs:ignore
}
/**
* Is Elementor WP Media Upload
*
* Checks whether the current request is a request to upload files into the WP Media Library via Elementor.
*
* @since 3.3.0
* @access private
*
* @return bool
*/
private function is_elementor_wp_media_upload() {
return isset( $_REQUEST['uploadTypeCaller'] ) && 'elementor-wp-media-upload' === $_REQUEST['uploadTypeCaller']; // phpcs:ignore
}
/**
* Add File Extension To Allowed Extensions List
*
* @since 3.3.0
* @access private
*
* @param string $file_type
*/
private function add_file_extension_to_allowed_extensions_list( $file_type ) {
$file_handler = $this->file_type_handlers[ $file_type ];
$file_extension = $file_handler->get_file_extension();
// Only add the file extension to the list if it doesn't already exist in it.
if ( ! in_array( $file_extension, $this->allowed_file_extensions, true ) ) {
$this->allowed_file_extensions[] = $file_extension;
}
}
/**
* Save Base64 as File
*
* Saves a Base64 string as a .tmp file in Elementor's temporary files directory.
*
* @since 3.3.0
* @access private
*
* @param $file
* @param array|null $allowed_file_extensions
*
* @return array|\WP_Error
*/
private function save_base64_to_tmp_file( $file, $allowed_file_extensions = null ) {
if ( empty( $file['fileName'] ) || empty( $file['fileData'] ) ) {
return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT );
}
$file_extension = pathinfo( $file['fileName'], PATHINFO_EXTENSION );
$is_file_type_allowed = $this->is_file_type_allowed( $file_extension, $allowed_file_extensions );
if ( is_wp_error( $is_file_type_allowed ) ) {
return $is_file_type_allowed;
}
$file_content = base64_decode( $file['fileData'] ); // phpcs:ignore
// If the decode fails
if ( ! $file_content ) {
return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT );
}
$temp_filename = $this->create_temp_file( $file_content, $file['fileName'] );
if ( is_wp_error( $temp_filename ) ) {
return $temp_filename;
}
return [
// the original uploaded file name
'name' => $file['fileName'],
// The path to the temporary file
'tmp_name' => $temp_filename,
];
}
/**
* Validate File
*
* @since 3.3.0
* @access private
*
* @param array $file
* @param array $file_extensions Optional
* @return bool|\WP_Error
*/
private function validate_file( array $file, $file_extensions = [] ) {
$uploaded_file_name = isset( $file['name'] ) ? $file['name'] : $file['tmp_name'];
$file_extension = pathinfo( $uploaded_file_name, PATHINFO_EXTENSION );
if ( ! $this->is_elementor_wp_media_upload() ) {
$is_file_type_allowed = $this->is_file_type_allowed( $file_extension, $file_extensions );
if ( is_wp_error( $is_file_type_allowed ) ) {
return $is_file_type_allowed;
}
}
$file_type_handler = $this->get_file_type_handlers( $file_extension );
// If Elementor does not have a handler for this file type, don't block it.
if ( ! $file_type_handler ) {
return true;
}
// If there is a File Type Handler for the uploaded file, it means it is a non-standard file type. In this case,
// we check if unfiltered file uploads are enabled or not before allowing it.
if ( ! self::are_unfiltered_uploads_enabled() ) {
$error = 'json' === $file_extension
? esc_html__( 'You do not have permission to upload JSON files.', 'elementor' )
: esc_html__( 'This file is not allowed for security reasons.', 'elementor' );
return new \WP_Error( Exceptions::FORBIDDEN, $error );
}
// Here is each file type handler's chance to run its own specific validations
return $file_type_handler->validate_file( $file );
}
/**
* Is File Type Allowed
*
* Checks whether the passed file extension is allowed for upload.
*
* @since 3.5.0
* @access private
*
* @param $file_extension
* @param $filtered_file_extensions
* @return bool|\WP_Error
*/
private function is_file_type_allowed( $file_extension, $filtered_file_extensions ) {
$allowed_file_extensions = $this->get_allowed_file_extensions();
if ( $filtered_file_extensions ) {
$allowed_file_extensions = array_intersect( $allowed_file_extensions, $filtered_file_extensions );
}
$is_allowed = false;
// Check if the file type (extension) is in the allowed extensions list. If it is a non-standard file type (not
// enabled by default in WordPress) and unfiltered file uploads are not enabled, it will not be in the allowed
// file extensions list.
foreach ( $allowed_file_extensions as $allowed_extension ) {
if ( preg_match( '/' . $allowed_extension . '/', $file_extension ) ) {
$is_allowed = true;
break;
}
}
if ( ! $is_allowed ) {
$is_allowed = new \WP_Error( Exceptions::FORBIDDEN, 'Uploading this file type is not allowed.' );
}
/**
* Elementor File Type Allowed
*
* Allows setting file types
*
* @since 3.5.0
*
* @param bool|\WP_Error $is_allowed
*/
return apply_filters( 'elementor/files/allow-file-type/' . $file_extension, $is_allowed );
}
/**
* Remove Directory with Files
*
* @since 3.3.0
* @access private
*
* @param string $dir
* @return bool
*/
private function remove_directory_with_files( $dir ) {
$dir_iterator = new \RecursiveDirectoryIterator( $dir, \RecursiveDirectoryIterator::SKIP_DOTS );
foreach ( new \RecursiveIteratorIterator( $dir_iterator, \RecursiveIteratorIterator::CHILD_FIRST ) as $name => $item ) {
if ( is_dir( $name ) ) {
rmdir( $name );
} elseif ( is_file( $name ) ) {
unlink( $name );
}
}
return rmdir( $dir );
}
/**
* Get Allowed File Extensions
*
* Retrieve an array containing the list of file extensions allowed for upload.
*
* @since 3.3.0
* @access private
*
* @return array file extension/s
*/
private function get_allowed_file_extensions() {
if ( ! $this->allowed_file_extensions ) {
$this->allowed_file_extensions = array_keys( get_allowed_mime_types() );
foreach ( $this->get_file_type_handlers() as $file_type => $handler ) {
if ( $handler->is_upload_allowed() ) {
// Add the file extension to the allowed extensions list only if unfiltered files upload is enabled.
$this->add_file_extension_to_allowed_extensions_list( $file_type );
}
}
}
return $this->allowed_file_extensions;
}
public function __construct() {
$this->register_file_types();
add_filter( 'upload_mimes', [ $this, 'support_unfiltered_elementor_file_uploads' ] );
add_filter( 'wp_handle_upload_prefilter', [ $this, 'handle_elementor_wp_media_upload' ] );
add_filter( 'wp_check_filetype_and_ext', [ $this, 'check_filetype_and_ext' ], 10, 4 );
// Ajax.
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
}
}
files/manager.php 0000644 00000011412 14720700033 0007764 0 ustar 00 register_actions();
}
public function get( $class, $args ) {
$id = $class . '-' . wp_json_encode( $args );
if ( ! isset( $this->files[ $id ] ) ) {
// Create an instance from dynamic args length.
$reflection_class = new \ReflectionClass( $class );
$this->files[ $id ] = $reflection_class->newInstanceArgs( $args );
}
return $this->files[ $id ];
}
/**
* On post delete.
*
* Delete post CSS immediately after a post is deleted from the database.
*
* Fired by `deleted_post` action.
*
* @since 1.2.0
* @access public
*
* @param string $post_id Post ID.
*/
public function on_delete_post( $post_id ) {
if ( ! Utils::is_post_support( $post_id ) ) {
return;
}
$css_file = Post_CSS::create( $post_id );
$css_file->delete();
}
/**
* On export post meta.
*
* When exporting data using WXR, skip post CSS file meta key. This way the
* export won't contain the post CSS file data used by Elementor.
*
* Fired by `wxr_export_skip_postmeta` filter.
*
* @since 1.2.0
* @access public
*
* @param bool $skip Whether to skip the current post meta.
* @param string $meta_key Current meta key.
*
* @return bool Whether to skip the post CSS meta.
*/
public function on_export_post_meta( $skip, $meta_key ) {
if ( Post_CSS::META_KEY === $meta_key ) {
$skip = true;
}
return $skip;
}
/**
* Clear cache.
*
* Delete all meta containing files data. And delete the actual
* files from the upload directory.
*
* @since 1.2.0
* @access public
*/
public function clear_cache() {
// Delete files.
$path = Base::get_base_uploads_dir() . Base::DEFAULT_FILES_DIR . '*';
foreach ( glob( $path ) as $file_path ) {
unlink( $file_path );
}
delete_post_meta_by_key( Post_CSS::META_KEY );
delete_post_meta_by_key( Document_Base::CACHE_META_KEY );
delete_post_meta_by_key( Assets::ASSETS_META_KEY );
delete_option( Global_CSS::META_KEY );
delete_option( Frontend::META_KEY );
$this->reset_assets_data();
/**
* Elementor clear files.
*
* Fires after Elementor clears files
*
* @since 2.1.0
*/
do_action( 'elementor/core/files/clear_cache' );
}
public function clear_custom_image_sizes() {
if ( ! defined( 'BFITHUMB_UPLOAD_DIR' ) ) {
return;
}
$upload_info = wp_upload_dir();
$upload_dir = $upload_info['basedir'] . '/' . BFITHUMB_UPLOAD_DIR;
$path = $upload_dir . '/*';
foreach ( glob( $path ) as $file_path ) {
unlink( $file_path );
}
}
/**
* Register Ajax Actions
*
* Deprecated - use the Uploads Manager instead.
*
* @deprecated 3.5.0
*
* @param Ajax $ajax
*/
public function register_ajax_actions( Ajax $ajax ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
Plugin::$instance->uploads_manager->register_ajax_actions( $ajax );
}
/**
* Ajax Unfiltered Files Upload
*
* Deprecated - use the Uploads Manager instead.
*
* @deprecated 3.5.0
*/
public function ajax_unfiltered_files_upload() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
Plugin::$instance->uploads_manager->enable_unfiltered_files_upload();
}
/**
* Register actions.
*
* Register filters and actions for the files manager.
*
* @since 1.2.0
* @access private
*/
private function register_actions() {
add_action( 'deleted_post', [ $this, 'on_delete_post' ] );
add_filter( 'wxr_export_skip_postmeta', [ $this, 'on_export_post_meta' ], 10, 2 );
add_action( 'update_option_home', function () {
$this->reset_assets_data();
} );
add_action( 'update_option_siteurl', function () {
$this->reset_assets_data();
} );
}
/**
* Reset Assets Data.
*
* Reset the page assets data.
*
* @since 3.3.0
* @access private
*/
private function reset_assets_data() {
delete_option( Page_Assets_Data_Manager::ASSETS_DATA_KEY );
}
}
files/assets/svg/svg-handler.php 0000644 00000015541 14720700033 0012674 0 ustar 00 uploads_manager->get_file_type_handlers( 'svg' );`
*/
class Svg_Handler extends Files_Upload_Handler {
/**
* Inline svg attachment meta key
*
* @deprecated 3.5.0
*/
const META_KEY = '_elementor_inline_svg';
/**
* @deprecated 3.5.0
*/
const SCRIPT_REGEX = '/(?:\w+script|data):/xi';
/**
* Attachment ID.
*
* Holds the current attachment ID.
*
* @deprecated 3.5.0
*
* @var int
*/
private $attachment_id;
/**
* @deprecated 3.5.0
*/
public static function get_name() {
return 'svg-handler';
}
/**
* get_meta
*
* @deprecated 3.5.0
*
* @return mixed
*/
protected function get_meta() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
return get_post_meta( $this->attachment_id, self::META_KEY, true );
}
/**
* update_meta
*
* @deprecated 3.5.0
*
* @param $meta
*/
protected function update_meta( $meta ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
update_post_meta( $this->attachment_id, self::META_KEY, $meta );
}
/**
* delete_meta
*
* @deprecated 3.5.0
*/
protected function delete_meta() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
delete_post_meta( $this->attachment_id, self::META_KEY );
}
/**
* @deprecated 3.5.0
*/
public function get_mime_type() {
return 'image/svg+xml';
}
/**
* @deprecated 3.5.0
*/
public function get_file_type() {
return 'svg';
}
/**
* delete_meta_cache
*
* @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->delete_meta_cache()` instead.
*/
public function delete_meta_cache() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->delete_meta_cache()' );
/** @var Svg $svg_handler */
$svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );
$svg_handler->delete_meta_cache();
}
/**
* get_inline_svg
*
* @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg::get_inline_svg()` instead.
*
* @param $attachment_id
*
* @return bool|mixed|string
*/
public static function get_inline_svg( $attachment_id ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Core\Files\File_Types\Svg::get_inline_svg()' );
return Svg::get_inline_svg( $attachment_id );
}
/**
* sanitize_svg
*
* @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->delete_meta_cache()->sanitize_svg()` instead.
*
* @param $filename
*
* @return bool
*/
public function sanitize_svg( $filename ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->delete_meta_cache()->sanitize_svg()' );
/** @var Svg $svg_handler */
$svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );
return $svg_handler->sanitize_svg( $filename );
}
/**
* sanitizer
*
* @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->sanitizer()` instead.
*
* @param $content
*
* @return bool|string
*/
public function sanitizer( $content ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->sanitizer()' );
/** @var Svg $svg_handler */
$svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );
return $svg_handler->sanitizer( $content );
}
/**
* wp_prepare_attachment_for_js
*
* @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->wp_prepare_attachment_for_js()` instead.
*
* @param $attachment_data
* @param $attachment
* @param $meta
*
* @return mixed
*/
public function wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->wp_prepare_attachment_for_js()' );
/** @var Svg $svg_handler */
$svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );
return $svg_handler->wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta );
}
/**
* set_attachment_id
*
* @deprecated 3.5.0
*
* @param $attachment_id
*
* @return int
*/
public function set_attachment_id( $attachment_id ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
$this->attachment_id = $attachment_id;
return $this->attachment_id;
}
/**
* get_attachment_id
*
* @deprecated 3.5.0
*
* @return int
*/
public function get_attachment_id() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
return $this->attachment_id;
}
/**
* set_svg_meta_data
*
* @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->set_svg_meta_data()` instead.
*
* @return mixed
*/
public function set_svg_meta_data( $data, $id ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->set_svg_meta_data()' );
/** @var Svg $svg_handler */
$svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' );
return $svg_handler->set_svg_meta_data( $data, $id );
}
/**
* handle_upload_prefilter
*
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()` instead.
*
* @param $file
*
* @return mixed
*/
public function handle_upload_prefilter( $file ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()' );
return Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload( $file );
}
}
files/assets/files-upload-handler.php 0000644 00000010527 14720700033 0013661 0 ustar 00 uploads_manager->are_unfiltered_uploads_enabled()` instead.
*
* @return bool
*/
private function is_elementor_media_upload() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()' );
return Plugin::$instance->uploads_manager->is_elementor_media_upload();
}
/**
* Is Enabled
*
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()` instead.
*
* @return bool
*/
final public static function is_enabled() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()' );
return Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled();
}
/**
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()` instead.
*/
final public function support_unfiltered_files_upload( $existing_mimes ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->support_unfiltered_file_uploads()' );
return Plugin::$instance->uploads_manager->support_unfiltered_elementor_file_uploads( $existing_mimes );
}
/**
* handle_upload_prefilter
*
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()` instead.
*
* @param $file
*
* @return mixed
*/
public function handle_upload_prefilter( $file ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()' );
return Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload( $file );
}
/**
* is_file_should_handled
*
* @deprecated 3.5.0
*
* @param $file
*
* @return bool
*/
protected function is_file_should_handled( $file ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' );
$ext = pathinfo( $file['name'], PATHINFO_EXTENSION );
return $this->is_elementor_media_upload() && $this->get_file_type() === $ext;
}
/**
* file_sanitizer_can_run
*
* @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg::file_sanitizer_can_run()` instead.
*
* @return bool
*/
public static function file_sanitizer_can_run() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Core\Files\File_Types\Svg::file_sanitizer_can_run()' );
return Svg::file_sanitizer_can_run();
}
/**
* Check filetype and ext
*
* A workaround for upload validation which relies on a PHP extension (fileinfo)
* with inconsistent reporting behaviour.
* ref: https://core.trac.wordpress.org/ticket/39550
* ref: https://core.trac.wordpress.org/ticket/40175
*
* @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->check_filetype_and_ext()` instead.
*
* @param $data
* @param $file
* @param $filename
* @param $mimes
*
* @return mixed
*/
public function check_filetype_and_ext( $data, $file, $filename, $mimes ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->check_filetype_and_ext()' );
Plugin::$instance->uploads_manager->check_filetype_and_ext( $data, $file, $filename, $mimes );
}
}
files/assets/manager.php 0000644 00000002325 14720700033 0011271 0 ustar 00 register_asset_types();
/**
* Elementor files assets registered.
*
* Fires after Elementor registers assets types
*
* @since 2.6.0
*/
do_action( 'elementor/core/files/assets/assets_registered', $this );
}
public function get_asset( $name ) {
return isset( $this->asset_types[ $name ] ) ? $this->asset_types[ $name ] : false;
}
/**
* Add Asset
* @param $instance
*/
public function add_asset( $instance ) {
$this->asset_types[ $instance::get_name() ] = $instance;
}
/**
* Register Asset Types
*
* Registers Elementor Asset Types
*/
private function register_asset_types() {
$this->add_asset( new Svg_Handler() );
}
}
files/assets/json/json-handler.php 0000644 00000001302 14720700033 0013206 0 ustar 00 uploads_manager->get_file_type_handlers( 'svg' );`
*/
class Json_Handler extends Files_Upload_Handler {
/**
* @deprecated 3.5.0
*/
public static function get_name() {
return 'json-handler';
}
/**
* @deprecated 3.5.0
*/
public function get_mime_type() {
return 'application/json';
}
/**
* @deprecated 3.5.0
*/
public function get_file_type() {
return 'json';
}
}
document-types/page.php 0000644 00000002050 14720700033 0011142 0 ustar 00 [
'title' => esc_html__( 'Single', 'elementor' ),
'active' => false,
'promotion' => [
'url' => esc_url( 'https://go.elementor.com/go-pro-section-single-widget-panel/' ),
],
],
]
);
}
/**
* @since 2.0.0
* @access public
*/
public function get_css_wrapper_selector() {
return 'body.elementor-page-' . $this->get_main_id();
}
/**
* @since 3.1.0
* @access protected
*/
protected function register_controls() {
parent::register_controls();
static::register_hide_title_control( $this );
static::register_post_fields_control( $this );
static::register_style_controls( $this );
}
/**
* @since 2.0.0
* @access public
* @static
* @param Document $document
*/
public static function register_hide_title_control( $document ) {
$document->start_injection( [
'of' => 'post_status',
'fallback' => [
'of' => 'post_title',
],
] );
$document->add_control(
'hide_title',
[
'label' => esc_html__( 'Hide Title', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'description' => sprintf(
/* translators: 1: Link open tag, 2: Link close tag. */
esc_html__( 'Set a different selector for the title in the %1$sLayout panel%2$s.', 'elementor' ),
'',
''
),
'separator' => 'before',
'selectors' => [
':root' => '--page-title-display: none',
],
]
);
$document->end_injection();
}
/**
* @since 2.0.0
* @access public
* @static
* @param Document $document
*/
public static function register_style_controls( $document ) {
$document->start_controls_section(
'section_page_style',
[
'label' => esc_html__( 'Body Style', 'elementor' ),
'tab' => Controls_Manager::TAB_STYLE,
]
);
$document->add_responsive_control(
'margin',
[
'label' => esc_html__( 'Margin', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'selectors' => [
'{{WRAPPER}}' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}',
],
]
);
$document->add_responsive_control(
'padding',
[
'label' => esc_html__( 'Padding', 'elementor' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'selectors' => [
'{{WRAPPER}}' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}',
],
]
);
$document->add_group_control(
Group_Control_Background::get_type(),
[
'name' => 'background',
'separator' => 'before',
'fields_options' => [
'image' => [
// Currently isn't supported.
'dynamic' => [
'active' => false,
],
],
],
]
);
$document->end_controls_section();
Plugin::$instance->controls_manager->add_custom_css_controls( $document );
}
public static function get_labels() : array {
$plural_label = static::get_plural_title();
$singular_label = static::get_title();
$labels = [
'name' => $plural_label, // Already translated.
'singular_name' => $singular_label, // Already translated.
'all_items' => sprintf( __( 'All %s', 'elementor' ), $plural_label ),
'add_new' => esc_html__( 'Add New', 'elementor' ),
'add_new_item' => sprintf( __( 'Add New %s', 'elementor' ), $singular_label ),
'edit_item' => sprintf( __( 'Edit %s', 'elementor' ), $singular_label ),
'new_item' => sprintf( __( 'New %s', 'elementor' ), $singular_label ),
'view_item' => sprintf( __( 'View %s', 'elementor' ), $singular_label ),
'search_items' => sprintf( __( 'Search %s', 'elementor' ), $plural_label ),
'not_found' => sprintf( __( 'No %s found.', 'elementor' ), strtolower( $plural_label ) ),
'not_found_in_trash' => sprintf( __( 'No %s found in Trash.', 'elementor' ), strtolower( $plural_label ) ),
'parent_item_colon' => '',
'menu_name' => $plural_label,
];
return $labels;
}
/**
* @since 2.0.0
* @access public
* @static
* @param Document $document
*/
public static function register_post_fields_control( $document ) {
$document->start_injection( [
'of' => 'post_status',
'fallback' => [
'of' => 'post_title',
],
] );
if ( post_type_supports( $document->post->post_type, 'excerpt' ) ) {
$document->add_control(
'post_excerpt',
[
'label' => esc_html__( 'Excerpt', 'elementor' ),
'type' => Controls_Manager::TEXTAREA,
'default' => $document->post->post_excerpt,
'separator' => 'before',
'ai' => [
'type' => 'excerpt',
],
]
);
}
if ( current_theme_supports( 'post-thumbnails' ) && post_type_supports( $document->post->post_type, 'thumbnail' ) ) {
$document->add_control(
'post_featured_image',
[
'label' => esc_html__( 'Featured Image', 'elementor' ),
'type' => Controls_Manager::MEDIA,
'default' => [
'id' => get_post_thumbnail_id(),
'url' => (string) get_the_post_thumbnail_url( $document->post->ID ),
],
'separator' => 'before',
]
);
}
if ( is_post_type_hierarchical( $document->post->post_type ) ) {
$document->add_control(
'menu_order',
[
'label' => esc_html__( 'Order', 'elementor' ),
'type' => Controls_Manager::NUMBER,
'default' => $document->post->menu_order,
'separator' => 'before',
]
);
}
if ( post_type_supports( $document->post->post_type, 'comments' ) ) {
$document->add_control(
'comment_status',
[
'label' => esc_html__( 'Allow Comments', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'return_value' => 'open',
'default' => $document->post->comment_status,
'separator' => 'before',
]
);
}
$document->end_injection();
}
/**
* @since 2.0.0
* @access public
*
* @param array $data
*
* @throws \Exception
*/
public function __construct( array $data = [] ) {
if ( $data ) {
$template = get_post_meta( $data['post_id'], '_wp_page_template', true );
if ( empty( $template ) ) {
$template = 'default';
}
$data['settings']['template'] = $template;
}
parent::__construct( $data );
}
protected function get_remote_library_config() {
$config = parent::get_remote_library_config();
$config['category'] = '';
$config['type'] = 'block';
$config['default_route'] = 'templates/blocks';
return $config;
}
}
document-types/post.php 0000644 00000002050 14720700033 0011213 0 ustar 00 get_settings();
ob_start();
$this->render();
$value = ob_get_clean();
if ( ! Utils::is_empty( $value ) ) {
// TODO: fix spaces in `before`/`after` if WRAPPED_TAG ( conflicted with .elementor-tag { display: inline-flex; } );
if ( ! Utils::is_empty( $settings, 'before' ) ) {
$value = wp_kses_post( $settings['before'] ) . $value;
}
if ( ! Utils::is_empty( $settings, 'after' ) ) {
$value .= wp_kses_post( $settings['after'] );
}
if ( static::WRAPPED_TAG ) :
$value = '' . $value . '';
endif;
} elseif ( ! Utils::is_empty( $settings, 'fallback' ) ) {
$value = wp_kses_post_deep( $settings['fallback'] );
}
return $value;
}
/**
* @since 2.0.0
* @access public
*/
final public function get_content_type() {
return 'ui';
}
/**
* @since 2.0.9
* @access public
*/
public function get_editor_config() {
$config = parent::get_editor_config();
$config['wrapped_tag'] = $this::WRAPPED_TAG;
return $config;
}
/**
* @since 2.0.0
* @access protected
*/
protected function register_advanced_section() {
$this->start_controls_section(
'advanced',
[
'label' => esc_html__( 'Advanced', 'elementor' ),
]
);
$this->add_control(
'before',
[
'label' => esc_html__( 'Before', 'elementor' ),
'ai' => [
'active' => false,
],
]
);
$this->add_control(
'after',
[
'label' => esc_html__( 'After', 'elementor' ),
'ai' => [
'active' => false,
],
]
);
$this->add_control(
'fallback',
[
'label' => esc_html__( 'Fallback', 'elementor' ),
'ai' => [
'active' => false,
],
]
);
$this->end_controls_section();
}
}
dynamic-tags/manager.php 0000644 00000026121 14720700033 0011245 0 ustar 00 add_actions();
}
/**
* Parse dynamic tags text.
*
* Receives the dynamic tag text, and returns a single value or multiple values
* from the tag callback function.
*
* @since 2.0.0
* @access public
*
* @param string $text Dynamic tag text.
* @param array $settings The dynamic tag settings.
* @param callable $parse_callback The functions that renders the dynamic tag.
*
* @return string|string[]|mixed A single string or an array of strings with
* the return values from each tag callback
* function.
*/
public function parse_tags_text( $text, array $settings, callable $parse_callback ) {
if ( ! empty( $settings['returnType'] ) && 'object' === $settings['returnType'] ) {
$value = $this->parse_tag_text( $text, $settings, $parse_callback );
} else {
$value = preg_replace_callback( '/\[' . self::TAG_LABEL . '.+?(?=\])\]/', function( $tag_text_match ) use ( $settings, $parse_callback ) {
return $this->parse_tag_text( $tag_text_match[0], $settings, $parse_callback );
}, $text );
}
return $value;
}
/**
* Parse dynamic tag text.
*
* Receives the dynamic tag text, and returns the value from the callback
* function.
*
* @since 2.0.0
* @access public
*
* @param string $tag_text Dynamic tag text.
* @param array $settings The dynamic tag settings.
* @param callable $parse_callback The functions that renders the dynamic tag.
*
* @return string|array|mixed If the tag was not found an empty string or an
* empty array will be returned, otherwise the
* return value from the tag callback function.
*/
public function parse_tag_text( $tag_text, array $settings, callable $parse_callback ) {
$tag_data = $this->tag_text_to_tag_data( $tag_text );
if ( ! $tag_data ) {
if ( ! empty( $settings['returnType'] ) && 'object' === $settings['returnType'] ) {
return [];
}
return '';
}
return call_user_func_array( $parse_callback, array_values( $tag_data ) );
}
/**
* @since 2.0.0
* @access public
*
* @param string $tag_text
*
* @return array|null
*/
public function tag_text_to_tag_data( $tag_text ) {
preg_match( '/id="(.*?(?="))"/', $tag_text, $tag_id_match );
preg_match( '/name="(.*?(?="))"/', $tag_text, $tag_name_match );
preg_match( '/settings="(.*?(?="]))/', $tag_text, $tag_settings_match );
if ( ! $tag_id_match || ! $tag_name_match || ! $tag_settings_match ) {
return null;
}
return [
'id' => $tag_id_match[1],
'name' => $tag_name_match[1],
'settings' => json_decode( urldecode( $tag_settings_match[1] ), true ),
];
}
/**
* Dynamic tag to text.
*
* Retrieve the shortcode that represents the dynamic tag.
*
* @since 2.0.0
* @access public
*
* @param Base_Tag $tag An instance of the dynamic tag.
*
* @return string The shortcode that represents the dynamic tag.
*/
public function tag_to_text( Base_Tag $tag ) {
return sprintf( '[%1$s id="%2$s" name="%3$s" settings="%4$s"]', self::TAG_LABEL, $tag->get_id(), $tag->get_name(), urlencode( wp_json_encode( $tag->get_settings(), JSON_FORCE_OBJECT ) ) );
}
/**
* @since 2.0.0
* @access public
* @param string $tag_id
* @param string $tag_name
* @param array $settings
*
* @return string
*/
public function tag_data_to_tag_text( $tag_id, $tag_name, array $settings = [] ) {
$tag = $this->create_tag( $tag_id, $tag_name, $settings );
if ( ! $tag ) {
return '';
}
return $this->tag_to_text( $tag );
}
/**
* @since 2.0.0
* @access public
* @param string $tag_id
* @param string $tag_name
* @param array $settings
*
* @return Tag|null
*/
public function create_tag( $tag_id, $tag_name, array $settings = [] ) {
$tag_info = $this->get_tag_info( $tag_name );
if ( ! $tag_info ) {
return null;
}
$tag_class = $tag_info['class'];
return new $tag_class( [
'settings' => $settings,
'id' => $tag_id,
] );
}
/**
* @since 2.0.0
* @access public
*
* @param $tag_id
* @param $tag_name
* @param array $settings
*
* @return null|string
*/
public function get_tag_data_content( $tag_id, $tag_name, array $settings = [] ) {
if ( self::MODE_REMOVE === $this->parsing_mode ) {
return null;
}
$tag = $this->create_tag( $tag_id, $tag_name, $settings );
if ( ! $tag ) {
return null;
}
return $tag->get_content();
}
/**
* @since 2.0.0
* @access public
*
* @param $tag_name
*
* @return mixed|null
*/
public function get_tag_info( $tag_name ) {
$tags = $this->get_tags();
if ( empty( $tags[ $tag_name ] ) ) {
return null;
}
return $tags[ $tag_name ];
}
/**
* @since 2.0.9
* @access public
*/
public function get_tags() {
if ( ! did_action( 'elementor/dynamic_tags/register_tags' ) ) {
/**
* Register dynamic tags.
*
* Fires when Elementor registers dynamic tags.
*
* @since 2.0.9
* @deprecated 3.5.0 Use `elementor/dynamic_tags/register` hook instead.
*
* @param Manager $this Dynamic tags manager.
*/
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action(
'elementor/dynamic_tags/register_tags',
[ $this ],
'3.5.0',
'elementor/dynamic_tags/register'
);
}
if ( ! did_action( 'elementor/dynamic_tags/register' ) ) {
/**
* Register dynamic tags.
*
* Fires when Elementor registers dynamic tags.
*
* @since 3.5.0
*
* @param Manager $this Dynamic tags manager.
*/
do_action( 'elementor/dynamic_tags/register', $this );
}
return $this->tags_info;
}
/**
* @since 2.0.0
* @access public
* @deprecated 3.5.0 Use `register()` method instead.
*
* @param string $class
*/
public function register_tag( $class ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
__METHOD__,
'3.5.0',
'register()'
);
/** @var Base_Tag $tag */
$instance = new $class();
$this->register( $instance );
}
/**
* Register a new Dynamic Tag.
*
* @param Base_Tag $dynamic_tag_instance
*
* @return void
* @since 3.5.0
* @access public
*
*/
public function register( Base_Tag $dynamic_tag_instance ) {
$this->tags_info[ $dynamic_tag_instance->get_name() ] = [
'class' => get_class( $dynamic_tag_instance ),
'instance' => $dynamic_tag_instance,
];
}
/**
* @since 2.0.9
* @access public
* @deprecated 3.5.0 Use `unregister()` method instead.
*
* @param string $tag_name
*/
public function unregister_tag( $tag_name ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
__METHOD__,
'3.5.0',
'unregister()'
);
$this->unregister( $tag_name );
}
/**
* Unregister a dynamic tag.
*
* @since 3.5.0
* @access public
*
* @param string $tag_name Dynamic Tag name to unregister.
*
* @return void
*/
public function unregister( $tag_name ) {
unset( $this->tags_info[ $tag_name ] );
}
/**
* @since 2.0.0
* @access public
*
* @param $group_name
* @param array $group_settings
*/
public function register_group( $group_name, array $group_settings ) {
$default_group_settings = [
'title' => '',
];
$group_settings = array_merge( $default_group_settings, $group_settings );
$this->tags_groups[ $group_name ] = $group_settings;
}
/**
* @since 2.0.0
* @access public
*/
public function print_templates() {
foreach ( $this->get_tags() as $tag_name => $tag_info ) {
$tag = $tag_info['instance'];
if ( ! $tag instanceof Tag ) {
continue;
}
$tag->print_template();
}
}
/**
* @since 2.0.0
* @access public
*/
public function get_tags_config() {
$config = [];
foreach ( $this->get_tags() as $tag_name => $tag_info ) {
/** @var Tag $tag */
$tag = $tag_info['instance'];
$config[ $tag_name ] = $tag->get_editor_config();
}
return $config;
}
/**
* @since 2.0.0
* @access public
*/
public function get_config() {
return [
'tags' => $this->get_tags_config(),
'groups' => $this->tags_groups,
];
}
/**
* @since 2.0.0
* @access public
*
* @throws \Exception If post ID is missing.
* @throws \Exception If current user don't have permissions to edit the post.
*/
public function ajax_render_tags( $data ) {
if ( empty( $data['post_id'] ) ) {
throw new \Exception( 'Missing post id.' );
}
if ( ! User::is_current_user_can_edit( $data['post_id'] ) ) {
throw new \Exception( 'Access denied.' );
}
Plugin::$instance->db->switch_to_post( $data['post_id'] );
/**
* Before dynamic tags rendered.
*
* Fires before Elementor renders the dynamic tags.
*
* @since 2.0.0
*/
do_action( 'elementor/dynamic_tags/before_render' );
$tags_data = [];
foreach ( $data['tags'] as $tag_key ) {
$tag_key_parts = explode( '-', $tag_key );
$tag_name = base64_decode( $tag_key_parts[0] );
$tag_settings = json_decode( urldecode( base64_decode( $tag_key_parts[1] ) ), true );
$tag = $this->create_tag( null, $tag_name, $tag_settings );
$tags_data[ $tag_key ] = $tag->get_content();
}
/**
* After dynamic tags rendered.
*
* Fires after Elementor renders the dynamic tags.
*
* @since 2.0.0
*/
do_action( 'elementor/dynamic_tags/after_render' );
return $tags_data;
}
/**
* @since 2.0.0
* @access public
*
* @param $mode
*/
public function set_parsing_mode( $mode ) {
$this->parsing_mode = $mode;
}
/**
* @since 2.0.0
* @access public
*/
public function get_parsing_mode() {
return $this->parsing_mode;
}
/**
* @since 2.1.0
* @access public
* @param Post $css_file
*/
public function after_enqueue_post_css( $css_file ) {
$post_id = $css_file->get_post_id();
$should_enqueue = apply_filters( 'elementor/css-file/dynamic/should_enqueue', true, $post_id );
if ( $should_enqueue ) {
$css_file = Dynamic_CSS::create( $post_id, $css_file );
$css_file->enqueue();
}
}
/**
* @since 2.3.0
* @access public
*/
public function register_ajax_actions( Ajax $ajax ) {
$ajax->register_ajax_action( 'render_tags', [ $this, 'ajax_render_tags' ] );
}
/**
* @since 2.0.0
* @access private
*/
private function add_actions() {
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
add_action( 'elementor/css-file/post/enqueue', [ $this, 'after_enqueue_post_css' ] );
}
}
dynamic-tags/data-tag.php 0000644 00000001363 14720700033 0011316 0 ustar 00 get_value( $options );
}
}
dynamic-tags/base-tag.php 0000644 00000007656 14720700033 0011332 0 ustar 00 print_panel_template();
$panel_template = ob_get_clean();
return [
'name' => $this->get_name(),
'title' => $this->get_title(),
'panel_template' => $panel_template,
'categories' => $this->get_categories(),
'group' => $this->get_group(),
'controls' => $this->get_controls(),
'content_type' => $this->get_content_type(),
'settings_required' => $this->is_settings_required(),
'editable' => $this->is_editable(),
];
}
/**
* @since 2.0.0
* @access public
*/
public function print_panel_template() {
$panel_template_setting_key = $this->get_panel_template_setting_key();
if ( ! $panel_template_setting_key ) {
return;
}
?><#
var key = ;
if ( key ) {
var settingsKey = "";
/*
* If the tag has controls,
* and key is an existing control (and not an old one),
* and the control has options (select/select2),
* and the key is an existing option (and not in a group or an old one).
*/
if ( controls && controls[settingsKey] ) {
var controlSettings = controls[settingsKey];
if ( controlSettings.options && controlSettings.options[ key ] ) {
key = controlSettings.options[ key ];
} else if ( controlSettings.groups ) {
var label = _.filter( _.pluck( _.pluck( controls.key.groups, 'options' ), key ) );
if ( label[0] ) {
key = label[0];
}
}
}
print( '(' + _.escape( key ) + ')' );
}
#>
get_name();
}
/**
* @since 2.0.0
* @access protected
*/
protected function register_advanced_section() {}
/**
* @since 2.0.0
* @access protected
*/
final protected function init_controls() {
Plugin::$instance->controls_manager->open_stack( $this );
$this->start_controls_section( 'settings', [
'label' => esc_html__( 'Settings', 'elementor' ),
] );
if ( $this->has_own_method( '_register_controls' ) ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_register_controls', '3.1.0', __CLASS__ . '::register_controls()' );
$this->_register_controls();
} else {
$this->register_controls();
}
$this->end_controls_section();
// If in fact no controls were registered, empty the stack
if ( 1 === count( Plugin::$instance->controls_manager->get_stacks( $this->get_unique_name() )['controls'] ) ) {
Plugin::$instance->controls_manager->open_stack( $this );
}
$this->register_advanced_section();
}
}
dynamic-tags/dynamic-css.php 0000644 00000005731 14720700033 0012051 0 ustar 00 post_dynamic_elements_ids ) ) {
return null;
}
return $this->post_id_for_data;
}
protected function is_global_parsing_supported() {
return false;
}
protected function render_styles( Element_Base $element ) {
$id = $element->get_id();
if ( in_array( $id, $this->post_dynamic_elements_ids ) ) {
parent::render_styles( $element );
}
foreach ( $element->get_children() as $child_element ) {
$this->render_styles( $child_element );
}
}
/**
* Dynamic_CSS constructor.
*
* @since 2.0.13
* @access public
*
* @param int $post_id Post ID
* @param Post_CSS $post_css_file
*/
public function __construct( $post_id, Post_CSS $post_css_file ) {
if ( $post_css_file instanceof Post_Preview ) {
$this->post_id_for_data = $post_css_file->get_post_id_for_data();
} else {
$this->post_id_for_data = $post_id;
}
$this->post_dynamic_elements_ids = $post_css_file->get_meta( 'dynamic_elements_ids' );
parent::__construct( $post_id );
}
/**
* @since 2.0.13
* @access public
*/
public function get_name() {
return 'dynamic';
}
/**
* Get Responsive Control Duplication Mode
*
* @since 3.4.0
*
* @return string
*/
protected function get_responsive_control_duplication_mode() {
return 'dynamic';
}
/**
* @since 2.0.13
* @access protected
*/
protected function use_external_file() {
return false;
}
/**
* @since 2.0.13
* @access protected
*/
protected function get_file_handle_id() {
return 'elementor-post-dynamic-' . $this->get_post_id_for_data();
}
/**
* @since 2.0.13
* @access public
*/
public function add_controls_stack_style_rules( Controls_Stack $controls_stack, array $controls, array $values, array $placeholders, array $replacements, array $all_controls = null ) {
$dynamic_settings = $controls_stack->get_settings( '__dynamic__' );
if ( ! empty( $dynamic_settings ) ) {
$controls = array_intersect_key( $controls, $dynamic_settings );
$all_controls = $controls_stack->get_controls();
$parsed_dynamic_settings = $controls_stack->parse_dynamic_settings( $values, $controls );
foreach ( $controls as $control ) {
if ( ! empty( $control['style_fields'] ) ) {
$this->add_repeater_control_style_rules( $controls_stack, $control, $values[ $control['name'] ], $placeholders, $replacements );
}
if ( empty( $control['selectors'] ) ) {
continue;
}
$this->add_control_style_rules( $control, $parsed_dynamic_settings, $all_controls, $placeholders, $replacements );
}
}
}
}
base/module.php 0000644 00000016611 14720700033 0007455 0 ustar 00 reflection ) {
$this->reflection = new \ReflectionClass( $this );
}
return $this->reflection;
}
/**
* Add module component.
*
* Add new component to the current module.
*
* @since 1.7.0
* @access public
*
* @param string $id Component ID.
* @param mixed $instance An instance of the component.
*/
public function add_component( $id, $instance ) {
$this->components[ $id ] = $instance;
}
/**
* @since 2.3.0
* @access public
* @return Module[]
*/
public function get_components() {
return $this->components;
}
/**
* Get module component.
*
* Retrieve the module component.
*
* @since 1.7.0
* @access public
*
* @param string $id Component ID.
*
* @return mixed An instance of the component, or `false` if the component
* doesn't exist.
*/
public function get_component( $id ) {
if ( isset( $this->components[ $id ] ) ) {
return $this->components[ $id ];
}
return false;
}
/**
* Get assets url.
*
* @since 2.3.0
* @access protected
*
* @param string $file_name
* @param string $file_extension
* @param string $relative_url Optional. Default is null.
* @param string $add_min_suffix Optional. Default is 'default'.
*
* @return string
*/
final protected function get_assets_url( $file_name, $file_extension, $relative_url = null, $add_min_suffix = 'default' ) {
static $is_test_mode = null;
if ( null === $is_test_mode ) {
$is_test_mode = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_TESTS;
}
if ( ! $relative_url ) {
$relative_url = $this->get_assets_relative_url() . $file_extension . '/';
}
$url = $this->get_assets_base_url() . $relative_url . $file_name;
if ( 'default' === $add_min_suffix ) {
$add_min_suffix = ! $is_test_mode;
}
if ( $add_min_suffix ) {
$url .= '.min';
}
return $url . '.' . $file_extension;
}
/**
* Get js assets url
*
* @since 2.3.0
* @access protected
*
* @param string $file_name
* @param string $relative_url Optional. Default is null.
* @param string $add_min_suffix Optional. Default is 'default'.
*
* @return string
*/
final protected function get_js_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default' ) {
return $this->get_assets_url( $file_name, 'js', $relative_url, $add_min_suffix );
}
/**
* Get css assets url
*
* @since 2.3.0
* @access protected
*
* @param string $file_name
* @param string $relative_url Optional. Default is null.
* @param string $add_min_suffix Optional. Default is 'default'.
* @param bool $add_direction_suffix Optional. Default is `false`
*
* @return string
*/
final protected function get_css_assets_url( $file_name, $relative_url = null, $add_min_suffix = 'default', $add_direction_suffix = false ) {
static $direction_suffix = null;
if ( ! $direction_suffix ) {
$direction_suffix = is_rtl() ? '-rtl' : '';
}
if ( $add_direction_suffix ) {
$file_name .= $direction_suffix;
}
return $this->get_assets_url( $file_name, 'css', $relative_url, $add_min_suffix );
}
/**
* Get Frontend File URL
*
* Returns the URL for the CSS file to be loaded in the front end. If requested via the second parameter, a custom
* file is generated based on a passed template file name. Otherwise, the URL for the default CSS file is returned.
*
* @since 3.24.0
*
* @access public
*
* @param string $file_name
* @param boolean $has_custom_breakpoints
*
* @return string frontend file URL
*/
public function get_frontend_file_url( $file_name, $has_custom_breakpoints ) {
return Plugin::$instance->frontend->get_frontend_file_url( $file_name, $has_custom_breakpoints );
}
/**
* Get assets base url
*
* @since 2.6.0
* @access protected
*
* @return string
*/
protected function get_assets_base_url() {
return ELEMENTOR_URL;
}
/**
* Get assets relative url
*
* @since 2.3.0
* @access protected
*
* @return string
*/
protected function get_assets_relative_url() {
return 'assets/';
}
/**
* Get the module's associated widgets.
*
* @return string[]
*/
protected function get_widgets() {
return [];
}
/**
* Initialize the module related widgets.
*/
public function init_widgets() {
$widget_manager = Plugin::instance()->widgets_manager;
foreach ( $this->get_widgets() as $widget ) {
$class_name = $this->get_reflection()->getNamespaceName() . '\Widgets\\' . $widget;
$widget_manager->register( new $class_name() );
}
}
public function __construct() {
add_action( 'elementor/widgets/register', [ $this, 'init_widgets' ] );
}
}
base/providers/social-network-provider.php 0000644 00000016233 14720700033 0014776 0 ustar 00 $data ) {
$icons[ $network ] = $data['icon'];
}
}
return $icons;
}
public static function get_icon_mapping( string $platform ): string {
static::init_social_networks_array_if_empty();
if ( isset( self::$social_networks[ $platform ]['icon'] ) ) {
return self::$social_networks[ $platform ]['icon'];
}
return '';
}
public static function get_name_mapping( string $platform ): string {
static::init_social_networks_array_if_empty();
if ( isset( self::$social_networks[ $platform ]['name'] ) ) {
return self::$social_networks[ $platform ]['name'];
}
return '';
}
public static function get_text_mapping( string $platform ): string {
static::init_social_networks_array_if_empty();
if ( isset( self::$social_networks[ $platform ]['text'] ) ) {
return self::$social_networks[ $platform ]['text'];
}
return '';
}
public static function get_social_networks_text( $providers = [] ): array {
static::init_social_networks_array_if_empty();
static $texts = [];
if ( empty( $texts ) ) {
foreach ( static::$social_networks as $network => $data ) {
$texts[ $network ] = $data['text'];
}
}
if ( $providers ) {
return array_intersect_key( $texts, array_flip( $providers ) );
}
return $texts;
}
private static function init_social_networks_array_if_empty(): void {
if ( ! empty( static::$social_networks ) ) {
return;
}
static::$social_networks[ static::VCF ] = [
'text' => esc_html__( 'Save contact (vCard)', 'elementor' ),
'icon' => 'fab fa-outlook',
'name' => 'vcf',
];
static::$social_networks[ static::FACEBOOK ] = [
'text' => esc_html__( 'Facebook', 'elementor' ),
'icon' => 'fab fa-facebook',
'name' => 'facebook',
];
static::$social_networks[ static::TWITTER ] = [
'text' => esc_html__( 'X (Twitter)', 'elementor' ),
'icon' => 'fab fa-x-twitter',
'name' => 'x-twitter',
];
static::$social_networks[ static::INSTAGRAM ] = [
'text' => esc_html__( 'Instagram', 'elementor' ),
'icon' => 'fab fa-instagram',
'name' => 'instagram',
];
static::$social_networks[ static::LINKEDIN ] = [
'text' => esc_html__( 'LinkedIn', 'elementor' ),
'icon' => 'fab fa-linkedin-in',
'name' => 'linkedin',
];
static::$social_networks[ static::PINTEREST ] = [
'text' => esc_html__( 'Pinterest', 'elementor' ),
'icon' => 'fab fa-pinterest',
'name' => 'pinterest',
];
static::$social_networks[ static::YOUTUBE ] = [
'text' => esc_html__( 'YouTube', 'elementor' ),
'icon' => 'fab fa-youtube',
'name' => 'youtube',
];
static::$social_networks[ static::TIKTOK ] = [
'text' => esc_html__( 'TikTok', 'elementor' ),
'icon' => 'fab fa-tiktok',
'name' => 'tiktok',
];
static::$social_networks[ static::WHATSAPP ] = [
'text' => esc_html__( 'WhatsApp', 'elementor' ),
'icon' => 'fab fa-whatsapp',
'name' => 'whatsapp',
];
static::$social_networks[ static::APPLEMUSIC ] = [
'text' => esc_html__( 'Apple Music', 'elementor' ),
'icon' => 'fa fa-music',
'name' => 'apple-music',
];
static::$social_networks[ static::SPOTIFY ] = [
'text' => esc_html__( 'Spotify', 'elementor' ),
'icon' => 'fab fa-spotify',
'name' => 'spotify',
];
static::$social_networks[ static::SOUNDCLOUD ] = [
'text' => esc_html__( 'SoundCloud', 'elementor' ),
'icon' => 'fab fa-soundcloud',
'name' => 'soundcloud',
];
static::$social_networks[ static::BEHANCE ] = [
'text' => esc_html__( 'Behance', 'elementor' ),
'icon' => 'fab fa-behance',
'name' => 'behance',
];
static::$social_networks[ static::DRIBBBLE ] = [
'text' => esc_html__( 'Dribbble', 'elementor' ),
'icon' => 'fab fa-dribbble',
'name' => 'dribble',
];
static::$social_networks[ static::VIMEO ] = [
'text' => esc_html__( 'Vimeo', 'elementor' ),
'icon' => 'fab fa-vimeo-v',
'name' => 'vimeo',
];
static::$social_networks[ static::WAZE ] = [
'text' => esc_html__( 'Waze', 'elementor' ),
'icon' => 'fab fa-waze',
'name' => 'waze',
];
static::$social_networks[ static::MESSENGER ] = [
'text' => esc_html__( 'Messenger', 'elementor' ),
'icon' => 'fab fa-facebook-messenger',
'name' => 'messenger',
];
static::$social_networks[ static::TELEPHONE ] = [
'text' => esc_html__( 'Telephone', 'elementor' ),
'icon' => 'fas fa-phone-alt',
'name' => 'phone',
];
static::$social_networks[ static::EMAIL ] = [
'text' => esc_html__( 'Email', 'elementor' ),
'icon' => 'fas fa-envelope',
'name' => 'email',
];
static::$social_networks[ static::URL ] = [
'text' => esc_html__( 'URL', 'elementor' ),
'icon' => 'fas fa-globe',
'name' => 'url',
];
static::$social_networks[ static::FILE_DOWNLOAD ] = [
'text' => esc_html__( 'File Download', 'elementor' ),
'icon' => 'fas fa-download',
'name' => 'download',
];
static::$social_networks[ static::SMS ] = [
'text' => esc_html__( 'SMS', 'elementor' ),
'icon' => 'fas fa-sms',
'name' => 'sms',
];
static::$social_networks[ static::VIBER ] = [
'text' => esc_html__( 'Viber', 'elementor' ),
'icon' => 'fab fa-viber',
'name' => 'viber',
];
static::$social_networks[ static::SKYPE ] = [
'text' => esc_html__( 'Skype', 'elementor' ),
'icon' => 'fab fa-skype',
'name' => 'skype',
];
}
public static function build_messenger_link( string $username ) {
return 'https://m.me/' . $username;
}
public static function build_email_link( array $data, string $prefix ) {
$email = $data[ $prefix . '_mail' ] ?? '';
$subject = $data[ $prefix . '_mail_subject' ] ?? '';
$body = $data[ $prefix . '_mail_body' ] ?? '';
if ( ! $email ) {
return '';
}
$link = 'mailto:' . $email;
if ( $subject ) {
$link .= '?subject=' . $subject;
}
if ( $body ) {
$link .= $subject ? '&' : '?';
$link .= 'body=' . $body;
}
return $link;
}
public static function build_viber_link( string $action, string $number ) {
if ( empty( $number ) ) {
return '';
}
return add_query_arg( [
'number' => urlencode( $number ),
], 'viber://' . $action );
}
}
base/traits/shared-widget-controls-trait.php 0000644 00000015766 14720700033 0015221 0 ustar 00 0,
'max' => 10,
'step' => 1,
];
protected function add_html_tag_control( string $name, string $default = 'h2' ): void {
$this->add_control(
$name,
[
'label' => esc_html__( 'HTML Tag', 'elementor' ),
'type' => Controls_Manager::SELECT,
'options' => [
'h1' => 'H1',
'h2' => 'H2',
'h3' => 'H3',
'h4' => 'H4',
'h5' => 'H5',
'h6' => 'H6',
'div' => 'div',
'span' => 'span',
'p' => 'p',
],
'default' => $default,
]
);
}
/**
* Remove any child arrays where all properties are empty
*/
protected function clean_array(
$input_array = []
) {
$output_array = array_filter( $input_array, function( $sub_array ) {
// Use array_filter on the sub array
$filtered_sub_array = array_filter( $sub_array, function( $val ) {
// Filter out empty or null values
return ! is_null( $val ) && '' !== $val;
} );
// A non-empty result means the sub array contains some non-empty value(s)
return ! empty( $filtered_sub_array );
} );
return $output_array;
}
protected function get_link_attributes(
$link = [],
$other_attributes = []
) {
$url_attrs = [];
$rel_string = '';
if ( ! empty( $link['url'] ) ) {
$url_attrs['href'] = esc_url( $link['url'] );
}
if ( ! empty( $link['is_external'] ) ) {
$url_attrs['target'] = '_blank';
$rel_string .= 'noopener ';
}
if ( ! empty( $link['nofollow'] ) ) {
$rel_string .= 'nofollow ';
}
if ( ! empty( $rel_string ) ) {
$url_attrs['rel'] = $rel_string;
}
/**
* Note - we deliberately merge $other_attributes second
* to allow overriding default attributes values such as a more formatted href
*/
$url_combined_attrs = array_merge(
$url_attrs,
$other_attributes,
Utils::parse_custom_attributes( $link['custom_attributes'] ?? '' ),
);
return $url_combined_attrs;
}
protected function add_icons_per_row_control(
string $name = 'icons_per_row',
$options = [
'2' => '2',
'3' => '3',
],
string $default = '3',
$label = '',
$selector_custom_property = '--e-link-in-bio-icon-columns'
): void {
if ( ! $label ) {
$label = esc_html__( 'Icons Per Row', 'elementor' );
}
$this->add_control(
$name,
[
'label' => $label,
'type' => Controls_Manager::SELECT,
'options' => $options,
'default' => $default,
'render_type' => 'template',
'selectors' => [
'{{WRAPPER}} .e-link-in-bio' => $selector_custom_property . ': {{VALUE}};',
],
]
);
}
protected function add_slider_control(
string $name,
array $args = []
): void {
$default_args = [
'type' => Controls_Manager::SLIDER,
'default' => [
'unit' => 'px',
],
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'range' => [
'px' => [
'min' => 0,
'max' => 100,
'step' => 1,
],
],
];
$this->add_control(
$name,
array_merge_recursive( $default_args, $args )
);
}
protected function add_borders_control(
string $prefix,
array $show_border_args = [],
array $border_width_args = [],
array $border_color_args = []
): void {
$show_border = [
'label' => esc_html__( 'Border', 'elementor' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'elementor' ),
'label_off' => esc_html__( 'No', 'elementor' ),
'return_value' => 'yes',
'default' => '',
];
$this->add_control(
$prefix . '_show_border',
array_merge( $show_border, $show_border_args )
);
$condition = [
$prefix . '_show_border' => 'yes',
];
if ( isset( $border_width_args['condition'] ) ) {
$condition = array_merge( $condition, $border_width_args['condition'] );
unset( $border_width_args['condition'] );
}
$border_width = [
'label' => esc_html__( 'Border Width', 'elementor' ) . ' (px)',
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px' ],
'range' => [
'px' => $this->border_width_range,
],
'condition' => $condition,
'default' => [
'unit' => 'px',
'size' => 1,
],
];
$this->add_responsive_control(
$prefix . '_border_width',
array_merge( $border_width, $border_width_args ),
);
$condition = [
$prefix . '_show_border' => 'yes',
];
if ( isset( $border_color_args['condition'] ) ) {
$condition = array_merge( $condition, $border_color_args['condition'] );
unset( $border_color_args['condition'] );
}
$border_color = [
'label' => esc_html__( 'Border Color', 'elementor' ),
'type' => Controls_Manager::COLOR,
'condition' => $condition,
'default' => '#000000',
];
$this->add_control(
$prefix . '_border_color',
array_merge( $border_color, $border_color_args )
);
}
protected function get_shape_divider( $side = 'bottom' ) {
$settings = $this->settings;
$base_setting_key = "identity_section_style_cover_divider_$side";
$file_name = $settings[ $base_setting_key ];
if ( empty( $file_name ) ) {
return [];
}
$negative = ! empty( $settings[ $base_setting_key . '_negative' ] );
$shape_path = Shapes::get_shape_path( $file_name, $negative );
if ( ! is_file( $shape_path ) || ! is_readable( $shape_path ) ) {
return [];
}
return [
'negative' => $negative,
'svg' => Utils::file_get_contents( $shape_path ),
];
}
protected function print_shape_divider( $side = 'bottom' ) {
$shape_divider = $this->get_shape_divider( $side );
if ( empty( $shape_divider ) ) {
return;
}
?>
breakpoints->get_active_devices_list( [ 'reverse' => true ] );
$active_breakpoint_instances = Plugin::$instance->breakpoints->get_active_breakpoints();
$devices_options = [];
foreach ( $active_devices as $device_key ) {
$device_label = 'desktop' === $device_key ? esc_html__( 'Desktop', 'elementor' ) : $active_breakpoint_instances[ $device_key ]->get_label();
$devices_options[ $device_key ] = $device_label;
}
return [
'active_devices' => $active_devices,
'devices_options' => $devices_options,
];
}
protected function add_hover_animation_control(
string $name,
array $args = []
): void {
$this->add_control(
$name,
array_merge(
[
'label' => esc_html__( 'Hover Animation', 'elementor' ),
'type' => Hover_Animation_Floating_Buttons::TYPE,
'frontend_available' => true,
'default' => 'grow',
],
$args
)
);
}
}
base/app.php 0000644 00000002344 14720700033 0006746 0 ustar 00 get_name();
$js_var = 'elementor' . str_replace( ' ', '', ucwords( str_replace( '-', ' ', $name ) ) ) . 'Config';
$config = $this->get_settings() + $this->get_components_config();
if ( ! $handle ) {
$handle = 'elementor-' . $name;
}
Utils::print_js_config( $handle, $js_var, $config );
}
/**
* Get components config.
*
* Retrieves the app components settings.
*
* @since 2.3.0
* @access private
*
* @return array
*/
private function get_components_config() {
$settings = [];
foreach ( $this->get_components() as $id => $instance ) {
$settings[ $id ] = $instance->get_settings();
}
return $settings;
}
}
base/background-task-manager.php 0000644 00000004744 14720700033 0012663 0 ustar 00 logger->get_logger();
$logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Started' );
}
public function on_runner_complete( $did_tasks = false ) {
$logger = Plugin::$instance->logger->get_logger();
$logger->info( $this->get_plugin_name() . '::' . $this->get_action() . ' Completed' );
}
public function get_task_runner() {
if ( empty( $this->task_runner ) ) {
$class_name = $this->get_task_runner_class();
$this->task_runner = new $class_name( $this );
}
return $this->task_runner;
}
// TODO: Replace with a db settings system.
protected function add_flag( $flag ) {
add_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag, 1 );
}
protected function get_flag( $flag ) {
return get_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag );
}
protected function delete_flag( $flag ) {
delete_option( $this->get_plugin_name() . '_' . $this->get_action() . '_' . $flag );
}
protected function get_start_action_url() {
return wp_nonce_url( add_query_arg( $this->get_action(), 'run' ), $this->get_action() . 'run' );
}
protected function get_continue_action_url() {
return wp_nonce_url( add_query_arg( $this->get_action(), 'continue' ), $this->get_action() . 'continue' );
}
private function continue_run() {
$runner = $this->get_task_runner();
$runner->continue_run();
}
public function __construct() {
if ( empty( $_GET[ $this->get_action() ] ) ) {
return;
}
Plugin::$instance->init_common();
if ( 'run' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'run' ) ) {
$this->start_run();
}
if ( 'continue' === $_GET[ $this->get_action() ] && check_admin_referer( $this->get_action() . 'continue' ) ) {
$this->continue_run();
}
wp_safe_redirect( remove_query_arg( [ $this->get_action(), '_wpnonce' ] ) );
die;
}
}
base/background-task.php 0000644 00000021572 14720700033 0011251 0 ustar 00 get_current_item();
if ( empty( $item['total'] ) ) {
$sql = preg_replace( '/^SELECT/', 'SELECT SQL_CALC_FOUND_ROWS', $sql );
}
// Add offset & limit.
$sql = preg_replace( '/;$/', '', $sql );
$sql .= ' LIMIT %d, %d;';
$results = $wpdb->get_col( $wpdb->prepare( $sql, $this->get_current_offset(), $this->get_limit() ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( ! empty( $results ) ) {
$this->set_total();
}
return $results;
}
public function should_run_again( $updated_rows ) {
return count( $updated_rows ) === $this->get_limit();
}
public function get_current_offset() {
$limit = $this->get_limit();
return ( $this->current_item['iterate_num'] - 1 ) * $limit;
}
public function get_limit() {
return $this->manager->get_query_limit();
}
public function set_total() {
global $wpdb;
if ( empty( $this->current_item['total'] ) ) {
$total_rows = $wpdb->get_var( 'SELECT FOUND_ROWS();' );
$total_iterates = ceil( $total_rows / $this->get_limit() );
$this->current_item['total'] = $total_iterates;
}
}
/**
* Complete
*
* Override if applicable, but ensure that the below actions are
* performed, or, call parent::complete().
*/
protected function complete() {
$this->manager->on_runner_complete( true );
parent::complete();
}
public function continue_run() {
// Used to fire an action added in WP_Background_Process::_construct() that calls WP_Background_Process::handle_cron_healthcheck().
// This method will make sure the database updates are executed even if cron is disabled. Nothing will happen if the updates are already running.
do_action( $this->cron_hook_identifier );
}
/**
* @return mixed
*/
public function get_current_item() {
return $this->current_item;
}
/**
* Get batch.
*
* @return \stdClass Return the first batch from the queue.
*/
protected function get_batch() {
$batch = parent::get_batch();
$batch->data = array_filter( (array) $batch->data );
return $batch;
}
/**
* Handle cron healthcheck
*
* Restart the background process if not already running
* and data exists in the queue.
*/
public function handle_cron_healthcheck() {
if ( $this->is_process_running() ) {
// Background process already running.
return;
}
if ( $this->is_queue_empty() ) {
// No data to process.
$this->clear_scheduled_event();
return;
}
$this->handle();
}
/**
* Schedule fallback event.
*/
protected function schedule_event() {
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier );
}
}
/**
* Is the updater running?
*
* @return boolean
*/
public function is_running() {
return false === $this->is_queue_empty();
}
/**
* See if the batch limit has been exceeded.
*
* @return bool
*/
protected function batch_limit_exceeded() {
return $this->time_exceeded() || $this->memory_exceeded();
}
/**
* Handle.
*
* Pass each queue item to the task handler, while remaining
* within server memory and time limit constraints.
*/
protected function handle() {
$this->manager->on_runner_start();
$this->lock_process();
do {
$batch = $this->get_batch();
foreach ( $batch->data as $key => $value ) {
$task = $this->task( $value );
if ( false !== $task ) {
$batch->data[ $key ] = $task;
} else {
unset( $batch->data[ $key ] );
}
if ( $this->batch_limit_exceeded() ) {
// Batch limits reached.
break;
}
}
// Update or delete current batch.
if ( ! empty( $batch->data ) ) {
$this->update( $batch->key, $batch->data );
} else {
$this->delete( $batch->key );
}
} while ( ! $this->batch_limit_exceeded() && ! $this->is_queue_empty() );
$this->unlock_process();
// Start next batch or complete process.
if ( ! $this->is_queue_empty() ) {
$this->dispatch();
} else {
$this->complete();
}
}
/**
* Use the protected `is_process_running` method as a public method.
* @return bool
*/
public function is_process_locked() {
return $this->is_process_running();
}
public function handle_immediately( $callbacks ) {
$this->manager->on_runner_start();
$this->lock_process();
foreach ( $callbacks as $callback ) {
$item = [
'callback' => $callback,
];
do {
$item = $this->task( $item );
} while ( $item );
}
$this->unlock_process();
}
/**
* Task
*
* Override this method to perform any actions required on each
* queue item. Return the modified item for further processing
* in the next pass through. Or, return false to remove the
* item from the queue.
*
* @param array $item
*
* @return array|bool
*/
protected function task( $item ) {
$result = false;
if ( ! isset( $item['iterate_num'] ) ) {
$item['iterate_num'] = 1;
}
$logger = Plugin::$instance->logger->get_logger();
$callback = $this->format_callback_log( $item );
if ( is_callable( $item['callback'] ) ) {
$progress = '';
if ( 1 < $item['iterate_num'] ) {
if ( empty( $item['total'] ) ) {
$progress = sprintf( '(x%s)', $item['iterate_num'] );
} else {
$percent = ceil( $item['iterate_num'] / ( $item['total'] / 100 ) );
$progress = sprintf( '(%s of %s, %s%%)', $item['iterate_num'], $item['total'], $percent );
}
}
$logger->info( sprintf( '%s Start %s', $callback, $progress ) );
$this->current_item = $item;
$result = (bool) call_user_func( $item['callback'], $this );
// get back the updated item.
$item = $this->current_item;
$this->current_item = null;
if ( $result ) {
if ( empty( $item['total'] ) ) {
$logger->info( sprintf( '%s callback needs to run again', $callback ) );
} elseif ( 1 === $item['iterate_num'] ) {
$logger->info( sprintf( '%s callback needs to run more %d times', $callback, $item['total'] - $item['iterate_num'] ) );
}
$item['iterate_num']++;
} else {
$logger->info( sprintf( '%s Finished', $callback ) );
}
} else {
$logger->notice( sprintf( 'Could not find %s callback', $callback ) );
}
return $result ? $item : false;
}
/**
* Schedule cron healthcheck.
*
* @param array $schedules Schedules.
* @return array
*/
public function schedule_cron_healthcheck( $schedules ) {
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
// Adds every 5 minutes to the existing schedules.
$schedules[ $this->identifier . '_cron_interval' ] = array(
'interval' => MINUTE_IN_SECONDS * $interval,
'display' => sprintf(
/* translators: %d: Interval in minutes. */
esc_html__( 'Every %d minutes', 'elementor' ),
$interval
),
);
return $schedules;
}
/**
* See if the batch limit has been exceeded.
*
* @return bool
*/
public function is_memory_exceeded() {
return $this->memory_exceeded();
}
/**
* Delete all batches.
*
* @return self
*/
public function delete_all_batches() {
global $wpdb;
$table = $wpdb->options;
$column = 'option_name';
if ( is_multisite() ) {
$table = $wpdb->sitemeta;
$column = 'meta_key';
}
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
$wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE {$column} LIKE %s", $key ) ); // @codingStandardsIgnoreLine.
return $this;
}
/**
* Kill process.
*
* Stop processing queue items, clear cronjob and delete all batches.
*/
public function kill_process() {
if ( ! $this->is_queue_empty() ) {
$this->delete_all_batches();
wp_clear_scheduled_hook( $this->cron_hook_identifier );
}
}
public function set_current_item( $item ) {
$this->current_item = $item;
}
protected function format_callback_log( $item ) {
return implode( '::', (array) $item['callback'] );
}
/**
* @var \Elementor\Core\Base\Background_Task_Manager
*/
protected $manager;
public function __construct( $manager ) {
$this->manager = $manager;
// Uses unique prefix per blog so each blog has separate queue.
$this->prefix = 'elementor_' . get_current_blog_id();
$this->action = $this->manager->get_action();
parent::__construct();
}
}
base/document.php 0000644 00000144610 14720700033 0010007 0 ustar 00 Utils::generate_random_string(),
'elType' => 'container',
'elements' => $internal_elements,
],
];
}
/**
* @param array $internal_elements
*
* @return array[]
*/
private function get_sections_elements_data( array $internal_elements ): array {
return [
[
'id' => Utils::generate_random_string(),
'elType' => 'section',
'elements' => [
[
'id' => Utils::generate_random_string(),
'elType' => 'column',
'elements' => $internal_elements,
],
],
],
];
}
/**
* @since 2.1.0
* @access protected
* @static
*/
protected static function get_editor_panel_categories() {
return Plugin::$instance->elements_manager->get_categories();
}
/**
* Get properties.
*
* Retrieve the document properties.
*
* @since 2.0.0
* @access public
* @static
*
* @return array Document properties.
*/
public static function get_properties() {
return [
'has_elements' => true,
'is_editable' => true,
'edit_capability' => '',
'show_in_finder' => true,
'show_on_admin_bar' => true,
'support_kit' => false,
'show_navigator' => true,
'allow_adding_widgets' => true,
'support_page_layout' => true,
'show_copy_and_share' => false,
'library_close_title' => esc_html__( 'Close', 'elementor' ),
'publish_button_title' => esc_html__( 'Publish', 'elementor' ),
'allow_closing_remote_library' => true,
];
}
/**
* @since 2.1.0
* @access public
* @static
*/
public static function get_editor_panel_config() {
$default_route = 'panel/elements/categories';
if ( ! Plugin::instance()->role_manager->user_can( 'design' ) ) {
$default_route = 'panel/page-settings/settings';
}
return [
'title' => static::get_title(), // JS Container title.
'widgets_settings' => [],
'elements_categories' => self::get_filtered_editor_panel_categories(),
'default_route' => $default_route,
'has_elements' => static::get_property( 'has_elements' ),
'support_kit' => static::get_property( 'support_kit' ),
'messages' => [
'publish_notification' => sprintf(
/* translators: %s: Document title. */
esc_html__( 'Hurray! Your %s is live.', 'elementor' ),
static::get_title()
),
],
'show_navigator' => static::get_property( 'show_navigator' ),
'allow_adding_widgets' => static::get_property( 'allow_adding_widgets' ),
'show_copy_and_share' => static::get_property( 'show_copy_and_share' ),
'library_close_title' => static::get_property( 'library_close_title' ),
'publish_button_title' => static::get_property( 'publish_button_title' ),
'allow_closing_remote_library' => static::get_property( 'allow_closing_remote_library' ),
];
}
public static function get_filtered_editor_panel_categories(): array {
$categories = static::get_editor_panel_categories();
$has_pro = Utils::has_pro();
foreach ( $categories as $index => $category ) {
if ( isset( $category['promotion'] ) ) {
$categories = self::get_panel_category_item( $category['promotion'], $index, $categories, $has_pro );
}
}
return $categories;
}
/**
* @param $promotion
* @param $index
* @param array $categories
*
* @return array
*/
private static function get_panel_category_item( $promotion, $index, array $categories, bool $has_pro ): array {
if ( ! $has_pro ) {
$categories[ $index ]['promotion'] = Filtered_Promotions_Manager::get_filtered_promotion_data(
$promotion,
'elementor/panel/' . $index . '/custom_promotion',
'url'
);
} else {
unset( $categories[ $index ]['promotion'] );
}
return $categories;
}
/**
* Get element title.
*
* Retrieve the element title.
*
* @since 2.0.0
* @access public
* @static
*
* @return string Element title.
*/
public static function get_title() {
return esc_html__( 'Document', 'elementor' );
}
public static function get_plural_title() {
return static::get_title();
}
public static function get_add_new_title() {
return sprintf(
/* translators: %s: Document title. */
esc_html__( 'Add New %s', 'elementor' ),
static::get_title()
);
}
/**
* Get property.
*
* Retrieve the document property.
*
* @since 2.0.0
* @access public
* @static
*
* @param string $key The property key.
*
* @return mixed The property value.
*/
public static function get_property( $key ) {
$id = static::get_class_full_name();
if ( ! isset( self::$properties[ $id ] ) ) {
self::$properties[ $id ] = static::get_properties();
}
return self::get_items( self::$properties[ $id ], $key );
}
/**
* @since 2.0.0
* @access public
* @static
*/
public static function get_class_full_name() {
return get_called_class();
}
public static function get_create_url() {
$properties = static::get_properties();
// BC Support - Each document should define it own CPT this code is for BC support.
$cpt = Source_Local::CPT;
if ( isset( $properties['cpt'][0] ) ) {
$cpt = $properties['cpt'][0];
}
return Plugin::$instance->documents->get_create_new_post_url( $cpt, static::get_type() );
}
public function get_name() {
return static::get_type();
}
/**
* @since 2.0.0
* @access public
*/
public function get_unique_name() {
return static::get_type() . '-' . $this->post->ID;
}
/**
* @since 2.3.0
* @access public
*/
public function get_post_type_title() {
$post_type_object = get_post_type_object( $this->post->post_type );
return $post_type_object->labels->singular_name;
}
/**
* @since 2.0.0
* @access public
*/
public function get_main_id() {
if ( ! $this->main_id ) {
$post_id = $this->post->ID;
$parent_post_id = wp_is_post_revision( $post_id );
if ( $parent_post_id ) {
$post_id = $parent_post_id;
}
$this->main_id = $post_id;
}
return $this->main_id;
}
/**
* @return null|Lock_Behavior
*/
public static function get_lock_behavior_v2() {
return null;
}
/**
* @since 2.0.0
* @access public
*
* @param $data
*
* @throws \Exception If the widget was not found.
*
* @return string
*/
public function render_element( $data ) {
// Start buffering
ob_start();
/** @var Widget_Base $widget */
$widget = Plugin::$instance->elements_manager->create_element_instance( $data );
if ( ! $widget ) {
throw new \Exception( 'Widget not found.' );
}
$widget->render_content();
$render_html = ob_get_clean();
return $render_html;
}
/**
* @since 2.0.0
* @access public
*/
public function get_main_post() {
return get_post( $this->get_main_id() );
}
public function get_container_attributes() {
$id = $this->get_main_id();
$attributes = [
'data-elementor-type' => $this->get_name(),
'data-elementor-id' => $id,
'class' => 'elementor elementor-' . $id,
];
$version_meta = $this->get_main_meta( '_elementor_version' );
if ( version_compare( $version_meta, '2.5.0', '<' ) ) {
$attributes['class'] .= ' elementor-bc-flex-widget';
}
if ( Plugin::$instance->preview->is_preview() ) {
$attributes['data-elementor-title'] = static::get_title();
} else {
$elementor_settings = $this->get_frontend_settings();
if ( ! empty( $elementor_settings ) ) {
$attributes['data-elementor-settings'] = wp_json_encode( $elementor_settings );
}
}
// apply this filter to allow the attributes to be modified by different sources
return apply_filters( 'elementor/document/wrapper_attributes', $attributes, $this );
}
/**
* @since 2.0.0
* @access public
*/
public function get_wp_preview_url() {
$main_post_id = $this->get_main_id();
$document = $this;
// Ajax request from editor.
$initial_document_id = Utils::get_super_global_value( $_POST, 'initial_document_id' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! empty( $initial_document_id ) ) {
$document = Plugin::$instance->documents->get( $initial_document_id ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
$url = get_preview_post_link(
$document->get_main_id(),
[
'preview_id' => $main_post_id,
'preview_nonce' => wp_create_nonce( 'post_preview_' . $main_post_id ),
]
);
/**
* Document "WordPress preview" URL.
*
* Filters the WordPress preview URL.
*
* @since 2.0.0
*
* @param string $url WordPress preview URL.
* @param Document $this The document instance.
*/
$url = apply_filters( 'elementor/document/urls/wp_preview', $url, $this );
return $url;
}
/**
* @since 2.0.0
* @access public
*/
public function get_exit_to_dashboard_url() {
$url = get_edit_post_link( $this->get_main_id(), 'raw' );
/**
* Document "exit to dashboard" URL.
*
* Filters the "Exit To Dashboard" URL.
*
* @since 2.0.0
*
* @param string $url The exit URL
* @param Document $this The document instance.
*/
$url = apply_filters( 'elementor/document/urls/exit_to_dashboard', $url, $this );
return $url;
}
/**
* Get All Post Type URL
*
* Get url of the page which display all the posts of the current active document's post type.
*
* @since 3.7.0
*
* @return string $url
*/
public function get_all_post_type_url() {
$post_type = get_post_type( $this->get_main_id() );
$url = get_admin_url() . 'edit.php';
if ( 'post' !== $post_type ) {
$url .= '?post_type=' . $post_type;
}
/**
* Document "display all post type" URL.
*
* @since 3.7.0
*
* @param string $url The URL.
* @param Document $this The document instance.
*/
$url = apply_filters( 'elementor/document/urls/all_post_type', $url, $this );
return $url;
}
/**
* Get Main WP dashboard URL.
*
* @since 3.7.0
*
* @return string $url
*/
protected function get_main_dashboard_url() {
$url = get_dashboard_url();
/**
* Document "Main Dashboard" URL.
*
* @since 3.7.0
*
* @param string $url The URL.
* @param Document $this The document instance.
*/
$url = apply_filters( 'elementor/document/urls/main_dashboard', $url, $this );
return $url;
}
/**
* Get auto-saved post revision.
*
* Retrieve the auto-saved post revision that is newer than current post.
*
* @since 2.0.0
* @access public
*
*
* @return bool|Document
*/
public function get_newer_autosave() {
$autosave = $this->get_autosave();
// Detect if there exists an autosave newer than the post.
if ( $autosave && mysql2date( 'U', $autosave->get_post()->post_modified_gmt, false ) > mysql2date( 'U', $this->post->post_modified_gmt, false ) ) {
return $autosave;
}
return false;
}
/**
* @since 2.0.0
* @access public
*/
public function is_autosave() {
return wp_is_post_autosave( $this->post->ID );
}
/**
* Check if the current document is a 'revision'
*
* @return bool
*/
public function is_revision() {
return 'revision' === $this->post->post_type;
}
/**
* Checks if the current document status is 'trash'.
*
* @return bool
*/
public function is_trash() {
return 'trash' === $this->post->post_status;
}
/**
* @since 2.0.0
* @access public
*
* @param int $user_id
* @param bool $create
*
* @return bool|Document
*/
public function get_autosave( $user_id = 0, $create = false ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
$autosave_id = $this->get_autosave_id( $user_id );
if ( $autosave_id ) {
$document = Plugin::$instance->documents->get( $autosave_id );
} elseif ( $create ) {
$autosave_id = wp_create_post_autosave( [
'post_ID' => $this->post->ID,
'post_type' => $this->post->post_type,
'post_title' => $this->post->post_title,
'post_excerpt' => $this->post->post_excerpt,
// Hack to cause $autosave_is_different=true in `wp_create_post_autosave`.
'post_content' => '',
'post_modified' => current_time( 'mysql' ),
] );
Plugin::$instance->db->copy_elementor_meta( $this->post->ID, $autosave_id );
$document = Plugin::$instance->documents->get( $autosave_id );
$document->save_template_type();
} else {
$document = false;
}
return $document;
}
/**
* Add/Remove edit link in dashboard.
*
* Add or remove an edit link to the post/page action links on the post/pages list table.
*
* Fired by `post_row_actions` and `page_row_actions` filters.
*
* @access public
*
* @param array $actions An array of row action links.
*
* @return array An updated array of row action links.
*/
public function filter_admin_row_actions( $actions ) {
if ( $this->is_built_with_elementor() && $this->is_editable_by_current_user() ) {
$actions['edit_with_elementor'] = sprintf(
'%2$s',
$this->get_edit_url(),
__( 'Edit with Elementor', 'elementor' )
);
}
return $actions;
}
/**
* @since 2.0.0
* @access public
*/
public function is_editable_by_current_user() {
$edit_capability = static::get_property( 'edit_capability' );
if ( $edit_capability && ! current_user_can( $edit_capability ) ) {
return false;
}
return self::get_property( 'is_editable' ) && User::is_current_user_can_edit( $this->get_main_id() );
}
/**
* @since 2.9.0
* @access protected
*/
protected function get_initial_config() {
// Get document data *after* the scripts hook - so plugins can run compatibility before get data, but *before* enqueue the editor script - so elements can enqueue their own scripts that depended in editor script.
$locked_user = Plugin::$instance->editor->get_locked_user( $this->get_main_id() );
if ( $locked_user ) {
$locked_user = $locked_user->display_name;
}
$post = $this->get_main_post();
$post_type_object = get_post_type_object( $post->post_type );
$settings = SettingsManager::get_settings_managers_config();
$config = [
'id' => $this->get_main_id(),
'type' => $this->get_name(),
'version' => $this->get_main_meta( '_elementor_version' ),
'settings' => $settings['page'],
'remoteLibrary' => $this->get_remote_library_config(),
'last_edited' => $this->get_last_edited(),
'panel' => static::get_editor_panel_config(),
'container' => 'body',
'post_type_title' => $this->get_post_type_title(),
'user' => [
'can_publish' => current_user_can( $post_type_object->cap->publish_posts ),
// Deprecated config since 2.9.0.
'locked' => $locked_user,
],
'urls' => [
'exit_to_dashboard' => $this->get_exit_to_dashboard_url(), // WP post type edit page
'all_post_type' => $this->get_all_post_type_url(),
'preview' => $this->get_preview_url(),
'wp_preview' => $this->get_wp_preview_url(),
'permalink' => $this->get_permalink(),
'have_a_look' => $this->get_have_a_look_url(),
'main_dashboard' => $this->get_main_dashboard_url(),
],
];
$post_status_object = get_post_status_object( $post->post_status );
if ( $post_status_object ) {
$config['status'] = [
'value' => $post_status_object->name,
'label' => $post_status_object->label,
];
}
do_action( 'elementor/document/before_get_config', $this );
if ( static::get_property( 'has_elements' ) ) {
$container_config = [];
$experiments_manager = Plugin::$instance->experiments;
if ( $experiments_manager->is_feature_active( 'container' ) ) {
$container_config = [
'container' => Plugin::$instance->elements_manager->get_element_types( 'container' )->get_config(),
];
}
$config['elements'] = $this->get_elements_raw_data( null, true );
$config['widgets'] = $container_config + Plugin::$instance->widgets_manager->get_widget_types_config();
}
$additional_config = [];
/**
* Additional document configuration.
*
* Filters the document configuration by adding additional configuration.
* External developers can use this hook to add custom configuration in
* addition to Elementor's initial configuration.
*
* Use the $post_id to add custom configuration for different pages.
*
* @param array $additional_config The additional document configuration.
* @param int $post_id The post ID of the document.
*/
$additional_config = apply_filters( 'elementor/document/config', $additional_config, $this->get_main_id() );
if ( ! empty( $additional_config ) ) {
$config = array_replace_recursive( $config, $additional_config );
}
return $config;
}
/**
* @since 3.1.0
* @access protected
*/
protected function register_controls() {
$this->register_document_controls();
/**
* Register document controls.
*
* Fires after Elementor registers the document controls.
*
* External developers can use this hook to add new controls to the document.
*
* @since 2.0.0
*
* @param Document $this The document instance.
*/
do_action( 'elementor/documents/register_controls', $this );
}
/**
* @since 2.0.0
* @access public
*
* @param $data
*
* @return bool
*/
public function save( $data ) {
/**
* Set locale to "C" to avoid issues with comma as decimal separator.
*
* @see https://github.com/elementor/elementor/issues/10992
*/
$original_lc = setlocale( LC_NUMERIC, 0 );
setlocale( LC_NUMERIC, 'C' );
/**
* Document save data.
*
* Filter the document data before saving process starts.
*
* External developers can use this hook to change the data before
* saving it to the database.
*
* @since 3.3.0
*
* @param array $data The document data.
* @param \Elementor\Core\Base\Document $this The document instance.
*/
$data = apply_filters( 'elementor/document/save/data', $data, $this );
$this->add_handle_revisions_changed_filter();
if ( ! $this->is_editable_by_current_user() ) {
return false;
}
$this->set_is_saving( true );
/**
* Before document save.
*
* Fires when document save starts on Elementor.
*
* @since 2.5.12
*
* @param \Elementor\Core\Base\Document $this The current document.
* @param $data.
*/
do_action( 'elementor/document/before_save', $this, $data );
if ( ! current_user_can( 'unfiltered_html' ) ) {
$data = wp_kses_post_deep( $data );
}
if ( ! empty( $data['settings'] ) ) {
if ( isset( $data['settings']['post_status'] ) && self::STATUS_AUTOSAVE === $data['settings']['post_status'] ) {
if ( ! defined( 'DOING_AUTOSAVE' ) ) {
define( 'DOING_AUTOSAVE', true );
}
}
$this->save_settings( $data['settings'] );
$this->refresh_post();
}
// Don't check is_empty, because an empty array should be saved.
if ( isset( $data['elements'] ) && is_array( $data['elements'] ) ) {
$this->save_elements( $data['elements'] );
}
$this->save_template_type();
$this->save_version();
// Remove Post CSS
$post_css = Post_CSS::create( $this->post->ID );
$post_css->delete();
// Remove Document Cache
$this->delete_cache();
/**
* After document save.
*
* Fires when document save is complete.
*
* @since 2.5.12
*
* @param \Elementor\Core\Base\Document $this The current document.
* @param $data.
*/
do_action( 'elementor/document/after_save', $this, $data );
$this->set_is_saving( false );
$this->remove_handle_revisions_changed_filter();
setlocale( LC_NUMERIC, $original_lc );
return true;
}
public function refresh_post() {
$this->post = get_post( $this->post->ID );
}
/**
* @param array $new_settings
*
* @return static
*/
public function update_settings( array $new_settings ) {
$document_settings = $this->get_meta( PageManager::META_KEY );
if ( ! $document_settings ) {
$document_settings = [];
}
$this->save_settings(
array_replace_recursive( $document_settings, $new_settings )
);
return $this;
}
/**
* Is built with Elementor.
*
* Check whether the post was built with Elementor.
*
* @since 2.0.0
* @access public
*
* @return bool Whether the post was built with Elementor.
*/
public function is_built_with_elementor() {
return ! ! $this->get_meta( self::BUILT_WITH_ELEMENTOR_META_KEY );
}
/**
* Mark the post as "built with elementor" or not.
*
* @param bool $is_built_with_elementor
*
* @return $this
*/
public function set_is_built_with_elementor( $is_built_with_elementor ) {
if ( $is_built_with_elementor ) {
// Use the string `builder` and not a boolean for rollback compatibility
$this->update_meta( self::BUILT_WITH_ELEMENTOR_META_KEY, 'builder' );
} else {
$this->delete_meta( self::BUILT_WITH_ELEMENTOR_META_KEY );
}
return $this;
}
/**
* @since 2.0.0
* @access public
* @static
*
* @return mixed
*/
public function get_edit_url() {
$url = add_query_arg(
[
'post' => $this->get_main_id(),
'action' => 'elementor',
],
admin_url( 'post.php' )
);
/**
* Document edit url.
*
* Filters the document edit url.
*
* @since 2.0.0
*
* @param string $url The edit url.
* @param Document $this The document instance.
*/
$url = apply_filters( 'elementor/document/urls/edit', $url, $this );
return $url;
}
/**
* @since 2.0.0
* @access public
*/
public function get_preview_url() {
/**
* Use a static var - to avoid change the `ver` parameter on every call.
*/
static $url;
if ( empty( $url ) ) {
add_filter( 'pre_option_permalink_structure', '__return_empty_string' );
$url = set_url_scheme( add_query_arg( [
'elementor-preview' => $this->get_main_id(),
'ver' => time(),
], $this->get_permalink() ) );
remove_filter( 'pre_option_permalink_structure', '__return_empty_string' );
/**
* Document preview URL.
*
* Filters the document preview URL.
*
* @since 2.0.0
*
* @param string $url The preview URL.
* @param Document $this The document instance.
*/
$url = apply_filters( 'elementor/document/urls/preview', $url, $this );
}
return $url;
}
/**
* @since 2.0.0
* @access public
*
* @param string $key
*
* @return array
*/
public function get_json_meta( $key ) {
$meta = get_post_meta( $this->post->ID, $key, true );
if ( is_string( $meta ) && ! empty( $meta ) ) {
$meta = json_decode( $meta, true );
}
if ( empty( $meta ) ) {
$meta = [];
}
return $meta;
}
public function update_json_meta( $key, $value ) {
$this->update_meta(
$key,
// `wp_slash` in order to avoid the unslashing during the `update_post_meta`
wp_slash( wp_json_encode( $value ) )
);
}
/**
* @since 2.0.0
* @access public
*
* @param null $data
* @param bool $with_html_content
*
* @return array
*/
public function get_elements_raw_data( $data = null, $with_html_content = false ) {
if ( ! static::get_property( 'has_elements' ) ) {
return [];
}
if ( is_null( $data ) ) {
$data = $this->get_elements_data();
}
// Change the current documents, so widgets can use `documents->get_current` and other post data
Plugin::$instance->documents->switch_to_document( $this );
$editor_data = [];
foreach ( $data as $element_data ) {
if ( ! is_array( $element_data ) ) {
throw new \Exception( 'Invalid data: ' . wp_json_encode( [
'data' => $data,
'element' => $element_data,
] ) );
}
$element = Plugin::$instance->elements_manager->create_element_instance( $element_data );
if ( ! $element ) {
continue;
}
if ( $this->is_saving ) {
$element_data = $element->get_data_for_save();
} else {
$element_data = $element->get_raw_data( $with_html_content );
}
$editor_data[] = $element_data;
} // End foreach().
Plugin::$instance->documents->restore_document();
return $editor_data;
}
/**
* @since 2.0.0
* @access public
*
* @param string $status
*
* @return array
*/
public function get_elements_data( $status = self::STATUS_PUBLISH ) {
$elements = $this->get_json_meta( '_elementor_data' );
if ( self::STATUS_DRAFT === $status ) {
$autosave = $this->get_newer_autosave();
if ( is_object( $autosave ) ) {
$autosave_elements = Plugin::$instance->documents
->get( $autosave->get_post()->ID )
->get_json_meta( '_elementor_data' );
}
}
if ( Plugin::$instance->editor->is_edit_mode() ) {
if ( empty( $elements ) && empty( $autosave_elements ) ) {
// Convert to Elementor.
$elements = $this->convert_to_elementor();
if ( $this->is_autosave() ) {
Plugin::$instance->db->copy_elementor_meta( $this->post->post_parent, $this->post->ID );
}
}
}
if ( ! empty( $autosave_elements ) ) {
$elements = $autosave_elements;
}
return $elements;
}
/**
* Get document setting from DB.
*
* @return array
*/
public function get_db_document_settings() {
return $this->get_meta( static::PAGE_META_KEY );
}
/**
* @since 2.3.0
* @access public
*/
public function convert_to_elementor() {
$this->save( [] );
if ( empty( $this->post->post_content ) ) {
return [];
}
// Check if it's only a shortcode.
preg_match_all( '/' . get_shortcode_regex() . '/', $this->post->post_content, $matches, PREG_SET_ORDER );
if ( ! empty( $matches ) ) {
foreach ( $matches as $shortcode ) {
if ( trim( $this->post->post_content ) === $shortcode[0] ) {
$widget_type = Plugin::$instance->widgets_manager->get_widget_types( 'shortcode' );
$settings = [
'shortcode' => $this->post->post_content,
];
break;
}
}
}
if ( empty( $widget_type ) ) {
$widget_type = Plugin::$instance->widgets_manager->get_widget_types( 'text-editor' );
$settings = [
'editor' => $this->post->post_content,
];
}
// TODO: Better coding to start template for editor
$converted_blocks = [
[
'id' => Utils::generate_random_string(),
'elType' => $widget_type::get_type(),
'widgetType' => $widget_type->get_name(),
'settings' => $settings,
],
];
return Plugin::$instance->experiments->is_feature_active( 'container' )
? $this->get_container_elements_data( $converted_blocks )
: $this->get_sections_elements_data( $converted_blocks );
}
/**
* @since 2.1.3
* @access public
*/
public function print_elements_with_wrapper( $elements_data = null ) {
if ( ! $elements_data ) {
$elements_data = $this->get_elements_data();
}
?>
get_container_attributes() ); ?>>
print_elements( $elements_data ); ?>
sprintf(
/* translators: %s: Document title. */
esc_html__( '%s Settings', 'elementor' ),
static::get_title()
),
];
}
/**
* @since 2.0.0
* @access public
*/
public function get_post() {
return $this->post;
}
/**
* @since 2.0.0
* @access public
*/
public function get_permalink() {
return get_permalink( $this->get_main_id() );
}
/**
* @since 2.0.8
* @access public
*/
public function get_content( $with_css = false ) {
return Plugin::$instance->frontend->get_builder_content( $this->post->ID, $with_css );
}
/**
* @since 2.0.0
* @access public
*/
public function delete() {
if ( 'revision' === $this->post->post_type ) {
$deleted = wp_delete_post_revision( $this->post );
} else {
$deleted = wp_delete_post( $this->post->ID );
}
return $deleted && ! is_wp_error( $deleted );
}
public function force_delete() {
$deleted = wp_delete_post( $this->post->ID, true );
return $deleted && ! is_wp_error( $deleted );
}
/**
* On import update dynamic content (e.g. post and term IDs).
*
* @since 3.8.0
*
* @param array $config The config of the passed element.
* @param array $data The data that requires updating/replacement when imported.
* @param array|null $controls The available controls.
*
* @return array Element data.
*/
public static function on_import_update_dynamic_content( array $config, array $data, $controls = null ) : array {
foreach ( $config as &$element_config ) {
$element_instance = Plugin::$instance->elements_manager->create_element_instance( $element_config );
if ( is_null( $element_instance ) ) {
continue;
}
if ( $element_instance->has_own_method( 'on_import_replace_dynamic_content' ) ) {
// TODO: Remove this check in the future.
$element_config = $element_instance::on_import_replace_dynamic_content( $element_config, $data['post_ids'] );
} else {
$element_config = $element_instance::on_import_update_dynamic_content( $element_config, $data, $element_instance->get_controls() );
}
$element_config['elements'] = static::on_import_update_dynamic_content( $element_config['elements'], $data );
}
return $config;
}
/**
* Update dynamic settings in the document for import.
*
* @param array $settings The settings of the document.
* @param array $config Import config to update the settings.
*
* @return array
*/
public function on_import_update_settings( array $settings, array $config ): array {
$controls = $this->get_controls();
$controls_manager = Plugin::$instance->controls_manager;
foreach ( $settings as $key => $value ) {
if ( ! isset( $controls[ $key ] ) ) {
continue;
}
$control = $controls[ $key ];
$control_instance = $controls_manager->get_control( $control['type'] );
if ( ! $control_instance ) {
continue;
}
$settings[ $key ] = $control_instance->on_import_update_settings( $value, $control, $config );
}
return $settings;
}
/**
* Save editor elements.
*
* Save data from the editor to the database.
*
* @since 2.0.0
* @access protected
*
* @param array $elements
*/
protected function save_elements( $elements ) {
$editor_data = $this->get_elements_raw_data( $elements );
// We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta`
$json_value = wp_slash( wp_json_encode( $editor_data ) );
// Don't use `update_post_meta` that can't handle `revision` post type
$is_meta_updated = update_metadata( 'post', $this->post->ID, '_elementor_data', $json_value );
/**
* Before saving data.
*
* Fires before Elementor saves data to the database.
*
* @since 1.0.0
*
* @param string $status Post status.
* @param int|bool $is_meta_updated Meta ID if the key didn't exist, true on successful update, false on failure.
*/
do_action( 'elementor/db/before_save', $this->post->post_status, $is_meta_updated );
Plugin::$instance->db->save_plain_text( $this->post->ID );
$elements_iteration_actions = $this->get_elements_iteration_actions();
if ( $elements_iteration_actions ) {
$this->iterate_elements( $elements, $elements_iteration_actions, 'save' );
}
/**
* After saving data.
*
* Fires after Elementor saves data to the database.
*
* @since 1.0.0
*
* @param int $post_id The ID of the post.
* @param array $editor_data Sanitize posted data.
*/
do_action( 'elementor/editor/after_save', $this->post->ID, $editor_data );
}
/**
* @since 2.0.0
* @access public
*
* @param int $user_id Optional. User ID. Default value is `0`.
*
* @return bool|int
*/
public function get_autosave_id( $user_id = 0 ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
$autosave = Utils::get_post_autosave( $this->post->ID, $user_id );
if ( $autosave ) {
return $autosave->ID;
}
return false;
}
public function save_version() {
if ( ! defined( 'IS_ELEMENTOR_UPGRADE' ) ) {
// Save per revision.
$this->update_meta( '_elementor_version', ELEMENTOR_VERSION );
/**
* Document version save.
*
* Fires when document version is saved on Elementor.
* Will not fire during Elementor Upgrade.
*
* @since 2.5.12
*
* @param \Elementor\Core\Base\Document $this The current document.
*
*/
do_action( 'elementor/document/save_version', $this );
}
}
/**
* @since 2.3.0
* @access public
*/
public function save_template_type() {
return $this->update_main_meta( self::TYPE_META_KEY, $this->get_name() );
}
/**
* @since 2.3.0
* @access public
*/
public function get_template_type() {
return $this->get_main_meta( self::TYPE_META_KEY );
}
/**
* @since 2.0.0
* @access public
*
* @param string $key Meta data key.
*
* @return mixed
*/
public function get_main_meta( $key ) {
return get_post_meta( $this->get_main_id(), $key, true );
}
/**
* @since 2.0.4
* @access public
*
* @param string $key Meta data key.
* @param mixed $value Meta data value.
*
* @return bool|int
*/
public function update_main_meta( $key, $value ) {
return update_post_meta( $this->get_main_id(), $key, $value );
}
/**
* @since 2.0.4
* @access public
*
* @param string $key Meta data key.
* @param string $value Optional. Meta data value. Default is an empty string.
*
* @return bool
*/
public function delete_main_meta( $key, $value = '' ) {
return delete_post_meta( $this->get_main_id(), $key, $value );
}
/**
* @since 2.0.0
* @access public
*
* @param string $key Meta data key.
*
* @return mixed
*/
public function get_meta( $key ) {
return get_post_meta( $this->post->ID, $key, true );
}
/**
* @since 2.0.0
* @access public
*
* @param string $key Meta data key.
* @param mixed $value Meta data value.
*
* @return bool|int
*/
public function update_meta( $key, $value ) {
// Use `update_metadata` in order to work also with revisions.
return update_metadata( 'post', $this->post->ID, $key, $value );
}
/**
* @since 2.0.3
* @access public
*
* @param string $key Meta data key.
* @param string $value Meta data value.
*
* @return bool
*/
public function delete_meta( $key, $value = '' ) {
// Use `delete_metadata` in order to work also with revisions.
return delete_metadata( 'post', $this->post->ID, $key, $value );
}
/**
* @since 2.0.0
* @access public
*/
public function get_last_edited() {
$post = $this->post;
$autosave_post = $this->get_autosave();
if ( $autosave_post ) {
$post = $autosave_post->get_post();
}
$date = date_i18n( _x( 'M j, H:i', 'revision date format', 'elementor' ), strtotime( $post->post_modified ) );
$display_name = get_the_author_meta( 'display_name', $post->post_author );
if ( $autosave_post || 'revision' === $post->post_type ) {
$last_edited = sprintf(
/* translators: 1: Saving date, 2: Author display name. */
esc_html__( 'Draft saved on %1$s by %2$s', 'elementor' ),
'',
$display_name
);
} else {
$last_edited = sprintf(
/* translators: 1: Editing date, 2: Author display name. */
esc_html__( 'Last edited on %1$s by %2$s', 'elementor' ),
'',
$display_name
);
}
return $last_edited;
}
/**
* @return bool
*/
public function is_saving() {
return $this->is_saving;
}
/**
* @param $is_saving
*
* @return $this
*/
public function set_is_saving( $is_saving ) {
$this->is_saving = $is_saving;
return $this;
}
/**
* @since 2.0.0
* @access public
*
* @param array $data
*
* @throws \Exception If the post does not exist.
*/
public function __construct( array $data = [] ) {
if ( $data ) {
if ( empty( $data['post_id'] ) ) {
$this->post = new \WP_Post( (object) [] );
} else {
$this->post = get_post( $data['post_id'] );
if ( ! $this->post ) {
throw new \Exception( sprintf( 'Post ID #%s does not exist.', $data['post_id'] ), Exceptions::NOT_FOUND );
}
}
// Each Control_Stack is based on a unique ID.
$data['id'] = $data['post_id'];
if ( ! isset( $data['settings'] ) ) {
$data['settings'] = [];
}
$saved_settings = get_post_meta( $this->post->ID, '_elementor_page_settings', true );
if ( ! empty( $saved_settings ) && is_array( $saved_settings ) ) {
$data['settings'] += $saved_settings;
}
}
parent::__construct( $data );
}
/*
* Get Export Data
*
* Filters a document's data on export
*
* @since 3.2.0
* @access public
*
* @return array The data to export
*/
public function get_export_data() {
$content = Plugin::$instance->db->iterate_data( $this->get_elements_data(), function( $element_data ) {
$element_data['id'] = Utils::generate_random_string();
$element = Plugin::$instance->elements_manager->create_element_instance( $element_data );
// If the widget/element does not exist, like a plugin that creates a widget but deactivated.
if ( ! $element ) {
return null;
}
return $this->process_element_import_export( $element, 'on_export' );
} );
return [
'content' => $content,
'settings' => $this->get_data( 'settings' ),
'metadata' => $this->get_export_metadata(),
];
}
public function get_export_summary() {
return [
'title' => $this->post->post_title,
'doc_type' => $this->get_name(),
'thumbnail' => get_the_post_thumbnail_url( $this->post ),
];
}
/*
* Get Import Data
*
* Filters a document's data on import
*
* @since 3.2.0
* @access public
*
* @return array The data to import
*/
public function get_import_data( array $data ) {
$data['content'] = Plugin::$instance->db->iterate_data( $data['content'], function( $element_data ) {
$element = Plugin::$instance->elements_manager->create_element_instance( $element_data );
// If the widget/element isn't exist, like a plugin that creates a widget but deactivated
if ( ! $element ) {
return null;
}
return $this->process_element_import_export( $element, 'on_import' );
} );
if ( ! empty( $data['settings'] ) ) {
$template_model = new Page_Model( [
'id' => 0,
'settings' => $data['settings'],
] );
$page_data = $this->process_element_import_export( $template_model, 'on_import' );
$data['settings'] = $page_data['settings'];
}
return $data;
}
/**
* Import
*
* Allows to import an external data to a document
*
* @since 3.2.0
* @access public
*
* @param array $data
*/
public function import( array $data ) {
$data = $this->get_import_data( $data );
$this->save( [
'elements' => $data['content'],
'settings' => $data['settings'],
] );
if ( $data['import_settings']['thumbnail'] ) {
$attachment = Plugin::$instance->templates_manager->get_import_images_instance()->import( [ 'url' => $data['import_settings']['thumbnail'] ] );
set_post_thumbnail( $this->get_main_post(), $attachment['id'] );
}
if ( ! empty( $data['metadata'] ) ) {
foreach ( $data['metadata'] as $key => $value ) {
$this->update_meta( $key, $value );
}
}
}
public function process_element_import_export( Controls_Stack $element, $method, $element_data = null ) {
if ( null === $element_data ) {
$element_data = $element->get_data();
}
if ( method_exists( $element, $method ) ) {
// TODO: Use the internal element data without parameters.
$element_data = $element->{$method}( $element_data );
}
foreach ( $element->get_controls() as $control ) {
$control_class = Plugin::$instance->controls_manager->get_control( $control['type'] );
// If the control isn't exist, like a plugin that creates the control but deactivated.
if ( ! $control_class ) {
return $element_data;
}
// Do not add default value to the final settings, if there is no value at the
// data before the methods `on_import` or `on_export` called.
$has_value = isset( $element_data['settings'][ $control['name'] ] );
if ( $has_value && method_exists( $control_class, $method ) ) {
$element_data['settings'][ $control['name'] ] = $control_class->{$method}(
$element_data['settings'][ $control['name'] ],
$control
);
}
// On Export, check if the control has an argument 'export' => false.
if ( 'on_export' === $method && isset( $control['export'] ) && false === $control['export'] ) {
unset( $element_data['settings'][ $control['name'] ] );
}
}
return $element_data;
}
protected function get_export_metadata() {
$metadata = get_post_meta( $this->get_main_id() );
foreach ( $metadata as $meta_key => $meta_value ) {
if ( is_protected_meta( $meta_key, 'post' ) ) {
unset( $metadata[ $meta_key ] );
continue;
}
$metadata[ $meta_key ] = $meta_value[0];
}
return $metadata;
}
protected function get_remote_library_config() {
$config = [
'type' => 'block',
'default_route' => 'templates/blocks',
'category' => $this->get_name(),
'autoImportSettings' => false,
];
return $config;
}
/**
* @since 2.0.4
* @access protected
*
* @param $settings
*/
protected function save_settings( $settings ) {
$page_settings_manager = SettingsManager::get_settings_managers( 'page' );
$page_settings_manager->ajax_before_save_settings( $settings, $this->post->ID );
$page_settings_manager->save_settings( $settings, $this->post->ID );
}
/**
* @since 2.1.3
* @access protected
*/
protected function print_elements( $elements_data ) {
if ( ! Plugin::$instance->experiments->is_feature_active( 'e_element_cache' ) ) {
$this->do_print_elements( $elements_data );
return;
}
$cached_data = $this->get_document_cache();
if ( false === $cached_data ) {
add_filter( 'elementor/element/should_render_shortcode', '__return_true' );
$scripts_to_queue = [];
$styles_to_queue = [];
global $wp_scripts, $wp_styles;
$should_store_scripts = $wp_scripts instanceof \WP_Scripts && $wp_styles instanceof \WP_Styles;
if ( $should_store_scripts ) {
$scripts_ignored = $wp_scripts->queue;
$styles_ignored = $wp_styles->queue;
}
ob_start();
$this->do_print_elements( $elements_data );
if ( $should_store_scripts ) {
$scripts_to_queue = array_values( array_diff( $wp_scripts->queue, $scripts_ignored ) );
$styles_to_queue = array_values( array_diff( $wp_styles->queue, $styles_ignored ) );
}
$cached_data = [
'content' => ob_get_clean(),
'scripts' => $scripts_to_queue,
'styles' => $styles_to_queue,
];
if ( $this->should_store_cache_elements() ) {
$this->set_document_cache( $cached_data );
}
remove_filter( 'elementor/element/should_render_shortcode', '__return_true' );
} else {
if ( ! empty( $cached_data['scripts'] ) ) {
foreach ( $cached_data['scripts'] as $script_handle ) {
wp_enqueue_script( $script_handle );
}
}
if ( ! empty( $cached_data['styles'] ) ) {
foreach ( $cached_data['styles'] as $style_handle ) {
wp_enqueue_style( $style_handle );
}
}
}
if ( ! empty( $cached_data['content'] ) ) {
echo do_shortcode( $cached_data['content'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
protected function do_print_elements( $elements_data ) {
// Collect all data updaters that should be updated on runtime.
$runtime_elements_iteration_actions = $this->get_runtime_elements_iteration_actions();
if ( $runtime_elements_iteration_actions ) {
$this->iterate_elements( $elements_data, $runtime_elements_iteration_actions, 'render' );
}
foreach ( $elements_data as $element_data ) {
$element = Plugin::$instance->elements_manager->create_element_instance( $element_data );
if ( ! $element ) {
continue;
}
$element->print_element();
}
}
public function set_document_cache( $value ) {
$expiration_hours = get_option( 'elementor_element_cache_ttl', '' );
if ( empty( $expiration_hours ) || ! is_numeric( $expiration_hours ) ) {
$expiration_hours = '24';
}
$expiration_hours = absint( $expiration_hours );
$expiration = '+' . $expiration_hours . ' hours';
$data = [
'timeout' => strtotime( $expiration, current_time( 'timestamp' ) ),
'value' => $value,
];
$this->update_json_meta( static::CACHE_META_KEY, $data );
}
private function get_document_cache() {
$cache = $this->get_json_meta( static::CACHE_META_KEY );
if ( empty( $cache['timeout'] ) ) {
return false;
}
if ( current_time( 'timestamp' ) > $cache['timeout'] ) {
return false;
}
if ( ! is_array( $cache['value'] ) ) {
return false;
}
return $cache['value'];
}
protected function delete_cache() {
$this->delete_meta( static::CACHE_META_KEY );
}
private function should_store_cache_elements() {
static $should_store_cache_elements = null;
if ( null === $should_store_cache_elements ) {
$should_store_cache_elements = (
! is_admin()
&& ! Plugin::$instance->preview->is_preview_mode()
);
}
return $should_store_cache_elements;
}
protected function register_document_controls() {
$this->start_controls_section(
'document_settings',
[
'label' => esc_html__( 'General Settings', 'elementor' ),
'tab' => Controls_Manager::TAB_SETTINGS,
]
);
$this->add_control(
'post_title',
[
'label' => esc_html__( 'Title', 'elementor' ),
'type' => Controls_Manager::TEXT,
'default' => $this->post->post_title,
'label_block' => true,
]
);
$post_type_object = get_post_type_object( $this->post->post_type );
$can_publish = $post_type_object && current_user_can( $post_type_object->cap->publish_posts );
$is_published = self::STATUS_PUBLISH === $this->post->post_status || self::STATUS_PRIVATE === $this->post->post_status;
if ( $is_published || $can_publish || ! Plugin::$instance->editor->is_edit_mode() ) {
$statuses = $this->get_post_statuses();
if ( 'future' === $this->get_main_post()->post_status ) {
$statuses['future'] = esc_html__( 'Future', 'elementor' );
}
$this->add_control(
'post_status',
[
'label' => esc_html__( 'Status', 'elementor' ),
'type' => Controls_Manager::SELECT,
'default' => $this->get_main_post()->post_status,
'options' => $statuses,
]
);
}
$this->end_controls_section();
}
protected function get_post_statuses() {
return get_post_statuses();
}
protected function get_have_a_look_url() {
return $this->get_permalink();
}
public function handle_revisions_changed( $post_has_changed, $last_revision, $post ) {
// In case default, didn't determine the changes.
if ( ! $post_has_changed ) {
$last_revision_id = $last_revision->ID;
$last_revision_document = Plugin::instance()->documents->get( $last_revision_id );
$post_document = Plugin::instance()->documents->get( $post->ID );
$last_revision_settings = $last_revision_document->get_settings();
$post_settings = $post_document->get_settings();
// TODO: Its better to add crc32 signature for each revision and then only compare one part of the checksum.
$post_has_changed = $last_revision_settings !== $post_settings;
}
return $post_has_changed;
}
private function add_handle_revisions_changed_filter() {
add_filter( 'wp_save_post_revision_post_has_changed', [ $this, 'handle_revisions_changed' ], 10, 3 );
}
private function remove_handle_revisions_changed_filter() {
remove_filter( 'wp_save_post_revision_post_has_changed', [ $this, 'handle_revisions_changed' ] );
}
private function get_runtime_elements_iteration_actions() {
$runtime_elements_iteration_actions = [];
$elements_iteration_actions = $this->get_elements_iteration_actions();
foreach ( $elements_iteration_actions as $elements_iteration_action ) {
if ( $elements_iteration_action->is_action_needed() ) {
$runtime_elements_iteration_actions[] = $elements_iteration_action;
}
}
return $runtime_elements_iteration_actions;
}
private function iterate_elements( $elements, $elements_iteration_actions, $mode ) {
$unique_page_elements = [];
foreach ( $elements_iteration_actions as $elements_iteration_action ) {
$elements_iteration_action->set_mode( $mode );
}
Plugin::$instance->db->iterate_data( $elements, function( array $element_data ) use ( &$unique_page_elements, $elements_iteration_actions ) {
$element_type = 'widget' === $element_data['elType'] ? $element_data['widgetType'] : $element_data['elType'];
$element = Plugin::$instance->elements_manager->create_element_instance( $element_data );
if ( $element ) {
if ( ! in_array( $element_type, $unique_page_elements, true ) ) {
$unique_page_elements[] = $element_type;
foreach ( $elements_iteration_actions as $elements_iteration_action ) {
$elements_iteration_action->unique_element_action( $element );
}
}
foreach ( $elements_iteration_actions as $elements_iteration_action ) {
$elements_iteration_action->element_action( $element );
}
}
return $element_data;
} );
foreach ( $elements_iteration_actions as $elements_iteration_action ) {
$elements_iteration_action->after_elements_iteration();
}
}
private function get_elements_iteration_actions() {
if ( ! $this->elements_iteration_actions ) {
$this->elements_iteration_actions[] = new Assets_Iteration_Action( $this );
}
return $this->elements_iteration_actions;
}
}
base/background-process/wp-async-request.php 0000644 00000005605 14720700033 0015213 0 ustar 00 identifier = $this->prefix . '_' . $this->action;
add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
}
/**
* Set data used during the request
*
* @param array $data Data.
*
* @return $this
*/
public function data( $data ) {
$this->data = $data;
return $this;
}
/**
* Dispatch the async request
*
* @return array|\WP_Error
*/
public function dispatch() {
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
$args = $this->get_post_args();
return wp_remote_post( esc_url_raw( $url ), $args );
}
/**
* Get query args
*
* @return array
*/
protected function get_query_args() {
if ( property_exists( $this, 'query_args' ) ) {
return $this->query_args;
}
return array(
'action' => $this->identifier,
'nonce' => wp_create_nonce( $this->identifier ),
);
}
/**
* Get query URL
*
* @return string
*/
protected function get_query_url() {
if ( property_exists( $this, 'query_url' ) ) {
return $this->query_url;
}
return admin_url( 'admin-ajax.php' );
}
/**
* Get post args
*
* @return array
*/
protected function get_post_args() {
if ( property_exists( $this, 'post_args' ) ) {
return $this->post_args;
}
return array(
'timeout' => 0.01,
'blocking' => false,
'body' => $this->data,
'cookies' => $_COOKIE,
/** This filter is documented in wp-includes/class-wp-http-streams.php */
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
);
}
/**
* Maybe handle
*
* Check for correct nonce and pass to handler.
*/
public function maybe_handle() {
// Don't lock up other requests while processing
session_write_close();
check_ajax_referer( $this->identifier, 'nonce' );
$this->handle();
wp_die();
}
/**
* Handle
*
* Override this method to perform any actions required
* during the async request.
*/
abstract protected function handle();
}
base/background-process/wp-background-process.php 0000644 00000025510 14720700033 0016200 0 ustar 00 cron_hook_identifier = $this->identifier . '_cron';
$this->cron_interval_identifier = $this->identifier . '_cron_interval';
add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
}
/**
* Dispatch
*
* @access public
* @return array|\WP_Error
*/
public function dispatch() {
// Schedule the cron healthcheck.
$this->schedule_event();
// Perform remote post.
return parent::dispatch();
}
/**
* Push to queue
*
* @param mixed $data Data.
*
* @return $this
*/
public function push_to_queue( $data ) {
$this->data[] = $data;
return $this;
}
/**
* Save queue
*
* @return $this
*/
public function save() {
$key = $this->generate_key();
if ( ! empty( $this->data ) ) {
update_site_option( $key, $this->data );
}
return $this;
}
/**
* Update queue
*
* @param string $key Key.
* @param array $data Data.
*
* @return $this
*/
public function update( $key, $data ) {
if ( ! empty( $data ) ) {
update_site_option( $key, $data );
}
return $this;
}
/**
* Delete queue
*
* @param string $key Key.
*
* @return $this
*/
public function delete( $key ) {
delete_site_option( $key );
return $this;
}
/**
* Generate key
*
* Generates a unique key based on microtime. Queue items are
* given a unique key so that they can be merged upon save.
*
* @param int $length Length.
*
* @return string
*/
protected function generate_key( $length = 64 ) {
$unique = md5( microtime() . rand() );
$prepend = $this->identifier . '_batch_';
return substr( $prepend . $unique, 0, $length );
}
/**
* Maybe process queue
*
* Checks whether data exists within the queue and that
* the process is not already running.
*/
public function maybe_handle() {
// Don't lock up other requests while processing
session_write_close();
if ( $this->is_process_running() ) {
// Background process already running.
wp_die();
}
if ( $this->is_queue_empty() ) {
// No data to process.
wp_die();
}
check_ajax_referer( $this->identifier, 'nonce' );
$this->handle();
wp_die();
}
/**
* Is queue empty
*
* @return bool
*/
protected function is_queue_empty() {
global $wpdb;
$table = $wpdb->options;
$column = 'option_name';
if ( is_multisite() ) {
$table = $wpdb->sitemeta;
$column = 'meta_key';
}
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// Can't use placeholders for table/column names, it will be wrapped by a single quote (') instead of a backquote (`).
$count = $wpdb->get_var( $wpdb->prepare( "
SELECT COUNT(*)
FROM {$table}
WHERE {$column} LIKE %s
", $key ) );
// phpcs:enable
return ( $count > 0 ) ? false : true;
}
/**
* Is process running
*
* Check whether the current process is already running
* in a background process.
*/
protected function is_process_running() {
if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
// Process already running.
return true;
}
return false;
}
/**
* Lock process
*
* Lock the process so that multiple instances can't run simultaneously.
* Override if applicable, but the duration should be greater than that
* defined in the time_exceeded() method.
*/
protected function lock_process() {
$this->start_time = time(); // Set start time of current process.
$lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
$lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
}
/**
* Unlock process
*
* Unlock the process so that other instances can spawn.
*
* @return $this
*/
protected function unlock_process() {
delete_site_transient( $this->identifier . '_process_lock' );
return $this;
}
/**
* Get batch
*
* @return \stdClass Return the first batch from the queue
*/
protected function get_batch() {
global $wpdb;
$table = $wpdb->options;
$column = 'option_name';
$key_column = 'option_id';
$value_column = 'option_value';
if ( is_multisite() ) {
$table = $wpdb->sitemeta;
$column = 'meta_key';
$key_column = 'meta_id';
$value_column = 'meta_value';
}
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// Can't use placeholders for table/column names, it will be wrapped by a single quote (') instead of a backquote (`).
$query = $wpdb->get_row( $wpdb->prepare( "
SELECT *
FROM {$table}
WHERE {$column} LIKE %s
ORDER BY {$key_column} ASC
LIMIT 1
", $key ) );
// phpcs:enable
$batch = new \stdClass();
$batch->key = $query->$column;
$batch->data = maybe_unserialize( $query->$value_column );
return $batch;
}
/**
* Handle
*
* Pass each queue item to the task handler, while remaining
* within server memory and time limit constraints.
*/
protected function handle() {
$this->lock_process();
do {
$batch = $this->get_batch();
foreach ( $batch->data as $key => $value ) {
$task = $this->task( $value );
if ( false !== $task ) {
$batch->data[ $key ] = $task;
} else {
unset( $batch->data[ $key ] );
}
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
// Batch limits reached.
break;
}
}
// Update or delete current batch.
if ( ! empty( $batch->data ) ) {
$this->update( $batch->key, $batch->data );
} else {
$this->delete( $batch->key );
}
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
$this->unlock_process();
// Start next batch or complete process.
if ( ! $this->is_queue_empty() ) {
$this->dispatch();
} else {
$this->complete();
}
wp_die();
}
/**
* Memory exceeded
*
* Ensures the batch process never exceeds 90%
* of the maximum WordPress memory.
*
* @return bool
*/
protected function memory_exceeded() {
$memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
$current_memory = memory_get_usage( true );
$return = false;
if ( $current_memory >= $memory_limit ) {
$return = true;
}
return apply_filters( $this->identifier . '_memory_exceeded', $return );
}
/**
* Get memory limit
*
* @return int
*/
protected function get_memory_limit() {
if ( function_exists( 'ini_get' ) ) {
$memory_limit = ini_get( 'memory_limit' );
} else {
// Sensible default.
$memory_limit = '128M';
}
if ( ! $memory_limit || -1 === intval( $memory_limit ) ) {
// Unlimited, set to 32GB.
$memory_limit = '32000M';
}
return intval( $memory_limit ) * 1024 * 1024;
}
/**
* Time exceeded.
*
* Ensures the batch never exceeds a sensible time limit.
* A timeout limit of 30s is common on shared hosting.
*
* @return bool
*/
protected function time_exceeded() {
$finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
$return = false;
if ( time() >= $finish ) {
$return = true;
}
return apply_filters( $this->identifier . '_time_exceeded', $return );
}
/**
* Complete.
*
* Override if applicable, but ensure that the below actions are
* performed, or, call parent::complete().
*/
protected function complete() {
// Unschedule the cron healthcheck.
$this->clear_scheduled_event();
}
/**
* Schedule cron healthcheck
*
* @access public
* @param mixed $schedules Schedules.
* @return mixed
*/
public function schedule_cron_healthcheck( $schedules ) {
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
if ( property_exists( $this, 'cron_interval' ) ) {
$interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
}
// Adds every 5 minutes to the existing schedules.
$schedules[ $this->identifier . '_cron_interval' ] = array(
'interval' => MINUTE_IN_SECONDS * $interval,
'display' => sprintf(
/* translators: %d: Interval in minutes. */
esc_html__( 'Every %d minutes', 'elementor' ),
$interval,
),
);
return $schedules;
}
/**
* Handle cron healthcheck
*
* Restart the background process if not already running
* and data exists in the queue.
*/
public function handle_cron_healthcheck() {
if ( $this->is_process_running() ) {
// Background process already running.
exit;
}
if ( $this->is_queue_empty() ) {
// No data to process.
$this->clear_scheduled_event();
exit;
}
$this->handle();
exit;
}
/**
* Schedule event
*/
protected function schedule_event() {
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
}
}
/**
* Clear scheduled event
*/
protected function clear_scheduled_event() {
$timestamp = wp_next_scheduled( $this->cron_hook_identifier );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
}
}
/**
* Cancel Process
*
* Stop processing queue items, clear cronjob and delete batch.
*
*/
public function cancel_process() {
if ( ! $this->is_queue_empty() ) {
$batch = $this->get_batch();
$this->delete( $batch->key );
wp_clear_scheduled_hook( $this->cron_hook_identifier );
}
}
/**
* Task
*
* Override this method to perform any actions required on each
* queue item. Return the modified item for further processing
* in the next pass through. Or, return false to remove the
* item from the queue.
*
* @param mixed $item Queue item to iterate over.
*
* @return mixed
*/
abstract protected function task( $item );
}
base/db-upgrades-manager.php 0000644 00000013751 14720700033 0011777 0 ustar 00 query_limit;
}
public function set_query_limit( $limit ) {
$this->query_limit = $limit;
}
public function get_current_version() {
if ( null === $this->current_version ) {
$this->current_version = get_option( $this->get_version_option_name() );
}
return $this->current_version;
}
public function should_upgrade() {
$current_version = $this->get_current_version();
// It's a new install.
if ( ! $current_version ) {
$this->update_db_version();
return false;
}
return version_compare( $this->get_new_version(), $current_version, '>' );
}
public function on_runner_start() {
parent::on_runner_start();
if ( ! defined( 'IS_ELEMENTOR_UPGRADE' ) ) {
define( 'IS_ELEMENTOR_UPGRADE', true );
}
}
public function on_runner_complete( $did_tasks = false ) {
$logger = Plugin::$instance->logger->get_logger();
$logger->info( 'Elementor data updater process has been completed.', [
'meta' => [
'plugin' => $this->get_plugin_label(),
'from' => $this->current_version,
'to' => $this->get_new_version(),
],
] );
$this->clear_cache();
$this->update_db_version();
if ( $did_tasks ) {
$this->add_flag( 'completed' );
}
}
protected function clear_cache() {
Plugin::$instance->files_manager->clear_cache();
}
public function admin_notice_start_upgrade() {
/**
* @var Admin_Notices $admin_notices
*/
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
$options = [
'title' => $this->get_updater_label(),
'description' => esc_html__( 'Your site database needs to be updated to the latest version.', 'elementor' ),
'type' => 'error',
'icon' => false,
'button' => [
'text' => esc_html__( 'Update Now', 'elementor' ),
'url' => $this->get_start_action_url(),
'class' => 'e-button e-button--cta',
],
];
$admin_notices->print_admin_notice( $options );
}
public function admin_notice_upgrade_is_running() {
/**
* @var Admin_Notices $admin_notices
*/
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
$options = [
'title' => $this->get_updater_label(),
'description' => esc_html__( 'Database update process is running in the background. Taking a while?', 'elementor' ),
'type' => 'warning',
'icon' => false,
'button' => [
'text' => esc_html__( 'Click here to run it now', 'elementor' ),
'url' => $this->get_continue_action_url(),
'class' => 'e-button e-button--primary',
],
];
$admin_notices->print_admin_notice( $options );
}
public function admin_notice_upgrade_is_completed() {
$this->delete_flag( 'completed' );
$message = esc_html__( 'The database update process is now complete. Thank you for updating to the latest version!', 'elementor' );
/**
* @var Admin_Notices $admin_notices
*/
$admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
$options = [
'description' => '' . $this->get_updater_label() . ' - ' . $message,
'type' => 'success',
'icon' => false,
];
$admin_notices->print_admin_notice( $options );
}
/**
* @access protected
*/
protected function start_run() {
$updater = $this->get_task_runner();
if ( $updater->is_running() ) {
return;
}
$upgrade_callbacks = $this->get_upgrade_callbacks();
if ( empty( $upgrade_callbacks ) ) {
$this->on_runner_complete();
return;
}
$this->clear_cache();
foreach ( $upgrade_callbacks as $callback ) {
$updater->push_to_queue( [
'callback' => $callback,
] );
}
$updater->save()->dispatch();
Plugin::$instance->logger->get_logger()->info( 'Elementor data updater process has been queued.', [
'meta' => [
'plugin' => $this->get_plugin_label(),
'from' => $this->current_version,
'to' => $this->get_new_version(),
],
] );
}
protected function update_db_version() {
update_option( $this->get_version_option_name(), $this->get_new_version() );
}
public function get_upgrade_callbacks() {
$prefix = '_v_';
$upgrades_class = $this->get_upgrades_class();
$upgrades_reflection = new \ReflectionClass( $upgrades_class );
$callbacks = [];
foreach ( $upgrades_reflection->getMethods() as $method ) {
$method_name = $method->getName();
if ( '_on_each_version' === $method_name ) {
$callbacks[] = [ $upgrades_class, $method_name ];
continue;
}
if ( false === strpos( $method_name, $prefix ) ) {
continue;
}
if ( ! preg_match_all( "/$prefix(\d+_\d+_\d+)/", $method_name, $matches ) ) {
continue;
}
$method_version = str_replace( '_', '.', $matches[1][0] );
if ( ! version_compare( $method_version, $this->current_version, '>' ) ) {
continue;
}
$callbacks[] = [ $upgrades_class, $method_name ];
}
return $callbacks;
}
public function __construct() {
// If upgrade is completed - show the notice only for admins.
// Note: in this case `should_upgrade` returns false, because it's already upgraded.
if ( is_admin() && current_user_can( 'update_plugins' ) && $this->get_flag( 'completed' ) ) {
add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_completed' ] );
}
if ( ! $this->should_upgrade() ) {
return;
}
$updater = $this->get_task_runner();
$this->start_run();
if ( $updater->is_running() && current_user_can( 'update_plugins' ) ) {
add_action( 'admin_notices', [ $this, 'admin_notice_upgrade_is_running' ] );
}
parent::__construct();
}
}
base/base-object.php 0000644 00000013064 14720700033 0010345 0 ustar 00 ensure_settings();
return self::get_items( $this->settings, $setting );
}
/**
* Set settings.
*
* @since 2.3.0
* @access public
*
* @param array|string $key If key is an array, the settings are overwritten by that array. Otherwise, the
* settings of the key will be set to the given `$value` param.
*
* @param mixed $value Optional. Default is null.
*/
final public function set_settings( $key, $value = null ) {
$this->ensure_settings();
if ( is_array( $key ) ) {
$this->settings = $key;
} else {
$this->settings[ $key ] = $value;
}
}
/**
* Delete setting.
*
* Deletes the settings array or a specific key of the settings array if `$key` is specified.
* @since 2.3.0
* @access public
*
* @param string $key Optional. Default is null.
*/
public function delete_setting( $key = null ) {
if ( $key ) {
unset( $this->settings[ $key ] );
} else {
$this->settings = [];
}
}
final public function merge_properties( array $default_props, array $custom_props, array $allowed_props_keys = [] ) {
$props = array_replace_recursive( $default_props, $custom_props );
if ( $allowed_props_keys ) {
$props = array_intersect_key( $props, array_flip( $allowed_props_keys ) );
}
return $props;
}
/**
* Get items.
*
* Utility method that receives an array with a needle and returns all the
* items that match the needle. If needle is not defined the entire haystack
* will be returned.
*
* @since 2.3.0
* @access protected
* @static
*
* @param array $haystack An array of items.
* @param string $needle Optional. Needle. Default is null.
*
* @return mixed The whole haystack or the needle from the haystack when requested.
*/
final protected static function get_items( array $haystack, $needle = null ) {
if ( $needle ) {
return isset( $haystack[ $needle ] ) ? $haystack[ $needle ] : null;
}
return $haystack;
}
/**
* Get init settings.
*
* Used to define the default/initial settings of the object. Inheriting classes may implement this method to define
* their own default/initial settings.
*
* @since 2.3.0
* @access protected
*
* @return array
*/
protected function get_init_settings() {
return [];
}
/**
* Ensure settings.
*
* Ensures that the `$settings` member is initialized
*
* @since 2.3.0
* @access private
*/
private function ensure_settings() {
if ( null === $this->settings ) {
$this->settings = $this->get_init_settings();
}
}
/**
* Has Own Method
*
* Used for check whether the method passed as a parameter was declared in the current instance or inherited.
* If a base_class_name is passed, it checks whether the method was declared in that class. If the method's
* declaring class is the class passed as $base_class_name, it returns false. Otherwise (method was NOT declared
* in $base_class_name), it returns true.
*
* Example #1 - only $method_name is passed:
* The initial declaration of `register_controls()` happens in the `Controls_Stack` class. However, all
* widgets which have their own controls declare this function as well, overriding the original
* declaration. If `has_own_method()` would be called by a Widget's class which implements `register_controls()`,
* with 'register_controls' passed as the first parameter - `has_own_method()` will return true. If the Widget
* does not declare `register_controls()`, `has_own_method()` will return false.
*
* Example #2 - both $method_name and $base_class_name are passed
* In this example, the widget class inherits from a base class `Widget_Base`, and the base implements
* `register_controls()` to add certain controls to all widgets inheriting from it. `has_own_method()` is called by
* the widget, with the string 'register_controls' passed as the first parameter, and 'Elementor\Widget_Base' (its full name
* including the namespace) passed as the second parameter. If the widget class implements `register_controls()`,
* `has_own_method` will return true. If the widget class DOESN'T implement `register_controls()`, it will return
* false (because `Widget_Base` is the declaring class for `register_controls()`, and not the class that called
* `has_own_method()`).
*
* @since 3.1.0
*
* @param string $method_name
* @param string $base_class_name
*
* @return bool True if the method was declared by the current instance, False if it was inherited.
*/
public function has_own_method( $method_name, $base_class_name = null ) {
try {
$reflection_method = new \ReflectionMethod( $this, $method_name );
// If a ReflectionMethod is successfully created, get its declaring class.
$declaring_class = $reflection_method->getDeclaringClass();
} catch ( \Exception $e ) {
return false;
}
if ( $base_class_name ) {
return $base_class_name !== $declaring_class->name;
}
return get_called_class() === $declaring_class->name;
}
}
base/elements-iteration-actions/base.php 0000644 00000003307 14720700033 0014346 0 ustar 00 mode = $mode;
}
public function __construct( $document ) {
$this->document = $document;
}
}
base/elements-iteration-actions/assets.php 0000644 00000012637 14720700033 0014744 0 ustar 00 get_active_settings();
$controls = $element_data->get_controls();
$element_assets = $this->get_assets( $settings, $controls );
$element_assets_depend = [
'styles' => $element_data->get_style_depends(),
'scripts' => $element_data->get_script_depends(),
];
if ( $element_assets_depend ) {
foreach ( $element_assets_depend as $assets_type => $assets ) {
if ( empty( $assets ) ) {
continue;
}
if ( ! isset( $element_assets[ $assets_type ] ) ) {
$element_assets[ $assets_type ] = [];
}
foreach ( $assets as $asset_name ) {
if ( ! in_array( $asset_name, $element_assets[ $assets_type ], true ) ) {
$element_assets[ $assets_type ][] = $asset_name;
}
}
}
}
if ( $element_assets ) {
$this->update_page_assets( $element_assets );
}
}
public function is_action_needed() {
// No need to evaluate in preview mode, will be made in the saving process.
if ( Plugin::$instance->preview->is_preview_mode() ) {
return false;
}
$page_assets = $this->get_saved_page_assets();
// When $page_assets is array it means that the assets registration has already been made at least once.
if ( is_array( $page_assets ) ) {
return false;
}
return true;
}
public function after_elements_iteration() {
// In case that the page assets value is empty, it should still be saved as an empty array as an indication that at lease one iteration has occurred.
if ( ! is_array( $this->page_assets ) ) {
$this->page_assets = [];
}
$this->get_document_assets();
// Saving the page assets data.
$this->document->update_meta( self::ASSETS_META_KEY, $this->page_assets );
if ( 'render' === $this->mode && $this->page_assets ) {
Plugin::$instance->assets_loader->enable_assets( $this->page_assets );
}
}
private function get_saved_page_assets( $force_meta_fetch = false ) {
if ( ! is_array( $this->saved_page_assets ) || $force_meta_fetch ) {
$this->saved_page_assets = $this->document->get_meta( self::ASSETS_META_KEY );
}
return $this->saved_page_assets;
}
private function update_page_assets( $new_assets ) {
if ( ! is_array( $this->page_assets ) ) {
$this->page_assets = [];
}
foreach ( $new_assets as $assets_type => $assets_type_data ) {
if ( ! isset( $this->page_assets[ $assets_type ] ) ) {
$this->page_assets[ $assets_type ] = [];
}
foreach ( $assets_type_data as $asset_name ) {
if ( ! in_array( $asset_name, $this->page_assets[ $assets_type ], true ) ) {
$this->page_assets[ $assets_type ][] = $asset_name;
}
}
}
}
private function get_assets( $settings, $controls ) {
$assets = [];
foreach ( $settings as $setting_key => $setting ) {
if ( ! isset( $controls[ $setting_key ] ) ) {
continue;
}
$control = $controls[ $setting_key ];
// Enabling assets loading from the registered control fields.
if ( ! empty( $control['assets'] ) ) {
foreach ( $control['assets'] as $assets_type => $dependencies ) {
foreach ( $dependencies as $dependency ) {
if ( ! empty( $dependency['conditions'] ) ) {
$is_condition_fulfilled = Conditions::check( $dependency['conditions'], $settings );
if ( ! $is_condition_fulfilled ) {
continue;
}
}
if ( ! isset( $assets[ $assets_type ] ) ) {
$assets[ $assets_type ] = [];
}
$assets[ $assets_type ][] = $dependency['name'];
}
}
}
// Enabling assets loading from the control object.
$control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
$control_conditional_assets = $control_obj::get_assets( $setting );
if ( $control_conditional_assets ) {
foreach ( $control_conditional_assets as $assets_type => $dependencies ) {
foreach ( $dependencies as $dependency ) {
if ( ! isset( $assets[ $assets_type ] ) ) {
$assets[ $assets_type ] = [];
}
$assets[ $assets_type ][] = $dependency;
}
}
}
}
return $assets;
}
private function get_document_assets() {
$document_id = $this->document->get_post()->ID;
// Getting the document instance in order to get the most updated settings.
$updated_document = Plugin::$instance->documents->get( $document_id, false );
$document_settings = $updated_document->get_settings();
$document_controls = $this->document->get_controls();
$document_assets = $this->get_assets( $document_settings, $document_controls );
if ( $document_assets ) {
$this->update_page_assets( $document_assets );
}
}
public function __construct( $document ) {
parent::__construct( $document );
// No need to enable assets in preview mode, all assets will be loaded by default by the assets loader.
if ( Plugin::$instance->preview->is_preview_mode() ) {
return;
}
$page_assets = $this->get_saved_page_assets();
// If $page_assets is not empty then enabling the assets for loading.
if ( $page_assets ) {
Plugin::$instance->assets_loader->enable_assets( $page_assets );
}
}
}
modules-manager.php 0000644 00000006403 14720700033 0010334 0 ustar 00 get_modules_namespace_prefix();
foreach ( $this->get_modules_names() as $module_name ) {
$class_name = str_replace( '-', ' ', $module_name );
$class_name = str_replace( ' ', '', ucwords( $class_name ) );
$class_name = $modules_namespace_prefix . '\\Modules\\' . $class_name . '\Module';
/** @var Module $class_name */
$experimental_data = $class_name::get_experimental_data();
if ( $experimental_data ) {
Plugin::$instance->experiments->add_feature( $experimental_data );
if ( ! Plugin::$instance->experiments->is_feature_active( $experimental_data['name'] ) ) {
continue;
}
}
if ( $class_name::is_active() ) {
$this->modules[ $module_name ] = $class_name::instance();
}
}
}
/**
* Get modules names.
*
* Retrieve the modules names.
*
* @since 2.0.0
* @access public
*
* @return string[] Modules names.
*/
public function get_modules_names() {
return [
'admin-bar',
'history',
'library',
'dynamic-tags',
'page-templates',
'gutenberg',
'wp-cli',
'safe-mode',
'ai',
'notifications',
'usage',
'dev-tools',
'landing-pages',
'compatibility-tag',
'generator-tag',
'elements-color-picker',
'shapes',
'favorites',
'admin-top-bar',
'checklist',
'element-manager',
'nested-elements',
// Depends on Nested Elements module
'nested-tabs',
'nested-accordion',
'container-converter',
'web-cli',
'promotions',
'notes',
'performance-lab',
'lazyload',
'image-loading-optimization',
'kit-elements-defaults',
'announcements',
'editor-app-bar',
'site-navigation',
'styleguide',
'element-cache',
'apps',
'home',
'link-in-bio',
'floating-buttons',
'content-sanitizer',
'editor-events',
'atomic-widgets',
'wc-product-editor',
'checklist',
];
}
/**
* Get modules.
*
* Retrieve all the registered modules or a specific module.
*
* @since 2.0.0
* @access public
*
* @param string $module_name Module name.
*
* @return null|Module|Module[] All the registered modules or a specific module.
*/
public function get_modules( $module_name ) {
if ( $module_name ) {
if ( isset( $this->modules[ $module_name ] ) ) {
return $this->modules[ $module_name ];
}
return null;
}
return $this->modules;
}
/**
* Get modules namespace prefix.
*
* Retrieve the modules namespace prefix.
*
* @since 2.0.0
* @access protected
*
* @return string Modules namespace prefix.
*/
protected function get_modules_namespace_prefix() {
return 'Elementor';
}
}
debug/inspector.php 0000644 00000006467 14720700033 0010362 0 ustar 00 is_enabled = is_null( $option ) ? $is_debug : 'enable' === $option;
if ( $this->is_enabled ) {
add_action( 'admin_bar_menu', [ $this, 'add_menu_in_admin_bar' ], 201 );
}
add_action( 'elementor/admin/after_create_settings/' . Tools::PAGE_ID, [ $this, 'register_admin_tools_fields' ], 50 );
}
/**
* @since 2.1.3
* @access public
*/
public function is_enabled() {
return $this->is_enabled;
}
/**
* @since 2.1.3
* @access public
*/
public function register_admin_tools_fields( Tools $tools ) {
$tools->add_fields( Settings::TAB_GENERAL, 'tools', [
'enable_inspector' => [
'label' => esc_html__( 'Debug Bar', 'elementor' ),
'field_args' => [
'type' => 'select',
'std' => $this->is_enabled ? 'enable' : '',
'options' => [
'' => esc_html__( 'Disable', 'elementor' ),
'enable' => esc_html__( 'Enable', 'elementor' ),
],
'desc' => esc_html__( 'Debug Bar adds an admin bar menu that lists all the templates that are used on a page that is being displayed.', 'elementor' ),
],
],
] );
}
/**
* @since 2.1.2
* @access public
*/
public function parse_template_path( $template ) {
// `untrailingslashit` for windows path style.
if ( 0 === strpos( $template, untrailingslashit( ELEMENTOR_PATH ) ) ) {
return 'Elementor - ' . basename( $template );
}
if ( 0 === strpos( $template, get_stylesheet_directory() ) ) {
return wp_get_theme()->get( 'Name' ) . ' - ' . basename( $template );
}
$plugins_dir = dirname( ELEMENTOR_PATH );
if ( 0 === strpos( $template, $plugins_dir ) ) {
return ltrim( str_replace( $plugins_dir, '', $template ), '/\\' );
}
return str_replace( WP_CONTENT_DIR, '', $template );
}
/**
* @since 2.1.2
* @access public
*/
public function add_log( $module, $title, $url = '' ) {
if ( ! $this->is_enabled ) {
return;
}
if ( ! isset( $this->log[ $module ] ) ) {
$this->log[ $module ] = [];
}
$this->log[ $module ][] = [
'title' => $title,
'url' => $url,
];
}
/**
* @since 2.1.2
* @access public
*/
public function add_menu_in_admin_bar( \WP_Admin_Bar $wp_admin_bar ) {
if ( empty( $this->log ) ) {
return;
}
$wp_admin_bar->add_node( [
'id' => 'elementor_inspector',
'title' => esc_html__( 'Elementor Debugger', 'elementor' ),
] );
foreach ( $this->log as $module => $log ) {
$module_id = sanitize_key( $module );
$wp_admin_bar->add_menu( [
'id' => 'elementor_inspector_' . $module_id,
'parent' => 'elementor_inspector',
'title' => $module,
] );
foreach ( $log as $index => $row ) {
$url = $row['url'];
unset( $row['url'] );
$wp_admin_bar->add_menu( [
'id' => 'elementor_inspector_log_' . $module_id . '_' . $index,
'parent' => 'elementor_inspector_' . $module_id,
'href' => $url,
'title' => implode( ' > ', $row ),
'meta' => [
'target' => '_blank',
],
] );
}
}
}
}
debug/classes/shop-page-edit.php 0000644 00000001305 14720700033 0012601 0 ustar 00 message = esc_html__( 'Your site\'s .htaccess file appears to be missing.', 'elementor' );
}
public function run() {
$safe_mode_enabled = get_option( Safe_Mode::OPTION_ENABLED, '' );
if ( empty( $safe_mode_enabled ) || is_multisite() ) {
return true;
}
$permalink_structure = get_option( 'permalink_structure' );
if ( empty( $permalink_structure ) || empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
return true;
}
$server = strtoupper( Utils::get_super_global_value( $_SERVER, 'SERVER_SOFTWARE' ) );
if ( strstr( $server, 'APACHE' ) ) {
$htaccess_file = get_home_path() . '.htaccess';
/* translators: %s: Path to .htaccess file. */
$this->message .= ' ' . sprintf( esc_html__( 'File Path: %s', 'elementor' ), $htaccess_file ) . ' ';
return file_exists( $htaccess_file );
}
return true;
}
public function get_name() {
return 'apache-htaccess';
}
public function get_message() {
return $this->message;
}
public function get_help_doc_url() {
return 'https://go.elementor.com/preview-not-loaded/#htaccess';
}
}
debug/classes/inspection-base.php 0000644 00000000766 14720700033 0013070 0 ustar 00 exists();
}
public function get_name() {
return 'theme-missing';
}
public function get_message() {
return esc_html__( 'Some of your theme files are missing.', 'elementor' );
}
public function get_help_doc_url() {
return 'https://go.elementor.com/preview-not-loaded/#theme-files';
}
}
debug/loading-inspection-manager.php 0000644 00000003370 14720700033 0013540 0 ustar 00 inspections['theme-missing'] = new Theme_Missing();
$this->inspections['htaccess'] = new Htaccess();
$is_editing_shop_page = Utils::get_super_global_value( $_GET, 'post' ) == get_option( 'woocommerce_shop_page_id' );
if ( $is_editing_shop_page ) {
$this->inspections['shop-page-edit'] = new Shop_Page_Edit();
}
}
/**
* @param Inspection_Base $inspection
*/
public function register_inspection( $inspection ) {
$this->inspections[ $inspection->get_name() ] = $inspection;
}
public function run_inspections() {
$debug_data = [
'message' => esc_html__( "We’re sorry, but something went wrong. Click on 'Learn more' and follow each of the steps to quickly solve it.", 'elementor' ),
'header' => esc_html__( 'The preview could not be loaded', 'elementor' ),
'doc_url' => 'https://go.elementor.com/preview-not-loaded/',
];
foreach ( $this->inspections as $inspection ) {
if ( ! $inspection->run() ) {
$debug_data = [
'message' => $inspection->get_message(),
'header' => $inspection->get_header_message(),
'doc_url' => $inspection->get_help_doc_url(),
'error' => true,
];
break;
}
}
return $debug_data;
}
}
admin/admin-notices.php 0000644 00000043472 14720700033 0011105 0 ustar 00 install_time ) {
$this->install_time = Plugin::$instance->get_install_time();
}
return $this->install_time;
}
private function get_elementor_pages_count() {
if ( null === $this->elementor_pages_count ) {
$elementor_pages = new \WP_Query( [
'no_found_rows' => true,
'post_type' => 'any',
'post_status' => 'publish',
'fields' => 'ids',
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'meta_key' => '_elementor_edit_mode',
'meta_value' => 'builder',
] );
$this->elementor_pages_count = $elementor_pages->post_count;
}
return $this->elementor_pages_count;
}
private function notice_api_upgrade_plugin() {
$upgrade_notice = Api::get_upgrade_notice();
if ( empty( $upgrade_notice ) ) {
return false;
}
if ( ! current_user_can( 'update_plugins' ) ) {
return false;
}
if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true ) ) {
return false;
}
// Check for upgrades.
$update_plugins = get_site_transient( 'update_plugins' );
$has_remote_update_package = ! ( empty( $update_plugins ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ] ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ]->package ) );
if ( ! $has_remote_update_package && empty( $upgrade_notice['update_link'] ) ) {
return false;
}
if ( $has_remote_update_package ) {
$product = $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ];
$details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $product->slug . '§ion=changelog&TB_iframe=true&width=600&height=800' );
$upgrade_url = wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' . ELEMENTOR_PLUGIN_BASE ), 'upgrade-plugin_' . ELEMENTOR_PLUGIN_BASE );
$new_version = $product->new_version;
} else {
$upgrade_url = $upgrade_notice['update_link'];
$details_url = $upgrade_url;
$new_version = $upgrade_notice['version'];
}
// Check if upgrade messages should be shown.
if ( version_compare( ELEMENTOR_VERSION, $upgrade_notice['version'], '>=' ) ) {
return false;
}
$notice_id = 'upgrade_notice_' . $upgrade_notice['version'];
if ( User::is_user_notice_viewed( $notice_id ) ) {
return false;
}
$message = sprintf(
/* translators: 1: Details URL, 2: Accessibility text, 3: Version number, 4: Update URL, 5: Accessibility text. */
__( 'There is a new version of Elementor Page Builder available. View version %3$s details or update now.', 'elementor' ),
esc_url( $details_url ),
esc_attr( sprintf(
/* translators: %s: Elementor version. */
__( 'View Elementor version %s details', 'elementor' ),
$new_version
) ),
$new_version,
esc_url( $upgrade_url ),
esc_attr( esc_html__( 'Update Now', 'elementor' ) )
);
$options = [
'title' => esc_html__( 'Update Notification', 'elementor' ),
'description' => $message,
'button' => [
'icon_classes' => 'dashicons dashicons-update',
'text' => esc_html__( 'Update Now', 'elementor' ),
'url' => $upgrade_url,
],
'id' => $notice_id,
];
$this->print_admin_notice( $options );
return true;
}
private function notice_api_notice() {
$admin_notice = Api::get_admin_notice();
if ( empty( $admin_notice ) ) {
return false;
}
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true ) ) {
return false;
}
$notice_id = 'admin_notice_api_' . $admin_notice['notice_id'];
if ( User::is_user_notice_viewed( $notice_id ) ) {
return false;
}
$options = [
'title' => esc_html__( 'Update Notification', 'elementor' ),
'description' => $admin_notice['notice_text'],
'id' => $notice_id,
];
$this->print_admin_notice( $options );
return true;
}
private function notice_tracker() {
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
// Show tracker notice after 24 hours from installed time.
if ( strtotime( '+24 hours', $this->get_install_time() ) > time() ) {
return false;
}
if ( '1' === get_option( 'elementor_tracker_notice' ) ) {
return false;
}
if ( Tracker::is_allow_track() ) {
return false;
}
if ( 2 > $this->get_elementor_pages_count() ) {
return false;
}
// TODO: Skip for development env.
$optin_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_into' ), 'opt_into' );
$optout_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_out' ), 'opt_out' );
$tracker_description_text = esc_html__( 'Become a super contributor by opting in to share non-sensitive plugin data and to receive periodic email updates from us.', 'elementor' );
/**
* Tracker admin description text.
*
* Filters the admin notice text for non-sensitive data collection.
*
* @since 1.0.0
*
* @param string $tracker_description_text Description text displayed in admin notice.
*/
$tracker_description_text = apply_filters( 'elementor/tracker/admin_description_text', $tracker_description_text );
$message = esc_html( $tracker_description_text ) . ' ' . esc_html__( 'Learn more.', 'elementor' ) . '';
$options = [
'title' => esc_html__( 'Love using Elementor?', 'elementor' ),
'description' => $message,
'button' => [
'text' => esc_html__( 'Sure! I\'d love to help', 'elementor' ),
'url' => $optin_url,
'type' => 'cta',
],
'button_secondary' => [
'text' => esc_html__( 'No thanks', 'elementor' ),
'url' => $optout_url,
'variant' => 'outline',
'type' => 'cta',
],
];
$this->print_admin_notice( $options );
return true;
}
private function notice_rate_us_feedback() {
$notice_id = 'rate_us_feedback';
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
if ( 'dashboard' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
return false;
}
if ( 10 >= $this->get_elementor_pages_count() ) {
return false;
}
$dismiss_url = add_query_arg( [
'action' => 'elementor_set_admin_notice_viewed',
'notice_id' => esc_attr( $notice_id ),
], admin_url( 'admin-post.php' ) );
$options = [
'title' => esc_html__( 'Congrats!', 'elementor' ),
'description' => esc_html__( 'You created over 10 pages with Elementor. Great job! If you can spare a minute, please help us by leaving a five star review on WordPress.org.', 'elementor' ),
'id' => $notice_id,
'button' => [
'text' => esc_html__( 'Happy To Help', 'elementor' ),
'url' => 'https://go.elementor.com/admin-review/',
'new_tab' => true,
'type' => 'cta',
],
'button_secondary' => [
'text' => esc_html__( 'Hide Notification', 'elementor' ),
'classes' => [ 'e-notice-dismiss' ],
'url' => esc_url_raw( $dismiss_url ),
'new_tab' => true,
'type' => 'cta',
],
];
$this->print_admin_notice( $options );
return true;
}
private function notice_role_manager_promote() {
$notice_id = 'role_manager_promote';
if ( Utils::has_pro() ) {
return false;
}
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
if ( 'elementor_page_elementor-role-manager' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
return false;
}
$users = new \WP_User_Query( [
'fields' => 'ID',
'number' => 10,
] );
if ( 5 > $users->get_total() ) {
return false;
}
$options = [
'title' => esc_html__( 'Managing a multi-user site?', 'elementor' ),
'description' => esc_html__( 'With Elementor Pro, you can control user access and make sure no one messes up your design.', 'elementor' ),
'id' => $notice_id,
'button' => [
'text' => esc_html__( 'Learn More', 'elementor' ),
'url' => 'https://go.elementor.com/plugin-promotion-role-manager/',
'new_tab' => true,
'type' => 'cta',
],
];
$options = Filtered_Promotions_Manager::get_filtered_promotion_data( $options, 'core/admin/notice_role_manager_promote', 'button', 'url' );
$this->print_admin_notice( $options );
return true;
}
private function notice_experiment_promotion() {
$notice_id = 'experiment_promotion';
if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
return false;
}
$experiments = Plugin::$instance->experiments;
$is_all_performance_features_active = (
$experiments->is_feature_active( 'e_element_cache' ) &&
$experiments->is_feature_active( 'e_font_icon_svg' )
);
if ( $is_all_performance_features_active ) {
return false;
}
$options = [
'title' => esc_html__( 'Improve your site’s performance score.', 'elementor' ),
'description' => esc_html__( 'With our experimental speed boosting features you can go faster than ever before. Look for the Performance label on our Experiments page and activate those experiments to improve your site loading speed.', 'elementor' ),
'id' => $notice_id,
'button' => [
'text' => esc_html__( 'Try it out', 'elementor' ),
'url' => Settings::get_settings_tab_url( 'experiments' ),
'type' => 'cta',
],
'button_secondary' => [
'text' => esc_html__( 'Learn more', 'elementor' ),
'url' => 'https://go.elementor.com/wp-dash-experiment-promotion/',
'new_tab' => true,
'type' => 'cta',
],
];
$this->print_admin_notice( $options );
return true;
}
private function notice_design_not_appearing() {
$installs_history = get_option( 'elementor_install_history', [] );
$is_first_install = 1 === count( $installs_history );
if ( $is_first_install || ! current_user_can( 'update_plugins' ) ) {
return false;
}
$notice_id = 'design_not_appearing';
$notice = User::get_user_notices()[ $notice_id ] ?? [];
$notice_version = $notice['meta']['version'] ?? null;
$is_version_changed = $this->get_elementor_version() !== $notice_version;
if ( $is_version_changed ) {
User::set_user_notice( $notice_id, false, [ 'version' => $this->get_elementor_version() ] );
}
if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard', 'update-core', 'plugins' ], true ) ) {
return false;
}
if ( User::is_user_notice_viewed( $notice_id ) ) {
return false;
}
$options = [
'title' => esc_html__( 'The version was updated successfully!', 'elementor' ),
'description' => sprintf(
esc_html__( 'Encountering issues after updating the version? Don’t worry - we’ve collected all the fixes for troubleshooting common issues. %1$sFind a solution%2$s', 'elementor' ),
'',
''
),
'id' => $notice_id,
];
$excluded_pages = [];
$this->print_admin_notice( $options, $excluded_pages );
return true;
}
// For testing purposes
public function get_elementor_version() {
return ELEMENTOR_VERSION;
}
private function notice_plugin_image_optimization() {
$notice_id = 'plugin_image_optimization';
if ( 'upload' !== $this->current_screen_id ) {
return false;
}
if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
return false;
}
$attachments = new \WP_Query( [
'post_type' => 'attachment',
'post_status' => 'any',
'fields' => 'ids',
] );
if ( 1 > $attachments->found_posts ) {
return false;
}
$plugin_file_path = 'image-optimization/image-optimization.php';
$plugin_slug = 'image-optimization';
if ( is_plugin_active( $plugin_file_path ) ) {
return false;
}
if ( $this->is_plugin_installed( $plugin_file_path ) ) {
$url = wp_nonce_url( 'plugins.php?action=activate&plugin=' . $plugin_file_path . '&plugin_status=all&paged=1&s', 'activate-plugin_' . $plugin_file_path );
$cta_text = esc_html__( 'Activate Plugin', 'elementor' );
} else {
$url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
$cta_text = esc_html__( 'Install Plugin', 'elementor' );
}
$options = [
'title' => esc_html__( 'Speed up your website with Image Optimizer by Elementor', 'elementor' ),
'description' => esc_html__( 'Automatically compress and optimize images, resize larger files, or convert to WebP. Optimize images individually, in bulk, or on upload.', 'elementor' ),
'id' => $notice_id,
'type' => 'cta',
'button_secondary' => [
'text' => $cta_text,
'url' => $url,
'type' => 'cta',
],
];
$this->print_admin_notice( $options );
return true;
}
private function is_plugin_installed( $file_path ): bool {
$installed_plugins = get_plugins();
return isset( $installed_plugins[ $file_path ] );
}
public function print_admin_notice( array $options, $exclude_pages = self::DEFAULT_EXCLUDED_PAGES ) {
global $pagenow;
if ( in_array( $pagenow, $exclude_pages, true ) ) {
return;
}
$default_options = [
'id' => null,
'title' => '',
'description' => '',
'classes' => [ 'notice', 'e-notice' ], // We include WP's default notice class so it will be properly handled by WP's js handler
'type' => '',
'dismissible' => true,
'icon' => 'eicon-elementor',
'button' => [],
'button_secondary' => [],
];
$options = array_replace_recursive( $default_options, $options );
$notice_classes = $options['classes'];
$dismiss_button = '';
$icon = '';
if ( $options['type'] ) {
$notice_classes[] = 'e-notice--' . $options['type'];
}
if ( $options['dismissible'] ) {
$label = esc_html__( 'Dismiss this notice.', 'elementor' );
$notice_classes[] = 'e-notice--dismissible';
$dismiss_button = '';
}
if ( $options['icon'] ) {
$notice_classes[] = 'e-notice--extended';
$icon = '
';
}
$wrapper_attributes = [
'class' => $notice_classes,
];
if ( $options['id'] ) {
$wrapper_attributes['data-notice_id'] = $options['id'];
}
?>
>
$button_settings ) {
if ( empty( $button_settings['variant'] ) && $index ) {
$button_settings['variant'] = 'outline';
}
if ( empty( $button_settings['text'] ) ) {
continue;
}
$button = new Button( $button_settings );
$button->print_button();
} ?>
install_time = Plugin::$instance->get_install_time();
$this->current_screen_id = get_current_screen()->id;
foreach ( $this->plain_notices as $notice ) {
$method_callback = "notice_{$notice}";
if ( $this->$method_callback() ) {
return;
}
}
/** @var Base_Notice $notice_instance */
foreach ( $this->get_notices() as $notice_instance ) {
if ( ! $notice_instance->should_print() ) {
continue;
}
$this->print_admin_notice( $notice_instance->get_config() );
// It exits the method to make sure it prints only one notice.
return;
}
}
/**
* @since 2.9.0
* @access public
*/
public function __construct() {
add_action( 'admin_notices', [ $this, 'admin_notices' ], 20 );
}
/**
* Get module name.
*
* Retrieve the module name.
*
* @since 2.9.0
* @access public
*
* @return string Module name.
*/
public function get_name() {
return 'admin-notices';
}
}
admin/feedback.php 0000644 00000013325 14720700033 0010071 0 ustar 00 is_plugins_screen() ) {
return;
}
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_feedback_dialog_scripts' ] );
} );
// Ajax.
add_action( 'wp_ajax_elementor_deactivate_feedback', [ $this, 'ajax_elementor_deactivate_feedback' ] );
}
/**
* Get module name.
*
* Retrieve the module name.
*
* @since 1.7.0
* @access public
*
* @return string Module name.
*/
public function get_name() {
return 'feedback';
}
/**
* Enqueue feedback dialog scripts.
*
* Registers the feedback dialog scripts and enqueues them.
*
* @since 1.0.0
* @access public
*/
public function enqueue_feedback_dialog_scripts() {
add_action( 'admin_footer', [ $this, 'print_deactivate_feedback_dialog' ] );
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_register_script(
'elementor-admin-feedback',
ELEMENTOR_ASSETS_URL . 'js/admin-feedback' . $suffix . '.js',
[
'elementor-common',
'wp-i18n',
],
ELEMENTOR_VERSION,
true
);
wp_enqueue_script( 'elementor-admin-feedback' );
wp_set_script_translations( 'elementor-admin-feedback', 'elementor' );
}
/**
* Print deactivate feedback dialog.
*
* Display a dialog box to ask the user why he deactivated Elementor.
*
* Fired by `admin_footer` filter.
*
* @since 1.0.0
* @access public
*/
public function print_deactivate_feedback_dialog() {
$deactivate_reasons = [
'no_longer_needed' => [
'title' => esc_html__( 'I no longer need the plugin', 'elementor' ),
'input_placeholder' => '',
],
'found_a_better_plugin' => [
'title' => esc_html__( 'I found a better plugin', 'elementor' ),
'input_placeholder' => esc_html__( 'Please share which plugin', 'elementor' ),
],
'couldnt_get_the_plugin_to_work' => [
'title' => esc_html__( 'I couldn\'t get the plugin to work', 'elementor' ),
'input_placeholder' => '',
],
'temporary_deactivation' => [
'title' => esc_html__( 'It\'s a temporary deactivation', 'elementor' ),
'input_placeholder' => '',
],
'elementor_pro' => [
'title' => esc_html__( 'I have Elementor Pro', 'elementor' ),
'input_placeholder' => '',
'alert' => esc_html__( 'Wait! Don\'t deactivate Elementor. You have to activate both Elementor and Elementor Pro in order for the plugin to work.', 'elementor' ),
],
'other' => [
'title' => esc_html__( 'Other', 'elementor' ),
'input_placeholder' => esc_html__( 'Please share the reason', 'elementor' ),
],
];
?>
id, [ 'plugins', 'plugins-network' ] );
}
}
admin/canary-deployment.php 0000644 00000011756 14720700033 0012006 0 ustar 00 response ) ) {
return $transient;
}
// Placeholder
$stable_version = '0.0.0';
if ( ! empty( $transient->response[ static::PLUGIN_BASE ]->new_version ) ) {
$stable_version = $transient->response[ static::PLUGIN_BASE ]->new_version;
}
if ( null === $this->canary_deployment_info ) {
$this->canary_deployment_info = $this->get_canary_deployment_info();
}
// Can be false - if canary version is not available.
if ( empty( $this->canary_deployment_info ) ) {
return $transient;
}
if ( ! version_compare( $this->canary_deployment_info['new_version'], $stable_version, '>' ) ) {
return $transient;
}
$canary_deployment_info = $this->canary_deployment_info;
// Most of plugin info comes from the $transient but on first check - the response is empty.
if ( ! empty( $transient->response[ static::PLUGIN_BASE ] ) ) {
$canary_deployment_info = array_merge( (array) $transient->response[ static::PLUGIN_BASE ], $canary_deployment_info );
}
$transient->response[ static::PLUGIN_BASE ] = (object) $canary_deployment_info;
return $transient;
}
protected function get_canary_deployment_remote_info( $force ) {
return Api::get_canary_deployment_info( $force );
}
private function get_canary_deployment_info() {
global $pagenow;
$force = 'update-core.php' === $pagenow && isset( $_GET['force-check'] );
$canary_deployment = $this->get_canary_deployment_remote_info( $force );
if ( empty( $canary_deployment['plugin_info']['new_version'] ) ) {
return false;
}
$canary_version = $canary_deployment['plugin_info']['new_version'];
if ( version_compare( $canary_version, static::CURRENT_VERSION, '<=' ) ) {
return false;
}
if ( ! empty( $canary_deployment['conditions'] ) && ! $this->check_conditions( $canary_deployment['conditions'] ) ) {
return false;
}
return $canary_deployment['plugin_info'];
}
private function check_conditions( $groups ) {
foreach ( $groups as $group ) {
if ( $this->check_group( $group ) ) {
return true;
}
}
return false;
}
private function check_group( $group ) {
$is_or_relation = ! empty( $group['relation'] ) && 'OR' === $group['relation'];
unset( $group['relation'] );
$result = false;
foreach ( $group as $condition ) {
// Reset results for each condition.
$result = false;
switch ( $condition['type'] ) {
case 'wordpress': // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled
// include an unmodified $wp_version
include ABSPATH . WPINC . '/version.php';
$result = version_compare( $wp_version, $condition['version'], $condition['operator'] );
break;
case 'multisite':
$result = is_multisite() === $condition['multisite'];
break;
case 'language':
$in_array = in_array( get_locale(), $condition['languages'], true );
$result = 'in' === $condition['operator'] ? $in_array : ! $in_array;
break;
case 'plugin':
if ( ! empty( $condition['plugin_file'] ) ) {
$plugin_file = $condition['plugin_file']; // For PHP Unit tests.
} else {
$plugin_file = WP_PLUGIN_DIR . '/' . $condition['plugin']; // Default.
}
$version = '';
if ( is_plugin_active( $condition['plugin'] ) && file_exists( $plugin_file ) ) {
$plugin_data = get_plugin_data( $plugin_file );
if ( isset( $plugin_data['Version'] ) ) {
$version = $plugin_data['Version'];
}
}
$result = version_compare( $version, $condition['version'], $condition['operator'] );
break;
case 'theme':
$theme = wp_get_theme();
if ( wp_get_theme()->parent() ) {
$theme = wp_get_theme()->parent();
}
if ( $theme->get_template() === $condition['theme'] ) {
$version = $theme->version;
} else {
$version = '';
}
$result = version_compare( $version, $condition['version'], $condition['operator'] );
break;
}
if ( ( $is_or_relation && $result ) || ( ! $is_or_relation && ! $result ) ) {
return $result;
}
}
return $result;
}
/**
* @since 2.6.0
* @access public
*/
public function __construct() {
add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_version' ] );
}
}
admin/menu/interfaces/admin-menu-item.php 0000644 00000000444 14720700033 0014420 0 ustar 00 init_args();
$this->init_options();
add_action( 'admin_menu', function() {
$this->register();
} );
if ( $this->options['separator'] ) {
add_action( 'admin_menu', function() {
$this->add_menu_separator();
} );
add_filter( 'custom_menu_order', '__return_true' );
add_filter( 'menu_order', function( $menu_order ) {
return $this->rearrange_menu_separator( $menu_order );
} );
}
}
public function get_args( $arg = null ) {
return self::get_items( $this->args, $arg );
}
public function add_submenu( $submenu_args ) {
$default_submenu_args = [
'page_title' => '',
'capability' => $this->args['capability'],
'function' => null,
'index' => null,
];
$this->submenus[] = array_merge( $default_submenu_args, $submenu_args );
}
protected function get_init_options() {
return [];
}
protected function register_default_submenus() {}
protected function register() {
$args = $this->args;
add_menu_page( $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'], $args['icon_url'], $args['position'] );
$this->register_default_submenus();
do_action( 'elementor/admin/menu_registered/' . $args['menu_slug'], $this );
usort( $this->submenus, function( $a, $b ) {
return $a['index'] - $b['index'];
} );
foreach ( $this->submenus as $index => $submenu_item ) {
$submenu_args = [
$args['menu_slug'],
$submenu_item['page_title'],
$submenu_item['menu_title'],
$submenu_item['capability'],
$submenu_item['menu_slug'],
$submenu_item['function'],
];
if ( 0 === $submenu_item['index'] ) {
$submenu_args[] = 0;
}
add_submenu_page( ...$submenu_args );
if ( ! empty( $submenu_item['class'] ) ) {
global $submenu;
$submenu[ $args['menu_slug'] ][ $index + 1 ][4] = $submenu_item['class']; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
}
}
private function init_args() {
$default_args = [
'function' => null,
'icon_url' => null,
'position' => null,
];
$this->args = array_merge( $default_args, $this->get_init_args() );
}
private function init_options() {
$default_options = [
'separator' => false,
];
$this->options = array_merge( $default_options, $this->get_init_options() );
}
private function add_menu_separator() {
global $menu;
$slug = $this->args['menu_slug'];
$menu[] = [ '', 'read', 'separator-' . $slug, '', 'wp-menu-separator ' . $slug ]; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
private function rearrange_menu_separator( $menu_order ) {
// Initialize our custom order array.
$custom_menu_order = [];
$slug = $this->args['menu_slug'];
$separator_name = 'separator-' . $slug;
// Get the index of our custom separator.
$custom_separator = array_search( $separator_name, $menu_order, true );
// Loop through menu order and do some rearranging.
foreach ( $menu_order as $item ) {
if ( $slug === $item ) {
$custom_menu_order[] = $separator_name;
$custom_menu_order[] = $item;
unset( $menu_order[ $custom_separator ] );
} elseif ( $separator_name !== $item ) {
$custom_menu_order[] = $item;
}
}
// Return order.
return $custom_menu_order;
}
}
admin/menu/main.php 0000644 00000004011 14720700033 0010225 0 ustar 00 esc_html__( 'Elementor', 'elementor' ),
'menu_title' => esc_html__( 'Elementor', 'elementor' ),
'capability' => 'manage_options',
'menu_slug' => 'elementor',
'function' => [ Plugin::$instance->settings, 'display_settings_page' ],
'position' => 58.5,
];
}
protected function get_init_options() {
return [
'separator' => true,
];
}
protected function register_default_submenus() {
$this->add_submenu( [
'page_title' => esc_html_x( 'Templates', 'Template Library', 'elementor' ),
'menu_title' => esc_html_x( 'Templates', 'Template Library', 'elementor' ),
'menu_slug' => Source_Local::ADMIN_MENU_SLUG,
'index' => 0,
] );
$this->add_submenu( [
'menu_title' => esc_html__( 'Help', 'elementor' ),
'menu_slug' => 'go_knowledge_base_site',
'function' => [ Plugin::$instance->settings, 'handle_external_redirects' ],
'index' => 150,
] );
}
protected function register() {
parent::register();
$this->rearrange_elementor_submenu();
}
private function rearrange_elementor_submenu() {
global $submenu;
$elementor_menu_slug = $this->get_args( 'menu_slug' );
$elementor_submenu_old_index = null;
$tools_submenu_index = null;
foreach ( $submenu[ $elementor_menu_slug ] as $index => $submenu_item ) {
if ( $elementor_menu_slug === $submenu_item[2] ) {
$elementor_submenu_old_index = $index;
} elseif ( Tools::PAGE_ID === $submenu_item[2] ) {
$tools_submenu_index = $index;
break;
}
}
$elementor_submenu = array_splice( $submenu[ $elementor_menu_slug ], $elementor_submenu_old_index, 1 );
$elementor_submenu[0][0] = esc_html__( 'Settings', 'elementor' );
array_splice( $submenu[ $elementor_menu_slug ], $tools_submenu_index, 0, $elementor_submenu );
}
}
admin/menu/admin-menu-manager.php 0000644 00000005367 14720700033 0012762 0 ustar 00 items[ $item_slug ] = $item;
}
public function unregister( $item_slug ) {
unset( $this->items[ $item_slug ] );
}
public function get( $item_slug ) {
if ( empty( $this->items[ $item_slug ] ) ) {
return null;
}
return $this->items[ $item_slug ];
}
public function get_all() {
return $this->items;
}
public function register_actions() {
add_action( 'admin_menu', function () {
$this->register_wp_menus();
}, 20 );
add_action( 'admin_head', function () {
$this->hide_invisible_menus();
} );
}
private function register_wp_menus() {
do_action( 'elementor/admin/menu/register', $this );
$hooks = [];
foreach ( $this->get_all() as $item_slug => $item ) {
$is_top_level = empty( $item->get_parent_slug() );
if ( $is_top_level ) {
$hooks[ $item_slug ] = $this->register_top_level_menu( $item_slug, $item );
} else {
$hooks[ $item_slug ] = $this->register_sub_menu( $item_slug, $item );
}
}
do_action( 'elementor/admin/menu/after_register', $this, $hooks );
}
private function register_top_level_menu( $item_slug, Admin_Menu_Item $item ) {
$has_page = ( $item instanceof Admin_Menu_Item_With_Page );
$has_position = ( $item instanceof Admin_Menu_Item_Has_Position );
$page_title = $has_page ? $item->get_page_title() : '';
$callback = $has_page ? [ $item, 'render' ] : '';
$position = $has_position ? $item->get_position() : null;
return add_menu_page(
$page_title,
$item->get_label(),
$item->get_capability(),
$item_slug,
$callback,
'',
$position
);
}
private function register_sub_menu( $item_slug, Admin_Menu_Item $item ) {
$has_page = ( $item instanceof Admin_Menu_Item_With_Page );
$page_title = $has_page ? $item->get_page_title() : '';
$callback = $has_page ? [ $item, 'render' ] : '';
return add_submenu_page(
$item->get_parent_slug(),
$page_title,
$item->get_label(),
$item->get_capability(),
$item_slug,
$callback
);
}
private function hide_invisible_menus() {
foreach ( $this->get_all() as $item_slug => $item ) {
if ( $item->is_visible() ) {
continue;
}
$is_top_level = empty( $item->get_parent_slug() );
if ( $is_top_level ) {
remove_menu_page( $item_slug );
} else {
remove_submenu_page( $item->get_parent_slug(), $item_slug );
}
}
}
}
admin/admin.php 0000644 00000075552 14720700033 0007447 0 ustar 00 =' );
} else {
// If `$latest_install` is not set, Elementor was never installed on this site.
$is_new_install = true;
}
if ( $already_had_onboarding || ! $is_new_install ) {
return;
}
wp_safe_redirect( admin_url( 'admin.php?page=elementor-app#onboarding' ) );
exit;
}
private function register_packages() {
$assets_config_provider = ( new Assets_Config_Provider() )
->set_path_resolver( function ( $name ) {
return ELEMENTOR_ASSETS_PATH . "js/packages/{$name}/{$name}.asset.php";
} );
Collection::make( [ 'ui', 'icons', 'query' ] )
->each( function( $package ) use ( $assets_config_provider ) {
$suffix = Utils::is_script_debug() ? '' : '.min';
$config = $assets_config_provider->load( $package )->get( $package );
if ( ! $config ) {
return;
}
wp_register_script(
$config['handle'],
ELEMENTOR_ASSETS_URL . "js/packages/{$package}/{$package}{$suffix}.js",
$config['deps'],
ELEMENTOR_VERSION,
true
);
} );
}
/**
* Enqueue admin scripts.
*
* Registers all the admin scripts and enqueues them.
*
* Fired by `admin_enqueue_scripts` action.
*
* @since 1.0.0
* @access public
*/
public function enqueue_scripts() {
wp_register_script(
'elementor-admin-modules',
$this->get_js_assets_url( 'admin-modules' ),
[],
ELEMENTOR_VERSION,
true
);
$this->register_packages();
// Temporary solution for the admin.
wp_register_script(
'elementor-ai-admin',
$this->get_js_assets_url( 'ai-admin' ),
[
'elementor-common',
'elementor-v2-ui',
'elementor-v2-icons',
],
ELEMENTOR_VERSION,
true
);
wp_register_script(
'elementor-admin',
$this->get_js_assets_url( 'admin' ),
[
'elementor-common',
'elementor-admin-modules',
],
ELEMENTOR_VERSION,
true
);
wp_enqueue_script( 'elementor-admin' );
wp_set_script_translations( 'elementor-admin', 'elementor' );
$this->maybe_enqueue_hints();
$this->print_config();
}
/**
* Enqueue admin styles.
*
* Registers all the admin styles and enqueues them.
*
* Fired by `admin_enqueue_scripts` action.
*
* @since 1.0.0
* @access public
*/
public function enqueue_styles() {
$direction_suffix = is_rtl() ? '-rtl' : '';
wp_register_style(
'elementor-admin',
$this->get_css_assets_url( 'admin' . $direction_suffix ),
[
'elementor-common',
],
ELEMENTOR_VERSION
);
wp_enqueue_style( 'elementor-admin' );
// It's for upgrade notice.
// TODO: enqueue this just if needed.
add_thickbox();
}
/**
* Print switch mode button.
*
* Adds a switch button in post edit screen (which has cpt support). To allow
* the user to switch from the native WordPress editor to Elementor builder.
*
* Fired by `edit_form_after_title` action.
*
* @since 1.0.0
* @access public
*
* @param \WP_Post $post The current post object.
*/
public function print_switch_mode_button( $post ) {
// Exit if Gutenberg are active.
if ( did_action( 'enqueue_block_editor_assets' ) ) {
return;
}
$document = Plugin::$instance->documents->get( $post->ID );
if ( ! $document || ! $document->is_editable_by_current_user() ) {
return;
}
wp_nonce_field( basename( __FILE__ ), '_elementor_edit_mode_nonce' );
?>
documents->get( $post_id )->set_is_built_with_elementor( ! empty( $_POST['_elementor_post_mode'] ) );
}
/**
* Add Elementor post state.
*
* Adds a new "Elementor" post state to the post table.
*
* Fired by `display_post_states` filter.
*
* @since 1.8.0
* @access public
*
* @param array $post_states An array of post display states.
* @param \WP_Post $post The current post object.
*
* @return array A filtered array of post display states.
*/
public function add_elementor_post_state( $post_states, $post ) {
$document = Plugin::$instance->documents->get( $post->ID );
if ( $document && $document->is_built_with_elementor() && $document->is_editable_by_current_user() ) {
$post_states['elementor'] = esc_html__( 'Elementor', 'elementor' );
}
return $post_states;
}
/**
* Body status classes.
*
* Adds CSS classes to the admin body tag.
*
* Fired by `admin_body_class` filter.
*
* @since 1.0.0
* @access public
*
* @param string $classes Space-separated list of CSS classes.
*
* @return string Space-separated list of CSS classes.
*/
public function body_status_classes( $classes ) {
global $pagenow;
if ( in_array( $pagenow, [ 'post.php', 'post-new.php' ], true ) && Utils::is_post_support() ) {
$post = get_post();
$document = Plugin::$instance->documents->get( $post->ID );
$mode_class = $document && $document->is_built_with_elementor() ? 'elementor-editor-active' : 'elementor-editor-inactive';
$classes .= ' ' . $mode_class;
}
return $classes;
}
/**
* Plugin action links.
*
* Adds action links to the plugin list table
*
* Fired by `plugin_action_links` filter.
*
* @since 1.0.0
* @access public
*
* @param array $links An array of plugin action links.
*
* @return array An array of plugin action links.
*/
public function plugin_action_links( $links ) {
$settings_link = sprintf( '%2$s', admin_url( 'admin.php?page=' . Settings::PAGE_ID ), esc_html__( 'Settings', 'elementor' ) );
array_unshift( $links, $settings_link );
$go_pro_text = esc_html__( 'Get Elementor Pro', 'elementor' );
if ( Utils::is_sale_time() ) {
$go_pro_text = esc_html__( 'Discounted Upgrades Now!', 'elementor' );
}
$links['go_pro'] = sprintf( '%2$s', 'https://go.elementor.com/go-pro-wp-plugins/', $go_pro_text );
return $links;
}
/**
* Plugin row meta.
*
* Adds row meta links to the plugin list table
*
* Fired by `plugin_row_meta` filter.
*
* @since 1.1.4
* @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 ) {
if ( ELEMENTOR_PLUGIN_BASE === $plugin_file ) {
$row_meta = [
'docs' => '' . esc_html__( 'Docs & FAQs', 'elementor' ) . '',
'ideo' => '' . esc_html__( 'Video Tutorials', 'elementor' ) . '',
];
$plugin_meta = array_merge( $plugin_meta, $row_meta );
}
return $plugin_meta;
}
/**
* Admin footer text.
*
* Modifies the "Thank you" text displayed in the admin footer.
*
* Fired by `admin_footer_text` filter.
*
* @since 1.0.0
* @access public
*
* @param string $footer_text The content that will be printed.
*
* @return string The content that will be printed.
*/
public function admin_footer_text( $footer_text ) {
$current_screen = get_current_screen();
$is_elementor_screen = ( $current_screen && false !== strpos( $current_screen->id, 'elementor' ) );
if ( $is_elementor_screen ) {
$footer_text = sprintf(
/* translators: 1: Elementor, 2: Link to plugin review */
__( 'Enjoyed %1$s? Please leave us a %2$s rating. We really appreciate your support!', 'elementor' ),
'' . esc_html__( 'Elementor', 'elementor' ) . '',
'★★★★★'
);
}
return $footer_text;
}
/**
* Register dashboard widgets.
*
* Adds a new Elementor widgets to WordPress dashboard.
*
* Fired by `wp_dashboard_setup` action.
*
* @since 1.9.0
* @access public
*/
public function register_dashboard_widgets() {
wp_add_dashboard_widget( 'e-dashboard-overview', esc_html__( 'Elementor Overview', 'elementor' ), [ $this, 'elementor_dashboard_overview_widget' ] );
// Move our widget to top.
global $wp_meta_boxes;
$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
$ours = [
'e-dashboard-overview' => $dashboard['e-dashboard-overview'],
];
$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
/**
* Displays the Elementor dashboard widget.
*
* Fired by `wp_add_dashboard_widget` function.
*
* @since 1.9.0
* @access public
*/
public function elementor_dashboard_overview_widget() {
?>
have_posts() ) { ?>
have_posts() ) {
$recently_edited_query->the_post();
$document = Plugin::$instance->documents->get( get_the_ID() );
$date = date_i18n( _x( 'M jS', 'Dashboard Overview Widget Recently Date', 'elementor' ), get_the_modified_time( 'U' ) );
?>
-
,
0 ) {
$elementor_feed = array_slice( $elementor_feed, 0, $limit_feed );
}
if ( ! empty( $elementor_feed ) ) { ?>
[
'title' => esc_html__( 'Blog', 'elementor' ),
'link' => 'https://go.elementor.com/overview-widget-blog/',
],
'help' => [
'title' => esc_html__( 'Help', 'elementor' ),
'link' => 'https://go.elementor.com/overview-widget-docs/',
],
];
$additions_actions = [];
if ( User::get_introduction_meta( 'ai_get_started' ) ) {
$additions_actions['ai-library'] = [
'title' => esc_html__( 'AI Prompts Library', 'elementor' ),
'link' => 'https://go.elementor.com/overview-ai-prompts-library/',
];
} else {
$additions_actions['ai'] = [
'title' => esc_html__( 'Build Smart with AI', 'elementor' ),
'link' => 'https://go.elementor.com/overview-widget-ai/',
];
}
$additions_actions['go-pro'] = [
'title' => esc_html__( 'Upgrade', 'elementor' ),
'link' => 'https://go.elementor.com/go-pro-wp-overview-widget/',
];
/**
* Dashboard widget footer actions.
*
* Filters the additions actions displayed in Elementor dashboard widget.
*
* Developers can add new action links to Elementor dashboard widget
* footer using this filter.
*
* @since 1.9.0
*
* @param array $additions_actions Elementor dashboard widget footer actions.
*/
$additions_actions = apply_filters( 'elementor/admin/dashboard_overview_widget/footer_actions', $additions_actions );
$actions = $base_actions + $additions_actions;
return $actions;
}
/**
* Get elementor dashboard overview widget footer actions.
*
* Retrieves the footer action links displayed in elementor dashboard widget.
*
* @since 1.9.0
* @access private
*/
private function get_dashboard_overview_widget_footer_actions() {
return self::static_get_dashboard_overview_widget_footer_actions();
}
/**
* Admin action new post.
*
* When a new post action is fired the title is set to 'Elementor' and the post ID.
*
* Fired by `admin_action_elementor_new_post` action.
*
* @since 1.9.0
* @access public
*/
public function admin_action_new_post() {
check_admin_referer( 'elementor_action_new_post' );
$post_type = Utils::get_super_global_value( $_GET, 'post_type' ) ?? 'post';
if ( ! User::is_current_user_can_edit_post_type( $post_type ) ) {
return;
}
if ( empty( $_GET['template_type'] ) ) {
$type = 'post';
} else {
$type = sanitize_text_field( wp_unslash( $_GET['template_type'] ) );
}
$post_data = Utils::get_super_global_value( $_GET, 'post_data' ) ?? [];
/**
* Create new post meta data.
*
* Filters the meta data of any new post created.
*
* @since 2.0.0
*
* @param array $meta Post meta data.
*/
$meta = [];
if ( isset( $_GET['meta'] ) && is_array( $_GET['meta'] ) ) {
$meta = array_map( 'sanitize_text_field', wp_unslash( $_GET['meta'] ) );
}
$meta = apply_filters( 'elementor/admin/create_new_post/meta', $meta );
$post_data['post_type'] = $post_type;
$document = Plugin::$instance->documents->create( $type, $post_data, $meta );
if ( is_wp_error( $document ) ) {
wp_die( $document ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
wp_redirect( $document->get_edit_url() );
die;
}
/**
* @since 2.3.0
* @access public
*/
public function add_new_template_template() {
Plugin::$instance->common->add_template( ELEMENTOR_PATH . 'includes/admin-templates/new-template.php' );
}
public function add_new_floating_elements_template() {
Plugin::$instance->common->add_template( ELEMENTOR_PATH . 'includes/admin-templates/new-floating-elements.php' );
}
public function enqueue_new_floating_elements_scripts() {
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_enqueue_script(
'elementor-floating-elements-modal',
ELEMENTOR_ASSETS_URL . 'js/floating-elements-modal' . $suffix . '.js',
[],
ELEMENTOR_VERSION,
true
);
wp_set_script_translations( 'elementor-floating-elements-modal', 'elementor' );
}
/**
* @access public
*/
public function enqueue_new_template_scripts() {
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_enqueue_script(
'elementor-new-template',
ELEMENTOR_ASSETS_URL . 'js/new-template' . $suffix . '.js',
[],
ELEMENTOR_VERSION,
true
);
wp_set_script_translations( 'elementor-new-template', 'elementor' );
}
/**
* @since 2.6.0
* @access public
*/
public function add_beta_tester_template() {
Plugin::$instance->common->add_template( ELEMENTOR_PATH . 'includes/admin-templates/beta-tester.php' );
}
/**
* @access public
*/
public function enqueue_beta_tester_scripts() {
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_enqueue_script(
'elementor-beta-tester',
ELEMENTOR_ASSETS_URL . 'js/beta-tester' . $suffix . '.js',
[],
ELEMENTOR_VERSION,
true
);
wp_set_script_translations( 'elementor-beta-tester', 'elementor' );
}
public function init_floating_elements() {
$screens = [
'elementor_library_page_e-floating-buttons' => true,
'edit-e-floating-buttons' => true,
];
if ( ! isset( $screens[ get_current_screen()->id ] ) ) {
return;
}
add_action( 'admin_head', [ $this, 'add_new_floating_elements_template' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_new_floating_elements_scripts' ] );
}
/**
* @access public
*/
public function init_new_template() {
if ( 'edit-elementor_library' !== get_current_screen()->id ) {
return;
}
// Allow plugins to add their templates on admin_head.
add_action( 'admin_head', [ $this, 'add_new_template_template' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_new_template_scripts' ] );
}
public function version_update_warning( $current_version, $new_version ) {
$current_version_minor_part = explode( '.', $current_version )[1];
$new_version_minor_part = explode( '.', $new_version )[1];
if ( $current_version_minor_part === $new_version_minor_part ) {
return;
}
?>
base ) || 'elementor_page_elementor-tools' === $current_screen->id ) {
add_action( 'admin_head', [ $this, 'add_beta_tester_template' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_beta_tester_scripts' ] );
}
}
/**
* Admin constructor.
*
* Initializing Elementor in WordPress admin.
*
* @since 1.0.0
* @access public
*/
public function __construct() {
Plugin::$instance->init_common();
$this->add_component( 'feedback', new Feedback() );
$this->add_component( 'admin-notices', new Admin_Notices() );
add_action( 'admin_init', [ $this, 'maybe_redirect_to_getting_started' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] );
add_action( 'edit_form_after_title', [ $this, 'print_switch_mode_button' ] );
add_action( 'save_post', [ $this, 'save_post' ] );
add_filter( 'display_post_states', [ $this, 'add_elementor_post_state' ], 10, 2 );
add_filter( 'plugin_action_links_' . ELEMENTOR_PLUGIN_BASE, [ $this, 'plugin_action_links' ] );
add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 2 );
add_filter( 'admin_body_class', [ $this, 'body_status_classes' ] );
add_filter( 'admin_footer_text', [ $this, 'admin_footer_text' ] );
// Register Dashboard Widgets.
add_action( 'wp_dashboard_setup', [ $this, 'register_dashboard_widgets' ] );
// Admin Actions
add_action( 'admin_action_elementor_new_post', [ $this, 'admin_action_new_post' ] );
add_action( 'current_screen', [ $this, 'init_new_template' ] );
add_action( 'current_screen', [ $this, 'init_floating_elements' ] );
add_action( 'current_screen', [ $this, 'init_beta_tester' ] );
add_action( 'in_plugin_update_message-' . ELEMENTOR_PLUGIN_BASE, function( $plugin_data ) {
$this->version_update_warning( ELEMENTOR_VERSION, $plugin_data['new_version'] );
} );
add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_hints' ] );
}
/**
* @since 2.3.0
* @access protected
*/
protected function get_init_settings() {
$beta_tester_email = get_user_meta( get_current_user_id(), User::BETA_TESTER_META_KEY, true );
$elementor_beta = get_option( 'elementor_beta', 'no' );
$all_introductions = User::get_introduction_meta();
$beta_tester_signup_dismissed = array_key_exists( Beta_Testers::BETA_TESTER_SIGNUP, $all_introductions );
$settings = [
'home_url' => home_url(),
'settings_url' => Settings::get_url(),
'user' => [
'introduction' => User::get_introduction_meta(),
'restrictions' => Plugin::$instance->role_manager->get_user_restrictions_array(),
'is_administrator' => current_user_can( 'manage_options' ),
],
'beta_tester' => [
'beta_tester_signup' => Beta_Testers::BETA_TESTER_SIGNUP,
'has_email' => $beta_tester_email,
'option_enabled' => 'no' !== $elementor_beta,
'signup_dismissed' => $beta_tester_signup_dismissed,
],
'experiments' => $this->get_experiments(),
];
/**
* Localize settings.
*
* Filters the initial localize settings in the admin.
*
* WordPress has it's own way to pass localized data from PHP (backend) to
* JS (frontend). Elementor uses this method to pass localize data in the
* admin. This hook can be used to add more localized settings in addition
* to the initial Elementor settings.
*
* @since 2.3.0
*
* @param array $settings Initial localize settings.
*/
$settings = apply_filters( 'elementor/admin/localize_settings', $settings );
return $settings;
}
private function get_experiments() {
return ( new Collection( Plugin::$instance->experiments->get_features() ) )
->map( function ( $experiment_data ) {
$dependencies = $experiment_data['dependencies'] ?? [];
$dependencies = ( new Collection( $dependencies ) )
->map( function ( $dependency ) {
return $dependency->get_name();
} )->all();
return [
'name' => $experiment_data['name'],
'title' => $experiment_data['title'] ?? $experiment_data['name'],
'state' => $experiment_data['state'],
'default' => $experiment_data['default'],
'dependencies' => $dependencies,
'messages' => $experiment_data['messages'] ?? [],
];
} )->all();
}
private function maybe_enqueue_hints() {
if ( ! Hints::should_display_hint( 'image-optimization' ) ) {
return;
}
wp_register_script(
'media-hints',
$this->get_js_assets_url( 'media-hints' ),
[],
ELEMENTOR_VERSION,
true
);
$content = sprintf("%1\$s %3\$s!",
__( 'Optimize your images to enhance site performance by using Image Optimizer.', 'elementor' ),
Hints::get_plugin_action_url( 'image-optimization' ),
( Hints::is_plugin_installed( 'image-optimization' ) ? __( 'Activate', 'elementor' ) : __( 'Install', 'elementor' ) ) . ' ' . __( 'Image Optimizer', 'elementor' )
);
$dismissible = 'image_optimizer_hint';
wp_localize_script( 'media-hints', 'elementorAdminHints', [
'mediaHint' => [
'display' => true,
'type' => 'info',
'content' => $content,
'icon' => true,
'dismissible' => $dismissible,
'dismiss' => __( 'Dismiss this notice.', 'elementor' ),
'button_event' => $dismissible,
'button_data' => base64_encode(
json_encode( [
'action_url' => Hints::get_plugin_action_url( 'image-optimization' ),
] ),
),
],
] );
wp_enqueue_script( 'media-hints' );
}
public function register_ajax_hints( $ajax_manager ) {
$ajax_manager->register_ajax_action( 'elementor_image_optimization_campaign', [ $this, 'ajax_set_image_optimization_campaign' ] );
}
public function ajax_set_image_optimization_campaign( $request ) {
if ( empty( $request['source'] ) ) {
return;
}
$campaign_data = [
'source' => sanitize_key( $request['source'] ),
'campaign' => 'io-plg',
'medium' => 'wp-dash',
];
set_transient( 'elementor_image_optimization_campaign', $campaign_data, 30 * DAY_IN_SECONDS );
}
}
admin/notices/base-notice.php 0000644 00000001003 14720700033 0012170 0 ustar 00 is_elementor_dev_installed() &&
! $this->is_install_screen() &&
(
$this->is_promotion_plugins_installed() ||
$this->is_promotion_options_enabled()
);
}
/**
* @inheritDoc
*/
public function get_config() {
return [
'id' => static::ID,
'title' => esc_html__( 'Elementor Developer Edition', 'elementor' ),
'description' => __(
'Get a sneak peek at our in progress development versions, and help us improve Elementor to perfection. Developer Edition releases contain experimental functionality for testing purposes.',
'elementor'
),
'button' => [
'text' => esc_html__( 'Install & Activate', 'elementor' ),
'url' => wp_nonce_url(
self_admin_url( 'update.php?action=install-plugin&plugin=' . static::PLUGIN_SLUG ),
'install-plugin_' . static::PLUGIN_SLUG
),
'type' => 'cta',
],
];
}
/**
* Return all the plugins names.
*
* This method is protected so it can be mocked in tests.
*
* @return array
*/
protected function get_plugins() {
if ( ! $this->plugins ) {
$this->plugins = array_keys( get_plugins() );
}
return $this->plugins;
}
/**
* Checks if elementor dev is installed
*
* @return bool
*/
private function is_elementor_dev_installed() {
return in_array( static::PLUGIN_NAME, $this->get_plugins(), true );
}
/**
* Checks if the admin screen is install screen.
*
* @return bool
*/
private function is_install_screen() {
$screen = get_current_screen();
if ( ! $screen ) {
return false;
}
return 'update' === $screen->id;
}
/**
* Checks if is one of the promotion plugins is installed
*
* @return bool
*/
private function is_promotion_plugins_installed() {
return array_reduce( $this->promotion_plugins, function ( $should_show_notice, $plugin_name ) {
if ( $should_show_notice ) {
return true;
}
return in_array( $plugin_name, $this->get_plugins(), true );
}, false );
}
/**
* Checks if is one of the promotion options is enabled.
*
* @return bool
*/
private function is_promotion_options_enabled() {
return array_reduce( $this->promotion_options, function ( $should_show_notice, $option ) {
if ( $should_show_notice ) {
return true;
}
return 'yes' === get_option( $option, 'no' );
}, false );
}
/**
* Checks if the current page is elementor settings page
*
* @return bool
*/
private function is_elementor_setting_page() {
$current_screen = get_current_screen();
return $current_screen && 'toplevel_page_' . Settings::PAGE_ID === $current_screen->id;
}
}
admin/ui/components/button.php 0000644 00000004565 14720700033 0012470 0 ustar 00 get_options();
if ( empty( $options['text'] ) ) {
return;
}
$html_tag = ! empty( $options['url'] ) ? 'a' : 'button';
$before = '';
$icon = '';
$attributes = [];
if ( ! empty( $options['icon'] ) ) {
$icon = '';
}
$classes = $options['classes'];
$default_classes = $this->get_default_options( 'classes' );
$classes = array_merge( $classes, $default_classes );
if ( ! empty( $options['type'] ) ) {
$classes[] = 'e-button--' . $options['type'];
}
if ( ! empty( $options['variant'] ) ) {
$classes[] = 'e-button--' . $options['variant'];
}
if ( ! empty( $options['before'] ) ) {
$before = '' . wp_kses_post( $options['before'] ) . '';
}
if ( ! empty( $options['url'] ) ) {
$attributes['href'] = $options['url'];
if ( $options['new_tab'] ) {
$attributes['target'] = '_blank';
}
}
$attributes['class'] = $classes;
$html = $before . '<' . $html_tag . ' ' . Utils::render_html_attributes( $attributes ) . '>';
$html .= $icon;
$html .= '' . sanitize_text_field( $options['text'] ) . '';
$html .= '' . $html_tag . '>';
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @param string $option Optional default is null
* @return array|mixed
*/
private function get_options( $option = null ) {
return $this->get_items( $this->options, $option );
}
/**
* @param null $option
* @return array
*/
private function get_default_options( $option = null ) {
$default_options = [
'classes' => [ 'e-button' ],
'icon' => '',
'new_tab' => false,
'text' => '',
'type' => '',
'url' => '',
'variant' => '',
'before' => '',
];
if ( null !== $option && -1 !== in_array( $option, $default_options ) ) {
return $default_options[ $option ];
}
return $default_options;
}
public function __construct( array $options ) {
$this->options = $this->merge_properties( $this->get_default_options(), $options );
}
}
documents-manager.php 0000644 00000045757 14720700033 0010704 0 ustar 00 register_ajax_action( 'save_builder', [ $this, 'ajax_save' ] );
$ajax_manager->register_ajax_action( 'discard_changes', [ $this, 'ajax_discard_changes' ] );
$ajax_manager->register_ajax_action( 'get_document_config', [ $this, 'ajax_get_document_config' ] );
}
/**
* Register default types.
*
* Registers the default document types.
*
* @since 2.0.0
* @access public
*/
public function register_default_types() {
$default_types = [
'post' => Post::get_class_full_name(), // BC.
'wp-post' => Post::get_class_full_name(),
'wp-page' => Page::get_class_full_name(),
];
foreach ( $default_types as $type => $class ) {
$this->register_document_type( $type, $class );
}
}
/**
* Register document type.
*
* Registers a single document.
*
* @since 2.0.0
* @access public
*
* @param string $type Document type name.
* @param string $class The name of the class that registers the document type.
* Full name with the namespace.
*
* @return Documents_Manager The updated document manager instance.
*/
public function register_document_type( $type, $class ) {
$this->types[ $type ] = $class;
$cpt = $class::get_property( 'cpt' );
if ( $cpt ) {
foreach ( $cpt as $post_type ) {
$this->cpt[ $post_type ] = $type;
}
}
if ( $class::get_property( 'register_type' ) ) {
Source_Local::add_template_type( $type );
}
return $this;
}
/**
* Get document.
*
* Retrieve the document data based on a post ID.
*
* @since 2.0.0
* @access public
*
* @param int $post_id Post ID.
* @param bool $from_cache Optional. Whether to retrieve cached data. Default is true.
*
* @return false|Document Document data or false if post ID was not entered.
*/
public function get( $post_id, $from_cache = true ) {
$this->register_types();
$post_id = absint( $post_id );
if ( ! $post_id || ! get_post( $post_id ) ) {
return false;
}
/**
* Retrieve document post ID.
*
* Filters the document post ID.
*
* @since 2.0.7
*
* @param int $post_id The post ID of the document.
*/
$post_id = apply_filters( 'elementor/documents/get/post_id', $post_id );
if ( ! $from_cache || ! isset( $this->documents[ $post_id ] ) ) {
$doc_type = $this->get_doc_type_by_id( $post_id );
$doc_type_class = $this->get_document_type( $doc_type );
$this->documents[ $post_id ] = new $doc_type_class( [
'post_id' => $post_id,
] );
}
return $this->documents[ $post_id ];
}
/**
* Retrieve a document after checking it exist and allowed to edit.
*
* @since 3.13.0
*
* @param int $post_id The post ID of the document.
*
* @return Document
* @throws \Exception
*/
public function get_with_permissions( $id ): Document {
$document = $this->get( $id );
if ( ! $document ) {
throw new \Exception( 'Not found.' );
}
if ( ! $document->is_editable_by_current_user() ) {
throw new \Exception( 'Access denied.' );
}
return $document;
}
/**
* A `void` version for `get_with_permissions`.
*
* @param $id
* @return void
* @throws \Exception
*/
public function check_permissions( $id ) {
$this->get_with_permissions( $id );
}
/**
* Get document or autosave.
*
* Retrieve either the document or the autosave.
*
* @since 2.0.0
* @access public
*
* @param int $id Optional. Post ID. Default is `0`.
* @param int $user_id Optional. User ID. Default is `0`.
*
* @return false|Document The document if it exist, False otherwise.
*/
public function get_doc_or_auto_save( $id, $user_id = 0 ) {
$document = $this->get( $id );
if ( $document && $document->get_autosave_id( $user_id ) ) {
$document = $document->get_autosave( $user_id );
}
return $document;
}
/**
* Get document for frontend.
*
* Retrieve the document for frontend use.
*
* @since 2.0.0
* @access public
*
* @param int $post_id Optional. Post ID. Default is `0`.
*
* @return false|Document The document if it exist, False otherwise.
*/
public function get_doc_for_frontend( $post_id ) {
$preview_id = (int) Utils::get_super_global_value( $_GET, 'preview_id' );
$is_preview = is_preview() && $post_id === $preview_id;
$is_nonce_verify = wp_verify_nonce( Utils::get_super_global_value( $_GET, 'preview_nonce' ), 'post_preview_' . $preview_id );
if ( ( $is_preview && $is_nonce_verify ) || Plugin::$instance->preview->is_preview_mode() ) {
$document = $this->get_doc_or_auto_save( $post_id, get_current_user_id() );
} else {
$document = $this->get( $post_id );
}
return $document;
}
/**
* Get document type.
*
* Retrieve the type of any given document.
*
* @since 2.0.0
* @access public
*
* @param string $type
*
* @param string $fallback
*
* @return Document|bool The type of the document.
*/
public function get_document_type( $type, $fallback = 'post' ) {
$types = $this->get_document_types();
if ( isset( $types[ $type ] ) ) {
return $types[ $type ];
}
if ( isset( $types[ $fallback ] ) ) {
return $types[ $fallback ];
}
return false;
}
/**
* Get document types.
*
* Retrieve the all the registered document types.
*
* @since 2.0.0
* @access public
*
* @param array $args Optional. An array of key => value arguments to match against
* the properties. Default is empty array.
* @param string $operator Optional. The logical operation to perform. 'or' means only one
* element from the array needs to match; 'and' means all elements
* must match; 'not' means no elements may match. Default 'and'.
*
* @return Document[] All the registered document types.
*/
public function get_document_types( $args = [], $operator = 'and' ) {
$this->register_types();
if ( ! empty( $args ) ) {
$types_properties = $this->get_types_properties();
$filtered = wp_filter_object_list( $types_properties, $args, $operator );
return array_intersect_key( $this->types, $filtered );
}
return $this->types;
}
/**
* Get document types with their properties.
*
* @return array A list of properties arrays indexed by the type.
*/
public function get_types_properties() {
$types_properties = [];
foreach ( $this->get_document_types() as $type => $class ) {
$types_properties[ $type ] = $class::get_properties();
}
return $types_properties;
}
/**
* Create a document.
*
* Create a new document using any given parameters.
*
* @since 2.0.0
* @access public
*
* @param string $type Document type.
* @param array $post_data An array containing the post data.
* @param array $meta_data An array containing the post meta data.
*
* @return Document The type of the document.
*/
public function create( $type, $post_data = [], $meta_data = [] ) {
$class = $this->get_document_type( $type, false );
if ( ! $class ) {
return new \WP_Error( 500, sprintf( 'Type %s does not exist.', $type ) );
}
if ( empty( $post_data['post_title'] ) ) {
$post_data['post_title'] = esc_html__( 'Elementor', 'elementor' );
if ( 'post' !== $type ) {
$post_data['post_title'] = sprintf(
/* translators: %s: Document title. */
__( 'Elementor %s', 'elementor' ),
call_user_func( [ $class, 'get_title' ] )
);
}
$update_title = true;
}
$meta_data['_elementor_edit_mode'] = 'builder';
// Save the type as-is for plugins that hooked at `wp_insert_post`.
$meta_data[ Document::TYPE_META_KEY ] = $type;
$post_data['meta_input'] = $meta_data;
$post_types = $class::get_property( 'cpt' );
if ( ! empty( $post_types[0] ) && empty( $post_data['post_type'] ) ) {
$post_data['post_type'] = $post_types[0];
}
$post_id = wp_insert_post( $post_data );
if ( ! empty( $update_title ) ) {
$post_data['ID'] = $post_id;
$post_data['post_title'] .= ' #' . $post_id;
// The meta doesn't need update.
unset( $post_data['meta_input'] );
wp_update_post( $post_data );
}
/** @var Document $document */
$document = new $class( [
'post_id' => $post_id,
] );
// Let the $document to re-save the template type by his way + version.
$document->save( [] );
return $document;
}
/**
* Remove user edit capabilities if document is not editable.
*
* Filters the user capabilities to disable editing in admin.
*
* @param array $allcaps An array of all the user's capabilities.
* @param array $caps Actual capabilities for meta capability.
* @param array $args Optional parameters passed to has_cap(), typically object ID.
*
* @return array
*/
public function remove_user_edit_cap( $allcaps, $caps, $args ) {
global $pagenow;
if ( ! in_array( $pagenow, [ 'post.php', 'edit.php' ], true ) ) {
return $allcaps;
}
// Don't touch not existing or not allowed caps.
if ( empty( $caps[0] ) || empty( $allcaps[ $caps[0] ] ) ) {
return $allcaps;
}
$capability = $args[0];
if ( 'edit_post' !== $capability ) {
return $allcaps;
}
if ( empty( $args[2] ) ) {
return $allcaps;
}
$post_id = $args[2];
$document = Plugin::$instance->documents->get( $post_id );
if ( ! $document ) {
return $allcaps;
}
$allcaps[ $caps[0] ] = $document::get_property( 'is_editable' );
return $allcaps;
}
/**
* Filter Post Row Actions.
*
* Let the Document to filter the array of row action links on the Posts list table.
*
* @param array $actions
* @param \WP_Post $post
*
* @return array
*/
public function filter_post_row_actions( $actions, $post ) {
$document = $this->get( $post->ID );
if ( $document ) {
$actions = $document->filter_admin_row_actions( $actions );
}
return $actions;
}
/**
* Save document data using ajax.
*
* Save the document on the builder using ajax, when saving the changes, and refresh the editor.
*
* @since 2.0.0
* @access public
*
* @param $request Post ID.
*
* @throws \Exception If current user don't have permissions to edit the post or the post is not using Elementor.
*
* @return array The document data after saving.
*/
public function ajax_save( $request ) {
$document = $this->get( $request['editor_post_id'] );
if ( ! $document->is_built_with_elementor() || ! $document->is_editable_by_current_user() ) {
throw new \Exception( 'Access denied.' );
}
$this->switch_to_document( $document );
// Set the post as global post.
Plugin::$instance->db->switch_to_post( $document->get_post()->ID );
$status = Document::STATUS_DRAFT;
if ( isset( $request['status'] ) && in_array( $request['status'], [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE, Document::STATUS_PENDING, Document::STATUS_AUTOSAVE ], true ) ) {
$status = $request['status'];
}
if ( Document::STATUS_AUTOSAVE === $status ) {
// If the post is a draft - save the `autosave` to the original draft.
// Allow a revision only if the original post is already published.
if ( in_array( $document->get_post()->post_status, [ Document::STATUS_PUBLISH, Document::STATUS_PRIVATE ], true ) ) {
$document = $document->get_autosave( 0, true );
}
}
// Set default page template because the footer-saver doesn't send default values,
// But if the template was changed from canvas to default - it needed to save.
if ( Utils::is_cpt_custom_templates_supported() && ! isset( $request['settings']['template'] ) ) {
$request['settings']['template'] = 'default';
}
$data = [
'elements' => $request['elements'],
'settings' => $request['settings'],
];
$document->save( $data );
$post = $document->get_post();
$main_post = $document->get_main_post();
// Refresh after save.
$document = $this->get( $post->ID, false );
$return_data = [
'status' => $post->post_status,
'config' => [
'document' => [
'last_edited' => $document->get_last_edited(),
'urls' => [
'wp_preview' => $document->get_wp_preview_url(),
],
],
],
];
$post_status_object = get_post_status_object( $main_post->post_status );
if ( $post_status_object ) {
$return_data['config']['document']['status'] = [
'value' => $post_status_object->name,
'label' => $post_status_object->label,
];
}
/**
* Returned documents ajax saved data.
*
* Filters the ajax data returned when saving the post on the builder.
*
* @since 2.0.0
*
* @param array $return_data The returned data.
* @param Document $document The document instance.
*/
$return_data = apply_filters( 'elementor/documents/ajax_save/return_data', $return_data, $document );
return $return_data;
}
/**
* Ajax discard changes.
*
* Load the document data from an autosave, deleting unsaved changes.
*
* @param $request
*
* @return bool True if changes discarded, False otherwise.
* @throws \Exception
*
* @since 2.0.0
* @access public
*
*/
public function ajax_discard_changes( $request ) {
$document = $this->get_with_permissions( $request['editor_post_id'] );
$autosave = $document->get_autosave();
if ( $autosave ) {
$success = $autosave->delete();
} else {
$success = true;
}
return $success;
}
public function ajax_get_document_config( $request ) {
$post_id = absint( $request['id'] );
Plugin::$instance->editor->set_post_id( $post_id );
$document = $this->get_doc_or_auto_save( $post_id );
if ( ! $document ) {
throw new \Exception( 'Not found.' );
}
if ( ! $document->is_editable_by_current_user() ) {
throw new \Exception( 'Access denied.' );
}
// Set the global data like $post, $authordata and etc
Plugin::$instance->db->switch_to_post( $post_id );
$this->switch_to_document( $document );
// Change mode to Builder
$document->set_is_built_with_elementor( true );
$doc_config = $document->get_config();
return $doc_config;
}
/**
* Switch to document.
*
* Change the document to any new given document type.
*
* @since 2.0.0
* @access public
*
* @param Document $document The document to switch to.
*/
public function switch_to_document( $document ) {
// If is already switched, or is the same post, return.
if ( $this->current_doc === $document ) {
$this->switched_data[] = false;
return;
}
$this->switched_data[] = [
'switched_doc' => $document,
'original_doc' => $this->current_doc, // Note, it can be null if the global isn't set
];
$this->current_doc = $document;
}
/**
* Restore document.
*
* Rollback to the original document.
*
* @since 2.0.0
* @access public
*/
public function restore_document() {
$data = array_pop( $this->switched_data );
// If not switched, return.
if ( ! $data ) {
return;
}
$this->current_doc = $data['original_doc'];
}
/**
* Get current document.
*
* Retrieve the current document.
*
* @since 2.0.0
* @access public
*
* @return Document The current document.
*/
public function get_current() {
return $this->current_doc;
}
public function localize_settings( $settings ) {
$translations = [];
foreach ( $this->get_document_types() as $type => $class ) {
$translations[ $type ] = $class::get_title();
}
return array_replace_recursive( $settings, [
'i18n' => $translations,
] );
}
private function register_types() {
if ( ! did_action( 'elementor/documents/register' ) ) {
/**
* Register Elementor documents.
*
* @since 2.0.0
*
* @param Documents_Manager $this The document manager instance.
*/
do_action( 'elementor/documents/register', $this );
}
}
/**
* Get create new post URL.
*
* Retrieve a custom URL for creating a new post/page using Elementor.
*
* @param string $post_type Optional. Post type slug. Default is 'page'.
* @param string|null $template_type Optional. Query arg 'template_type'. Default is null.
*
* @return string A URL for creating new post using Elementor.
*/
public static function get_create_new_post_url( $post_type = 'page', $template_type = null ) {
$query_args = [
'action' => 'elementor_new_post',
'post_type' => $post_type,
];
if ( $template_type ) {
$query_args['template_type'] = $template_type;
}
$new_post_url = add_query_arg( $query_args, admin_url( 'edit.php' ) );
$new_post_url = add_query_arg( '_wpnonce', wp_create_nonce( 'elementor_action_new_post' ), $new_post_url );
return $new_post_url;
}
private function get_doc_type_by_id( $post_id ) {
// Auto-save inherits from the original post.
if ( wp_is_post_autosave( $post_id ) ) {
$post_id = wp_get_post_parent_id( $post_id );
}
// Content built with Elementor.
$template_type = get_post_meta( $post_id, Document::TYPE_META_KEY, true );
if ( $template_type && isset( $this->types[ $template_type ] ) ) {
return $template_type;
}
// Elementor installation on a site with existing content (which doesn't contain Elementor's meta).
$post_type = get_post_type( $post_id );
return $this->cpt[ $post_type ] ?? 'post';
}
}
utils/svg/svg-sanitizer.php 0000644 00000034403 14720700033 0012021 0 ustar 00 is_encoded( $original_content );
if ( $is_encoded ) {
$decoded = $this->decode_svg( $original_content );
if ( false === $decoded ) {
return false;
}
$original_content = $decoded;
}
$valid_svg = $this->sanitize( $original_content );
if ( false === $valid_svg ) {
return false;
}
// If we were gzipped, we need to re-zip
if ( $is_encoded ) {
$valid_svg = $this->encode_svg( $valid_svg );
}
file_put_contents( $filename, $valid_svg );
return true;
}
/**
* Sanitize
*
* @since 3.16.0
* @access public
*
* @param $content
* @return bool|string
*/
public function sanitize( $content ) {
// Strip php tags
$content = $this->strip_comments( $content );
$content = $this->strip_php_tags( $content );
$content = $this->strip_line_breaks( $content );
// Find the start and end tags so we can cut out miscellaneous garbage.
$start = strpos( $content, '