<?php
namespace ElementorPro\Modules\Posts\Skins;
use Elementor\Controls_Manager;
use Elementor\Core\Kits\Documents\Tabs\Global_Colors;
use Elementor\Core\Kits\Documents\Tabs\Global_Typography;
use Elementor\Group_Control_Css_Filter;
use Elementor\Group_Control_Image_Size;
use Elementor\Group_Control_Typography;
use Elementor\Group_Control_Text_Stroke;
use Elementor\Icons_Manager;
use Elementor\Skin_Base as Elementor_Skin_Base;
use Elementor\Utils;
use Elementor\Widget_Base;
use ElementorPro\Modules\Posts\Traits\Button_Widget_Trait;
use ElementorPro\Plugin;
use ElementorPro\Modules\Posts\Widgets\Posts_Base;
use ElementorPro\Core\Utils as ProUtils;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
abstract class Skin_Base extends Elementor_Skin_Base {
use Button_Widget_Trait;
/**
* @var string Save current permalink to avoid conflict with plugins the filters the permalink during the post render.
*/
protected $current_permalink;
protected function _register_controls_actions() {
add_action( 'elementor/element/posts/section_layout/before_section_end', [ $this, 'register_controls' ] );
add_action( 'elementor/element/posts/section_query/after_section_end', [ $this, 'register_style_sections' ] );
}
public function register_style_sections( Widget_Base $widget ) {
$this->parent = $widget;
$this->register_design_controls();
}
public function register_controls( Widget_Base $widget ) {
$this->parent = $widget;
$this->register_columns_controls();
$this->register_post_count_control();
$this->register_thumbnail_controls();
$this->register_title_controls();
$this->register_excerpt_controls();
$this->register_meta_data_controls();
$this->register_read_more_controls();
$this->register_link_controls();
}
public function register_design_controls() {
$this->register_design_layout_controls();
$this->register_design_image_controls();
$this->register_design_content_controls();
}
protected function register_thumbnail_controls() {
$this->add_control(
'thumbnail',
[
'label' => esc_html__( 'Image Position', 'elementor-pro' ),
'type' => Controls_Manager::SELECT,
'default' => 'top',
'options' => [
'top' => esc_html__( 'Top', 'elementor-pro' ),
'left' => esc_html__( 'Left', 'elementor-pro' ),
'right' => esc_html__( 'Right', 'elementor-pro' ),
'none' => esc_html__( 'None', 'elementor-pro' ),
],
'prefix_class' => 'elementor-posts--thumbnail-',
]
);
$this->add_control(
'masonry',
[
'label' => esc_html__( 'Masonry', 'elementor-pro' ),
'type' => Controls_Manager::SWITCHER,
'label_off' => esc_html__( 'Off', 'elementor-pro' ),
'label_on' => esc_html__( 'On', 'elementor-pro' ),
'condition' => [
$this->get_control_id( 'columns!' ) => '1',
$this->get_control_id( 'thumbnail' ) => 'top',
],
'render_type' => 'ui',
'frontend_available' => true,
]
);
$this->add_group_control(
Group_Control_Image_Size::get_type(),
[
'name' => 'thumbnail_size',
'default' => 'medium',
'exclude' => [ 'custom' ],
'condition' => [
$this->get_control_id( 'thumbnail!' ) => 'none',
],
'prefix_class' => 'elementor-posts--thumbnail-size-',
]
);
$this->add_responsive_control(
'item_ratio',
[
'label' => esc_html__( 'Image Ratio', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'default' => [
'size' => 0.66,
],
'tablet_default' => [
'size' => '',
],
'mobile_default' => [
'size' => 0.5,
],
'range' => [
'px' => [
'min' => 0.1,
'max' => 2,
'step' => 0.01,
],
],
'selectors' => [
'{{WRAPPER}} .elementor-posts-container .elementor-post__thumbnail' => 'padding-bottom: calc( {{SIZE}} * 100% );',
'{{WRAPPER}}:after' => 'content: "{{SIZE}}";',
],
'condition' => [
$this->get_control_id( 'thumbnail!' ) => 'none',
$this->get_control_id( 'masonry' ) => '',
],
]
);
$this->add_responsive_control(
'image_width',
[
'label' => esc_html__( 'Image Width', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ],
'range' => [
'%' => [
'min' => 10,
'max' => 100,
],
'px' => [
'min' => 10,
'max' => 600,
],
],
'default' => [
'size' => 100,
'unit' => '%',
],
'tablet_default' => [
'size' => '',
'unit' => '%',
],
'mobile_default' => [
'size' => 100,
'unit' => '%',
],
'selectors' => [
'{{WRAPPER}} .elementor-post__thumbnail__link' => 'width: {{SIZE}}{{UNIT}};',
],
'condition' => [
$this->get_control_id( 'thumbnail!' ) => 'none',
],
]
);
}
protected function register_columns_controls() {
$this->add_responsive_control(
'columns',
[
'label' => esc_html__( 'Columns', 'elementor-pro' ),
'type' => Controls_Manager::SELECT,
'default' => '3',
'tablet_default' => '2',
'mobile_default' => '1',
'options' => [
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
],
'prefix_class' => 'elementor-grid%s-',
'frontend_available' => true,
]
);
}
protected function register_post_count_control() {
$this->add_control(
'posts_per_page',
[
'label' => esc_html__( 'Posts Per Page', 'elementor-pro' ),
'type' => Controls_Manager::NUMBER,
'default' => 6,
]
);
}
protected function register_title_controls() {
$this->add_control(
'show_title',
[
'label' => esc_html__( 'Title', 'elementor-pro' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Show', 'elementor-pro' ),
'label_off' => esc_html__( 'Hide', 'elementor-pro' ),
'default' => 'yes',
'separator' => 'before',
]
);
$this->add_control(
'title_tag',
[
'label' => esc_html__( 'Title HTML Tag', 'elementor-pro' ),
'type' => Controls_Manager::SELECT,
'options' => [
'h1' => 'H1',
'h2' => 'H2',
'h3' => 'H3',
'h4' => 'H4',
'h5' => 'H5',
'h6' => 'H6',
'div' => 'div',
'span' => 'span',
'p' => 'p',
],
'default' => 'h3',
'condition' => [
$this->get_control_id( 'show_title' ) => 'yes',
],
]
);
}
protected function register_excerpt_controls() {
$this->add_control(
'show_excerpt',
[
'label' => esc_html__( 'Excerpt', 'elementor-pro' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Show', 'elementor-pro' ),
'label_off' => esc_html__( 'Hide', 'elementor-pro' ),
'default' => 'yes',
]
);
$this->add_control(
'excerpt_length',
[
'label' => esc_html__( 'Excerpt Length', 'elementor-pro' ),
'type' => Controls_Manager::NUMBER,
/** This filter is documented in wp-includes/formatting.php */
'default' => apply_filters( 'excerpt_length', 25 ),
'condition' => [
$this->get_control_id( 'show_excerpt' ) => 'yes',
],
]
);
$this->add_control(
'apply_to_custom_excerpt',
[
'label' => esc_html__( 'Apply to custom Excerpt', 'elementor-pro' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'elementor-pro' ),
'label_off' => esc_html__( 'No', 'elementor-pro' ),
'default' => 'no',
'condition' => [
$this->get_control_id( 'show_excerpt' ) => 'yes',
],
]
);
}
protected function register_read_more_controls() {
$this->add_control(
'show_read_more',
[
'label' => esc_html__( 'Read More', 'elementor-pro' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Show', 'elementor-pro' ),
'label_off' => esc_html__( 'Hide', 'elementor-pro' ),
'default' => 'yes',
'separator' => 'before',
]
);
$this->add_control(
'read_more_text',
[
'label' => esc_html__( 'Read More Text', 'elementor-pro' ),
'type' => Controls_Manager::TEXT,
'dynamic' => [
'active' => true,
],
'default' => esc_html__( 'Read More ยป', 'elementor-pro' ),
'condition' => [
$this->get_control_id( 'show_read_more' ) => 'yes',
],
]
);
$this->add_control(
'read_more_alignment',
[
'label' => esc_html__( 'Automatically align buttons', 'elementor-pro' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'elementor-pro' ),
'label_off' => esc_html__( 'No', 'elementor-pro' ),
'default' => '',
'render_type' => 'template',
'selectors' => [
// --item-display is used for the styling of both elementor-post__card and elementor-post__text
'{{WRAPPER}}' => '--item-display: flex; --read-more-alignment: 1;',
],
'condition' => [
$this->get_control_id( 'masonry!' ) => 'yes',
$this->get_control_id( 'show_read_more' ) => 'yes',
],
]
);
}
protected function register_link_controls() {
$this->add_control(
'open_new_tab',
[
'label' => esc_html__( 'Open in new window', 'elementor-pro' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'elementor-pro' ),
'label_off' => esc_html__( 'No', 'elementor-pro' ),
'default' => 'no',
'render_type' => 'none',
]
);
}
protected function get_optional_link_attributes_html() {
$settings = $this->parent->get_settings();
$new_tab_setting_key = $this->get_control_id( 'open_new_tab' );
$optional_attributes_html = 'yes' === $settings[ $new_tab_setting_key ] ? 'target="_blank"' : '';
return $optional_attributes_html;
}
protected function register_meta_data_controls() {
$this->add_control(
'meta_data',
[
'label' => esc_html__( 'Meta Data', 'elementor-pro' ),
'label_block' => true,
'type' => Controls_Manager::SELECT2,
'default' => [ 'date', 'comments' ],
'multiple' => true,
'options' => [
'author' => esc_html__( 'Author', 'elementor-pro' ),
'date' => esc_html__( 'Date', 'elementor-pro' ),
'time' => esc_html__( 'Time', 'elementor-pro' ),
'comments' => esc_html__( 'Comments', 'elementor-pro' ),
'modified' => esc_html__( 'Date Modified', 'elementor-pro' ),
],
'separator' => 'before',
]
);
$this->add_control(
'meta_separator',
[
'label' => esc_html__( 'Separator Between', 'elementor-pro' ),
'type' => Controls_Manager::TEXT,
'default' => '///',
'ai' => [
'active' => false,
],
'selectors' => [
'{{WRAPPER}} .elementor-post__meta-data span + span:before' => 'content: "{{VALUE}}"',
],
'condition' => [
$this->get_control_id( 'meta_data!' ) => [],
],
'dynamic' => [
'active' => true,
],
]
);
}
/**
* Style Tab
*/
protected function register_design_layout_controls() {
$this->start_controls_section(
'section_design_layout',
[
'label' => esc_html__( 'Layout', 'elementor-pro' ),
'tab' => Controls_Manager::TAB_STYLE,
]
);
$this->add_responsive_control(
'column_gap',
[
'label' => esc_html__( 'Columns Gap', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'default' => [
'size' => 30,
],
'range' => [
'px' => [
'min' => 0,
'max' => 100,
],
],
'selectors' => [
'{{WRAPPER}}' => '--grid-column-gap: {{SIZE}}{{UNIT}}',
],
]
);
$this->add_responsive_control(
'row_gap',
[
'label' => esc_html__( 'Rows Gap', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'default' => [
'size' => 35,
],
'range' => [
'px' => [
'min' => 0,
'max' => 100,
],
],
'frontend_available' => true,
'selectors' => [
'{{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}',
],
]
);
$this->add_control(
'alignment',
[
'label' => esc_html__( 'Alignment', 'elementor-pro' ),
'type' => Controls_Manager::CHOOSE,
'options' => [
'left' => [
'title' => esc_html__( 'Left', 'elementor-pro' ),
'icon' => 'eicon-text-align-left',
],
'center' => [
'title' => esc_html__( 'Center', 'elementor-pro' ),
'icon' => 'eicon-text-align-center',
],
'right' => [
'title' => esc_html__( 'Right', 'elementor-pro' ),
'icon' => 'eicon-text-align-right',
],
],
'prefix_class' => 'elementor-posts--align-',
]
);
$this->end_controls_section();
}
protected function register_design_image_controls() {
$this->start_controls_section(
'section_design_image',
[
'label' => esc_html__( 'Image', 'elementor-pro' ),
'tab' => Controls_Manager::TAB_STYLE,
'condition' => [
$this->get_control_id( 'thumbnail!' ) => 'none',
],
]
);
$this->add_responsive_control(
'img_border_radius',
[
'label' => esc_html__( 'Border Radius', 'elementor-pro' ),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ],
'selectors' => [
'{{WRAPPER}} .elementor-post__thumbnail' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
'condition' => [
$this->get_control_id( 'thumbnail!' ) => 'none',
],
]
);
$this->add_responsive_control(
'image_spacing',
[
'label' => esc_html__( 'Spacing', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'range' => [
'px' => [
'max' => 100,
],
],
'selectors' => [
'{{WRAPPER}}.elementor-posts--thumbnail-left .elementor-post__thumbnail__link' => 'margin-right: {{SIZE}}{{UNIT}}',
'{{WRAPPER}}.elementor-posts--thumbnail-right .elementor-post__thumbnail__link' => 'margin-left: {{SIZE}}{{UNIT}}',
'{{WRAPPER}}.elementor-posts--thumbnail-top .elementor-post__thumbnail__link' => 'margin-bottom: {{SIZE}}{{UNIT}}',
],
'default' => [
'size' => 20,
],
'condition' => [
$this->get_control_id( 'thumbnail!' ) => 'none',
],
]
);
$this->start_controls_tabs( 'thumbnail_effects_tabs' );
$this->start_controls_tab( 'normal',
[
'label' => esc_html__( 'Normal', 'elementor-pro' ),
]
);
$this->add_group_control(
Group_Control_Css_Filter::get_type(),
[
'name' => 'thumbnail_filters',
'selector' => '{{WRAPPER}} .elementor-post__thumbnail img',
]
);
$this->end_controls_tab();
$this->start_controls_tab( 'hover',
[
'label' => esc_html__( 'Hover', 'elementor-pro' ),
]
);
$this->add_group_control(
Group_Control_Css_Filter::get_type(),
[
'name' => 'thumbnail_hover_filters',
'selector' => '{{WRAPPER}} .elementor-post:hover .elementor-post__thumbnail img',
]
);
$this->end_controls_tab();
$this->end_controls_tabs();
$this->end_controls_section();
}
protected function register_design_content_controls() {
$this->start_controls_section(
'section_design_content',
[
'label' => esc_html__( 'Content', 'elementor-pro' ),
'tab' => Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'heading_title_style',
[
'label' => esc_html__( 'Title', 'elementor-pro' ),
'type' => Controls_Manager::HEADING,
'condition' => [
$this->get_control_id( 'show_title' ) => 'yes',
],
]
);
$this->add_control(
'title_color',
[
'label' => esc_html__( 'Color', 'elementor-pro' ),
'type' => Controls_Manager::COLOR,
'global' => [
'default' => Global_Colors::COLOR_SECONDARY,
],
'selectors' => [
'{{WRAPPER}} .elementor-post__title, {{WRAPPER}} .elementor-post__title a' => 'color: {{VALUE}};',
],
'condition' => [
$this->get_control_id( 'show_title' ) => 'yes',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'title_typography',
'global' => [
'default' => Global_Typography::TYPOGRAPHY_PRIMARY,
],
'selector' => '{{WRAPPER}} .elementor-post__title, {{WRAPPER}} .elementor-post__title a',
'condition' => [
$this->get_control_id( 'show_title' ) => 'yes',
],
]
);
$this->add_group_control(
Group_Control_Text_Stroke::get_type(),
[
'name' => 'text_stroke',
'selector' => '{{WRAPPER}} .elementor-post__title',
]
);
$this->add_responsive_control(
'title_spacing',
[
'label' => esc_html__( 'Spacing', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'range' => [
'px' => [
'max' => 100,
],
],
'selectors' => [
'{{WRAPPER}} .elementor-post__title' => 'margin-bottom: {{SIZE}}{{UNIT}};',
],
'condition' => [
$this->get_control_id( 'show_title' ) => 'yes',
],
]
);
$this->add_control(
'heading_meta_style',
[
'label' => esc_html__( 'Meta', 'elementor-pro' ),
'type' => Controls_Manager::HEADING,
'separator' => 'before',
'condition' => [
$this->get_control_id( 'meta_data!' ) => [],
],
]
);
$this->add_control(
'meta_color',
[
'label' => esc_html__( 'Color', 'elementor-pro' ),
'type' => Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .elementor-post__meta-data' => 'color: {{VALUE}};',
],
'condition' => [
$this->get_control_id( 'meta_data!' ) => [],
],
]
);
$this->add_control(
'meta_separator_color',
[
'label' => esc_html__( 'Separator Color', 'elementor-pro' ),
'type' => Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .elementor-post__meta-data span:before' => 'color: {{VALUE}};',
],
'condition' => [
$this->get_control_id( 'meta_data!' ) => [],
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'meta_typography',
'global' => [
'default' => Global_Typography::TYPOGRAPHY_SECONDARY,
],
'selector' => '{{WRAPPER}} .elementor-post__meta-data',
'condition' => [
$this->get_control_id( 'meta_data!' ) => [],
],
]
);
$this->add_responsive_control(
'meta_spacing',
[
'label' => esc_html__( 'Spacing', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'range' => [
'px' => [
'max' => 100,
],
],
'selectors' => [
'{{WRAPPER}} .elementor-post__meta-data' => 'margin-bottom: {{SIZE}}{{UNIT}};',
],
'condition' => [
$this->get_control_id( 'meta_data!' ) => [],
],
]
);
$this->add_control(
'heading_excerpt_style',
[
'label' => esc_html__( 'Excerpt', 'elementor-pro' ),
'type' => Controls_Manager::HEADING,
'separator' => 'before',
'condition' => [
$this->get_control_id( 'show_excerpt' ) => 'yes',
],
]
);
$this->add_control(
'excerpt_color',
[
'label' => esc_html__( 'Color', 'elementor-pro' ),
'type' => Controls_Manager::COLOR,
'selectors' => [
'{{WRAPPER}} .elementor-post__excerpt p' => 'color: {{VALUE}};',
],
'condition' => [
$this->get_control_id( 'show_excerpt' ) => 'yes',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'excerpt_typography',
'global' => [
'default' => Global_Typography::TYPOGRAPHY_TEXT,
],
'selector' => '{{WRAPPER}} .elementor-post__excerpt p',
'condition' => [
$this->get_control_id( 'show_excerpt' ) => 'yes',
],
]
);
$this->add_responsive_control(
'excerpt_spacing',
[
'label' => esc_html__( 'Spacing', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'range' => [
'px' => [
'max' => 100,
],
],
'selectors' => [
'{{WRAPPER}} .elementor-post__excerpt' => 'margin-bottom: {{SIZE}}{{UNIT}};',
],
'condition' => [
$this->get_control_id( 'show_excerpt' ) => 'yes',
],
]
);
$this->add_control(
'heading_readmore_style',
[
'label' => esc_html__( 'Read More', 'elementor-pro' ),
'type' => Controls_Manager::HEADING,
'separator' => 'before',
'condition' => [
$this->get_control_id( 'show_read_more' ) => 'yes',
],
]
);
$this->add_control(
'read_more_color',
[
'label' => esc_html__( 'Color', 'elementor-pro' ),
'type' => Controls_Manager::COLOR,
'global' => [
'default' => Global_Colors::COLOR_ACCENT,
],
'selectors' => [
'{{WRAPPER}} .elementor-post__read-more' => 'color: {{VALUE}};',
],
'condition' => [
$this->get_control_id( 'show_read_more' ) => 'yes',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'read_more_typography',
// The 'a' selector is added for specificity, for when this control's selector is used in globals CSS.
'selector' => '{{WRAPPER}} a.elementor-post__read-more',
'global' => [
'default' => Global_Typography::TYPOGRAPHY_ACCENT,
],
'condition' => [
$this->get_control_id( 'show_read_more' ) => 'yes',
],
]
);
$this->add_responsive_control(
'read_more_spacing',
[
'label' => esc_html__( 'Spacing', 'elementor-pro' ),
'type' => Controls_Manager::SLIDER,
'size_units' => [ 'px', 'em', 'rem', 'custom' ],
'range' => [
'px' => [
'max' => 100,
],
],
'selectors' => [
'{{WRAPPER}} .elementor-post__text' => 'margin-bottom: {{SIZE}}{{UNIT}};',
],
'condition' => [
$this->get_control_id( 'show_read_more' ) => 'yes',
],
]
);
$this->end_controls_section();
}
public function render() {
$this->parent->query_posts();
/** @var \WP_Query $query */
$query = $this->parent->get_query();
if ( ! $query->found_posts ) {
return;
}
$this->render_loop_header();
// It's the global `wp_query` it self. and the loop was started from the theme.
if ( $query->in_the_loop ) {
$this->current_permalink = get_permalink();
$this->render_post();
} else {
while ( $query->have_posts() ) {
$query->the_post();
$this->current_permalink = get_permalink();
$this->render_post();
}
}
wp_reset_postdata();
$this->render_loop_footer();
}
public function filter_excerpt_length() {
return $this->get_instance_value( 'excerpt_length' );
}
public function filter_excerpt_more( $more ) {
return '';
}
public function get_container_class() {
return 'elementor-posts--skin-' . $this->get_id();
}
protected function render_thumbnail() {
$thumbnail = $this->get_instance_value( 'thumbnail' );
if ( 'none' === $thumbnail && ! Plugin::elementor()->editor->is_edit_mode() ) {
return;
}
$settings = $this->parent->get_settings();
$setting_key = $this->get_control_id( 'thumbnail_size' );
$settings[ $setting_key ] = [
'id' => get_post_thumbnail_id(),
];
$thumbnail_html = Group_Control_Image_Size::get_attachment_image_html( $settings, $setting_key );
if ( empty( $thumbnail_html ) ) {
return;
}
$optional_attributes_html = $this->get_optional_link_attributes_html();
?>
<a class="elementor-post__thumbnail__link" href="<?php echo esc_attr( $this->current_permalink ); ?>" <?php echo esc_attr( $optional_attributes_html ); ?>>
<div class="elementor-post__thumbnail"><?php echo wp_kses_post( $thumbnail_html ); ?></div>
</a>
<?php
}
protected function render_title() {
if ( ! $this->get_instance_value( 'show_title' ) ) {
return;
}
$optional_attributes_html = $this->get_optional_link_attributes_html();
$tag = $this->get_instance_value( 'title_tag' );
?>
<<?php Utils::print_validated_html_tag( $tag ); ?> class="elementor-post__title">
<a href="<?php echo esc_attr( $this->current_permalink ); ?>" <?php echo esc_attr( $optional_attributes_html ); ?>>
<?php the_title(); ?>
</a>
</<?php Utils::print_validated_html_tag( $tag ); ?>>
<?php
}
protected function render_excerpt() {
add_filter( 'excerpt_more', [ $this, 'filter_excerpt_more' ], 20 );
add_filter( 'excerpt_length', [ $this, 'filter_excerpt_length' ], 20 );
if ( ! $this->get_instance_value( 'show_excerpt' ) ) {
return;
}
add_filter( 'excerpt_more', [ $this, 'filter_excerpt_more' ], 20 );
add_filter( 'excerpt_length', [ $this, 'filter_excerpt_length' ], 20 );
?>
<div class="elementor-post__excerpt">
<?php
global $post;
$apply_to_custom_excerpt = $this->get_instance_value( 'apply_to_custom_excerpt' );
// Force the manually-generated Excerpt length as well if the user chose to enable 'apply_to_custom_excerpt'.
if ( 'yes' === $apply_to_custom_excerpt && ! empty( $post->post_excerpt ) ) {
$max_length = (int) $this->get_instance_value( 'excerpt_length' );
$excerpt = apply_filters( 'the_excerpt', get_the_excerpt() );
$excerpt = ProUtils::trim_words( $excerpt, $max_length );
echo wp_kses_post( $excerpt );
} else {
the_excerpt();
}
?>
</div>
<?php
remove_filter( 'excerpt_length', [ $this, 'filter_excerpt_length' ], 20 );
remove_filter( 'excerpt_more', [ $this, 'filter_excerpt_more' ], 20 );
}
protected function render_read_more() {
$settings = $this->parent->get_settings_for_display();
$read_more_key = $this->get_control_id( 'read_more_text' );
$read_more = $settings[ $read_more_key ];
if ( ! $this->get_instance_value( 'show_read_more' ) ) {
return;
}
$aria_label_text = sprintf(
/* translators: %s: Post title. */
esc_attr__( 'Read more about %s', 'elementor-pro' ),
get_the_title()
);
$optional_attributes_html = $this->get_optional_link_attributes_html();
if ( $this->display_read_more_bottom() ) : ?>
<div class="elementor-post__read-more-wrapper">
<?php endif; ?>
<a class="elementor-post__read-more" href="<?php echo esc_url( $this->current_permalink ); ?>" aria-label="<?php echo esc_attr( $aria_label_text ); ?>" <?php Utils::print_unescaped_internal_string( $optional_attributes_html ); ?>>
<?php echo wp_kses_post( $read_more ); ?>
</a>
<?php if ( $this->display_read_more_bottom() ) : ?>
</div>
<?php endif;
}
protected function render_post_header() {
?>
<article <?php post_class( [ 'elementor-post elementor-grid-item' ] ); ?>>
<?php
}
protected function render_post_footer() {
?>
</article>
<?php
}
protected function render_text_header() {
?>
<div class="elementor-post__text">
<?php
}
protected function render_text_footer() {
?>
</div>
<?php
}
protected function get_loop_header_widget_classes() {
return [
'elementor-posts-container',
'elementor-posts',
$this->get_container_class(),
];
}
protected function render_loop_header() {
$classes = $this->get_loop_header_widget_classes();
/** @var \WP_Query $wp_query */
$wp_query = $this->parent->get_query();
// Use grid only if found posts.
if ( $wp_query->found_posts ) {
$classes[] = 'elementor-grid';
}
$render_attributes = apply_filters( 'elementor/skin/loop_header_attributes', [
'class' => $classes,
] );
$this->parent->add_render_attribute( 'container', $render_attributes );
?>
<div <?php $this->parent->print_render_attribute_string( 'container' ); ?>>
<?php
}
protected function render_message() {
$settings = $this->parent->get_settings_for_display();
?>
<div class="e-load-more-message"><?php echo esc_html( $settings['load_more_no_posts_custom_message'] ); ?></div>
<?php
}
protected function render_loop_footer() {
?>
</div>
<?php
$parent_settings = $this->parent->get_settings_for_display();
// If the skin has no pagination, there's nothing to render in the loop footer.
if ( ! isset( $parent_settings['pagination_type'] ) ) {
return;
}
$using_ajax_pagination = in_array( $parent_settings['pagination_type'], [
Posts_Base::LOAD_MORE_ON_CLICK,
Posts_Base::LOAD_MORE_INFINITE_SCROLL,
], true);
if ( $using_ajax_pagination && ! empty( $parent_settings['load_more_spinner']['value'] ) ) : ?>
<span class="e-load-more-spinner">
<?php Icons_Manager::render_icon( $parent_settings['load_more_spinner'], [ 'aria-hidden' => 'true' ] ); ?>
</span>
<?php endif; ?>
<?php
if ( '' === $parent_settings['pagination_type'] ) {
return;
}
$page_limit = $this->parent->get_query()->max_num_pages;
// Page limit control should not effect in load more mode.
if ( '' !== $parent_settings['pagination_page_limit'] && ! $using_ajax_pagination ) {
$page_limit = min( $parent_settings['pagination_page_limit'], $page_limit );
}
if ( 2 > $page_limit ) {
return;
}
$this->parent->add_render_attribute( 'pagination', 'class', 'elementor-pagination' );
$has_numbers = in_array( $parent_settings['pagination_type'], [ 'numbers', 'numbers_and_prev_next' ] );
$has_prev_next = in_array( $parent_settings['pagination_type'], [ 'prev_next', 'numbers_and_prev_next' ] );
$load_more_type = $parent_settings['pagination_type'];
$current_page = $this->parent->get_current_page();
$next_page = intval( $current_page ) + 1;
$this->parent->add_render_attribute( 'load_more_anchor', [
'data-page' => $current_page,
'data-max-page' => $this->parent->get_query()->max_num_pages,
'data-next-page' => $this->parent->get_wp_link_page( $next_page ),
] );
?>
<div class="e-load-more-anchor" <?php $this->parent->print_render_attribute_string( 'load_more_anchor' ); ?>></div>
<?php
if ( $using_ajax_pagination ) {
if ( 'load_more_on_click' === $load_more_type ) {
// The link-url control is hidden, so default value is added to keep the same style like button widget.
$this->parent->set_settings( 'link', [ 'url' => '#' ] );
$this->render_button( $this->parent );
}
$this->render_message();
return;
}
$links = [];
if ( $has_numbers ) {
$paginate_args = [
'type' => 'array',
'current' => $this->parent->get_current_page(),
'total' => $page_limit,
'prev_next' => false,
'show_all' => 'yes' !== $parent_settings['pagination_numbers_shorten'],
'before_page_number' => '<span class="elementor-screen-only">' . esc_html__( 'Page', 'elementor-pro' ) . '</span>',
];
if ( is_singular() && ! is_front_page() && ! $this->parent->is_rest_request() ) {
$paginate_args = $this->get_paginate_args_for_singular_post( $paginate_args );
}
if ( is_archive() && $this->parent->current_url_contains_taxonomy_filter() ) {
$paginate_args = $this->get_paginate_args_for_archive_with_filters( $paginate_args );
}
if ( $this->parent->is_rest_request() ) {
$paginate_args = $this->get_paginate_args_for_rest_request( $paginate_args );
}
$links = paginate_links( $paginate_args );
}
if ( $has_prev_next ) {
$prev_next = $this->parent->get_posts_nav_link( $page_limit );
array_unshift( $links, $prev_next['prev'] );
$links[] = $prev_next['next'];
}
// PHPCS - Seems that `$links` is safe.
?>
<nav class="elementor-pagination" aria-label="<?php esc_attr_e( 'Pagination', 'elementor-pro' ); ?>">
<?php echo implode( PHP_EOL, $links ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</nav>
<?php
}
protected function get_paginate_args_for_singular_post( $paginate_args ) {
global $wp_rewrite;
if ( $wp_rewrite->using_permalinks() ) {
$paginate_args['base'] = trailingslashit( get_permalink() ) . '%_%';
$paginate_args['format'] = user_trailingslashit( '%#%', 'single_paged' );
} else {
$paginate_args['format'] = '?page=%#%';
}
return $paginate_args;
}
protected function get_paginate_args_for_archive_with_filters( $paginate_args ) {
global $wp_rewrite;
if ( ! $wp_rewrite->using_permalinks() ) {
$paginate_args['format'] = '?page=%#%';
}
return $paginate_args;
}
protected function get_paginate_args_for_rest_request( $paginate_args ) {
global $wp_rewrite;
$link_unescaped = wp_get_referer();
$url_components = wp_parse_url( $link_unescaped );
$add_args = [];
if ( isset( $url_components['query'] ) ) {
wp_parse_str( $url_components['query'], $add_args );
}
$url_to_post_id = url_to_postid( $link_unescaped );
$pagination_base_url = 0 !== $url_to_post_id
? get_permalink( $url_to_post_id )
: get_query_var( 'pagination_base_url' );
if ( $wp_rewrite->using_permalinks() ) {
$paginate_args['base'] = trailingslashit( $pagination_base_url ) . '%_%';
$paginate_args['format'] = user_trailingslashit( '%#%', 'single_paged' );
$paginate_args['add_args'] = $add_args;
if ( 0 === $url_to_post_id ) {
unset( $paginate_args['format'] );
}
} else {
$paginate_args['base'] = trailingslashit( $pagination_base_url ) . '%_%';
$paginate_args['format'] = '&page=%#%';
$paginate_args['add_args'] = $add_args;
}
return $paginate_args;
}
protected function render_meta_data() {
/** @var array $settings e.g. [ 'author', 'date', ... ] */
$settings = $this->get_instance_value( 'meta_data' );
if ( empty( $settings ) ) {
return;
}
?>
<div class="elementor-post__meta-data">
<?php
if ( in_array( 'author', $settings ) ) {
$this->render_author();
}
if ( in_array( 'date', $settings ) ) {
$this->render_date_by_type();
}
if ( in_array( 'time', $settings ) ) {
$this->render_time();
}
if ( in_array( 'comments', $settings ) ) {
$this->render_comments();
}
if ( in_array( 'modified', $settings ) ) {
$this->render_date_by_type( 'modified' );
}
?>
</div>
<?php
}
protected function render_author() {
?>
<span class="elementor-post-author">
<?php the_author(); ?>
</span>
<?php
}
protected function render_date_by_type( $type = 'publish' ) {
?>
<span class="elementor-post-date">
<?php
switch ( $type ) :
case 'modified':
$date = get_the_modified_date();
break;
default:
$date = get_the_date();
endswitch;
/** This filter is documented in wp-includes/general-template.php */
// PHPCS - The date is safe.
echo apply_filters( 'the_date', $date, get_option( 'date_format' ), '', '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
</span>
<?php
}
protected function render_time() {
?>
<span class="elementor-post-time">
<?php the_time(); ?>
</span>
<?php
}
/**
* Check if the Read More links needs to be displayed at the bottom of the Post item.
*
* Conditions:
* 1) Read More aligned to the bottom
* 2) Masonry layout not used.
* 3) Display Read More link.
*
* @since 3.7.0
*
* @return boolean
*/
protected function display_read_more_bottom() {
$settings = $this->parent->get_settings();
if ( 'full_content' === $settings['_skin'] ) {
return false;
}
return 'yes' === $settings[ $this->get_control_id( 'read_more_alignment' ) ] &&
'yes' === $settings[ $this->get_control_id( 'show_read_more' ) ] &&
'yes' !== $settings[ $this->get_control_id( 'masonry' ) ];
}
protected function render_comments() {
?>
<span class="elementor-post-avatar">
<?php comments_number(); ?>
</span>
<?php
}
protected function render_post() {
$this->render_post_header();
$this->render_thumbnail();
$this->render_text_header();
$this->render_title();
$this->render_meta_data();
$this->render_excerpt();
$this->render_read_more();
$this->render_text_footer();
$this->render_post_footer();
}
}