File manager - Edit - /home/premiey/www/wp-includes/images/media/loop-builder.tar
Back
documents/loop.php 0000666 00000027434 15165766212 0010260 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Documents; use Elementor\Controls_Manager; use Elementor\Core\Base\Document; use ElementorPro\Modules\LoopBuilder\Files\Css\Loop as Loop_CSS; use ElementorPro\Modules\LoopBuilder\Files\Css\Loop_Preview; use ElementorPro\Modules\ThemeBuilder\Documents\Theme_Document; use ElementorPro\Core\Utils; use ElementorPro\Plugin; use ElementorPro\Modules\LoopBuilder\Module as LoopBuilderModule; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Loop extends Theme_Document { const DOCUMENT_TYPE = 'loop-item'; const RECOMMENDED_POSTS_WIDGET_NAMES = [ 'theme-post-title', 'theme-post-excerpt', 'theme-post-featured-image', 'theme-post-content', 'post-info', ]; const WIDGETS_TO_HIDE = [ 'loop-grid', 'woocommerce-product-data-tabs', 'loop-carousel', ]; public static function get_type() { return static::DOCUMENT_TYPE; } public static function get_title() { return esc_html__( 'Loop Item', 'elementor-pro' ); } public static function get_plural_title() { return esc_html__( 'Loop Items', 'elementor-pro' ); } protected static function get_site_editor_icon() { return 'eicon-loop-builder'; } public static function get_site_editor_tooltip_data() { return [ 'title' => esc_html__( 'What is a loop?', 'elementor-pro' ), 'content' => esc_html__( 'A Loop is a layout you can customize to display recurring dynamic content - like listings, posts, portfolios, products, , etc.', 'elementor-pro' ), 'tip' => esc_html__( 'Start by creating a master item. All the other instances in the grid will match this design. Then go back to the widget in the editor panel and assign both a template and a source of content. Your grid should populate automatically.', 'elementor-pro' ), 'docs' => 'https://go.elementor.com/app-theme-builder-loop', 'video_url' => 'https://www.youtube.com/embed/zMvY9XaE1YY', ]; } protected static function get_site_editor_thumbnail_url() { return ELEMENTOR_PRO_MODULES_URL . 'loop-builder/assets/images/loop-item.svg'; } public static function get_properties() { $properties = parent::get_properties(); $properties['support_conditions'] = false; return $properties; } public function save( $data ) { if ( isset( $data['settings']['source'] ) ) { update_post_meta( $this->get_main_id(), '_elementor_source', $data['settings']['source'] ); } parent::save( $data ); } public function get_container_attributes() { $attributes = Document::get_container_attributes(); $post_id = get_the_ID(); $attributes['class'] .= ' e-loop-item'; $attributes['class'] .= ' e-loop-item-' . $post_id; $attributes['class'] .= ' ' . esc_attr( implode( ' ', get_post_class( [], $post_id ) ) ); $attributes['data-custom-edit-handle'] = true; return $attributes; } public function get_initial_config() { $config = parent::get_initial_config(); $loop_builder_module = new LoopBuilderModule(); if ( 'post' === $loop_builder_module->get_source_type_from_post_meta( $this->get_main_id() ) ) { foreach ( static::RECOMMENDED_POSTS_WIDGET_NAMES as $recommended_posts_widget_name ) { $config['panel']['widgets_settings'][ $recommended_posts_widget_name ] = [ 'categories' => [ 'recommended' ], 'show_in_panel' => true, ]; } } $config['panel']['widgets_settings']['container'] = [ 'categories' => [ 'layout' ], ]; foreach ( static::WIDGETS_TO_HIDE as $widget_to_hide ) { $config['panel']['widgets_settings'][ $widget_to_hide ] = [ 'show_in_panel' => false, ]; } $config['container_attributes'] = $this->get_container_attributes(); return $config; } public static function get_site_editor_config() { $config = parent::get_site_editor_config(); $config['show_instances'] = false; return $config; } public function get_location_label() { return ''; } public function get_css_wrapper_selector() { return '.e-loop-item-' . $this->get_main_id(); } public static function get_preview_as_options() { $post_types = Utils::get_public_post_types(); $post_types_options = []; foreach ( $post_types as $post_type => $label ) { $post_types_options[ 'single/' . $post_type ] = get_post_type_object( $post_type )->labels->singular_name; } return [ 'single' => [ 'label' => esc_html__( 'Single', 'elementor-pro' ), 'options' => $post_types_options, ], ]; } protected function get_remote_library_config() { $config = parent::get_remote_library_config(); $config['type'] = self::DOCUMENT_TYPE; $config['default_route'] = 'templates/loop-items'; return $config; } /** * Get Edit Url * * Disable the Library modal for non-container (section) users. * * @return string */ public function get_edit_url() { $url = parent::get_edit_url(); if ( ! Plugin::elementor()->experiments->is_feature_active( 'container' ) ) { $url = str_replace( '#library', '', $url ); } return $url; } protected static function get_editor_panel_categories() { $new_categories = [ 'recommended' => [ 'title' => esc_html__( 'Recommended', 'elementor-pro' ), ], 'layout' => [ 'title' => esc_html__( 'Layout', 'elementor-pro' ), 'hideIfEmpty' => true, ], ]; return static::insert_categories_after_favorites( $new_categories ); } protected function register_controls() { parent::register_controls(); $this->remove_control( 'content_wrapper_html_tag' ); $this->update_preview_control(); $this->inject_width_control(); $this->add_query_section(); Plugin::elementor()->controls_manager->add_custom_css_controls( $this ); } /** * Get Wrapper Tags * * We remove the `content_wrapper_html_tag` control in this document and default to using a `div`. * The setting no longer exists when printing the document element, so we need to override this method so that * the extended document class defaults to using a `div` when printing the element. * * @since 3.8.0 * * @return false */ public function get_wrapper_tags() { return false; } /** * Print elements with wrapper. * * Overwrite method from theme-document.php to render some custom markup if a variable * $elements_data['empty_loop_template'] is set. This variable is set via a filter hook * 'elementor/frontend/builder_content_data' in the loop builder module. * * @since 3.8.0 * * @param $elements_data * * @return void */ public function print_elements_with_wrapper( $elements_data = null ) { if ( isset( $elements_data['empty_loop_template'] ) ) { $this->print_empty_loop_template_markup( $elements_data['empty_loop_template_id'] ); } else { parent::print_elements_with_wrapper( $elements_data ); } } private function enqueue_loop_css() { if ( $this->is_autosave() ) { $css_file = Loop_Preview::create( $this->post->ID ); } else { $css_file = Loop_CSS::create( $this->post->ID ); } $css_file->print_all_css( $this->post->ID ); } /** * Get content. * * Override the parent method to retrieve the content with CSS in the Editor. * * @since 3.8.0 */ public function get_content( $with_css = false ) { $edit_mode = Plugin::elementor()->editor->is_edit_mode(); add_filter( 'elementor/frontend/builder_content/before_print_css', [ $this, 'prevent_inline_css_printing' ] ); $this->enqueue_loop_css(); Plugin::elementor()->editor->set_edit_mode( false ); $content = parent::get_content(); remove_filter( 'elementor/frontend/builder_content/before_print_css', [ $this, 'prevent_inline_css_printing' ] ); Plugin::elementor()->editor->set_edit_mode( $edit_mode ); return $content; } /** * Runs on the 'elementor/frontend/builder_content/before_print_css' hook. * * @return false */ public function prevent_inline_css_printing() { return false; } /** * Print empty loop template markup. * * This function is used to render markup in the editor when a loop template is empty/blank. * Currently, nothing will be rendered in the editor if the template is empty. * This markup is needed in the DOM for us to be able to switch to this document in place. * * @since 3.8.0 * * @param int $post_id The post ID of the document. * * @return void */ protected function print_empty_loop_template_markup( $post_id ) { ?> <div data-elementor-type="<?php echo esc_attr( static::get_type() ); ?>" data-elementor-post-type="<?php echo esc_attr( $this->get_post()->post_type ); ?>" data-elementor-id="<?php echo esc_attr( $post_id ); ?>" class="elementor elementor-<?php echo esc_attr( $post_id ); ?> elementor-edit-area elementor-edit-mode elementor-edit-area-active e-loop-first-edit" data-elementor-title="<?php echo esc_attr( ucfirst( static::get_type() ) ); ?>" > <div class="elementor-section-wrap ui-sortable"></div> </div> <?php } /** * @return void */ protected function add_query_section() { $this->start_controls_section( '_section_query', [ 'label' => esc_html__( 'Query', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_SETTINGS, ] ); $loop_builder_module = new LoopBuilderModule(); $source_type = $loop_builder_module->get_source_type_from_post_meta( $this->get_main_id() ); $this->add_control( 'source', [ 'label' => esc_html__( 'Source Type', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'post' => esc_html__( 'Posts', 'elementor-pro' ), ], 'default' => $source_type, 'prefix_class' => 'elementor-view-', ] ); do_action( 'elementor-pro/modules/loop-builder/documents/loop/query_settings', $this ); $this->add_control( 'apply_query_source', [ 'type' => Controls_Manager::BUTTON, 'label' => esc_html__( 'Apply', 'elementor-pro' ), 'label_block' => true, 'show_label' => false, 'text' => esc_html__( 'Apply', 'elementor-pro' ), 'separator' => 'none', 'event' => 'elementorLoopBuilder:ApplySourceChange', ] ); $this->add_control( 'query_source_description', [ 'raw' => esc_html__( 'This affects the types of widgets and templates you can use for your master item.', 'elementor-pro' ), 'type' => Controls_Manager::RAW_HTML, 'content_classes' => 'elementor-descriptor', ] ); $this->end_controls_section(); } /** * @return void */ protected function inject_width_control() { $this->start_injection([ 'type' => 'section', 'at' => 'start', 'of' => 'preview_settings', ]); $this->add_responsive_control( 'preview_width', [ 'label' => esc_html__( 'Width', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'range' => [ 'px' => [ 'min' => 200, 'max' => 1140, ], ], 'selectors' => [ '{{WRAPPER}}' => '--preview-width: {{SIZE}}{{UNIT}};', ], ] ); $this->end_injection(); } /** * @return void */ protected function update_preview_control() { $loop_builder_module = new LoopBuilderModule(); $source_type = $loop_builder_module->get_source_type_from_post_meta( $this->get_main_id() ); $this->update_control( 'preview_type', [ 'default' => 'single/' . $source_type, 'label' => esc_html__( 'Preview a specific post or item', 'elementor-pro' ), ] ); $latest_posts = get_posts([ 'posts_per_page' => 1, 'post_type' => $source_type, ]); if ( ! empty( $latest_posts ) ) { $this->update_control( 'preview_id', [ 'default' => $latest_posts[0]->ID, ] ); } } /** * @param array $new_categories * @return array */ private static function insert_categories_after_favorites( array $new_categories ) { $existing_categories = parent::get_editor_panel_categories(); $category_keys = array_keys( $existing_categories ); $index = array_search( 'favorites', $category_keys, true ); return array_splice( $existing_categories, 0, $index + 1 ) + $new_categories + array_splice( $existing_categories, $index + 1 ); } } assets/images/loop-item.svg 0000666 00000051270 15165766212 0011765 0 ustar 00 <svg width="187" height="180" viewBox="0 0 187 180" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect x="0.0273438" y="0.998047" width="186.487" height="178.379" fill="#FDFDFD"/> <mask id="mask0_1525_39115" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="187" height="180"> <rect x="0.0273438" y="0.998047" width="186.487" height="178.379" fill="white"/> </mask> <g mask="url(#mask0_1525_39115)"> <mask id="mask1_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="71" y="35" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M71.9219 35.6465H114.895V59.9709H71.9219V35.6465Z" fill="white"/> </mask> <g mask="url(#mask1_1525_39115)"> <rect x="71.1875" y="33.52" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask2_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="71" y="33" width="46" height="31"> <rect x="71.1875" y="33.52" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask2_1525_39115)"> <path d="M123.906 65.839C123.906 65.839 112.005 43.206 102.999 41.368C93.9934 39.53 90.466 57.3383 85.0767 54.9764C84.0756 54.5377 78.5571 48.2733 74.1015 50.0308C70.8968 51.2949 64.0663 65.9218 64.0663 65.9218H123.906V65.839Z" fill="#F8F9FA"/> <path d="M84.5399 48.5701C85.5759 48.5701 86.4158 47.7406 86.4158 46.7173C86.4158 45.694 85.5759 44.8645 84.5399 44.8645C83.5039 44.8645 82.6641 45.694 82.6641 46.7173C82.6641 47.7406 83.5039 48.5701 84.5399 48.5701Z" fill="#F8F9FA"/> </g> </g> <rect x="71.9219" y="63.2407" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="79.457" width="25.946" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="71.3489" width="38.1082" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="67.2947" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="75.4028" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <mask id="mask3_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="71" y="93" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M71.9219 93.6465H114.895V117.971H71.9219V93.6465Z" fill="white"/> </mask> <g mask="url(#mask3_1525_39115)"> <rect x="71.1875" y="91.52" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask4_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="71" y="91" width="46" height="31"> <rect x="71.1875" y="91.52" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask4_1525_39115)"> <path d="M123.906 123.839C123.906 123.839 112.005 101.206 102.999 99.368C93.9934 97.53 90.466 115.338 85.0767 112.976C84.0756 112.538 78.5571 106.273 74.1015 108.031C70.8968 109.295 64.0663 123.922 64.0663 123.922H123.906V123.839Z" fill="#F8F9FA"/> <path d="M84.5399 106.57C85.5759 106.57 86.4158 105.741 86.4158 104.717C86.4158 103.694 85.5759 102.865 84.5399 102.865C83.5039 102.865 82.6641 103.694 82.6641 104.717C82.6641 105.741 83.5039 106.57 84.5399 106.57Z" fill="#F8F9FA"/> </g> </g> <rect x="71.9219" y="121.241" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="137.457" width="25.946" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="129.349" width="38.1082" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="125.295" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="133.403" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <mask id="mask5_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="71" y="147" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M71.9219 147.646H114.895V171.971H71.9219V147.646Z" fill="white"/> </mask> <g mask="url(#mask5_1525_39115)"> <rect x="71.1875" y="145.52" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask6_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="71" y="145" width="46" height="31"> <rect x="71.1875" y="145.52" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask6_1525_39115)"> <path d="M123.906 177.839C123.906 177.839 112.005 155.206 102.999 153.368C93.9934 151.53 90.466 169.338 85.0767 166.976C84.0756 166.538 78.5571 160.273 74.1015 162.031C70.8968 163.295 64.0663 177.922 64.0663 177.922H123.906V177.839Z" fill="#F8F9FA"/> <path d="M84.5399 160.57C85.5759 160.57 86.4158 159.741 86.4158 158.717C86.4158 157.694 85.5759 156.865 84.5399 156.865C83.5039 156.865 82.6641 157.694 82.6641 158.717C82.6641 159.741 83.5039 160.57 84.5399 160.57Z" fill="#F8F9FA"/> </g> </g> <rect x="71.9219" y="175.241" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="71.9219" y="179.295" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <mask id="mask7_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="19" y="93" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M19.9219 93.6465H62.895V117.971H19.9219V93.6465Z" fill="white"/> </mask> <g mask="url(#mask7_1525_39115)"> <rect x="19.1875" y="91.52" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask8_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="19" y="91" width="46" height="31"> <rect x="19.1875" y="91.52" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask8_1525_39115)"> <path d="M71.9062 123.839C71.9062 123.839 60.0055 101.206 50.9994 99.368C41.9934 97.53 38.466 115.338 33.0767 112.976C32.0756 112.538 26.5571 106.273 22.1015 108.031C18.8968 109.295 12.0663 123.922 12.0663 123.922H71.9062V123.839Z" fill="#F8F9FA"/> <path d="M32.5399 106.57C33.5759 106.57 34.4158 105.741 34.4158 104.717C34.4158 103.694 33.5759 102.865 32.5399 102.865C31.5039 102.865 30.6641 103.694 30.6641 104.717C30.6641 105.741 31.5039 106.57 32.5399 106.57Z" fill="#F8F9FA"/> </g> </g> <rect x="19.9219" y="121.241" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="19.9219" y="137.457" width="25.946" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="19.9219" y="129.349" width="38.1082" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="19.9219" y="125.295" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="19.9219" y="133.403" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <mask id="mask9_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="19" y="147" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M19.9219 147.646H62.895V171.971H19.9219V147.646Z" fill="white"/> </mask> <g mask="url(#mask9_1525_39115)"> <rect x="19.1875" y="145.52" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask10_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="19" y="145" width="46" height="31"> <rect x="19.1875" y="145.52" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask10_1525_39115)"> <path d="M71.9062 177.839C71.9062 177.839 60.0055 155.206 50.9994 153.368C41.9934 151.53 38.466 169.338 33.0767 166.976C32.0756 166.538 26.5571 160.273 22.1015 162.031C18.8968 163.295 12.0663 177.922 12.0663 177.922H71.9062V177.839Z" fill="#F8F9FA"/> <path d="M32.5399 160.57C33.5759 160.57 34.4158 159.741 34.4158 158.717C34.4158 157.694 33.5759 156.865 32.5399 156.865C31.5039 156.865 30.6641 157.694 30.6641 158.717C30.6641 159.741 31.5039 160.57 32.5399 160.57Z" fill="#F8F9FA"/> </g> </g> <rect x="19.9219" y="175.241" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="19.9219" y="179.295" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <mask id="mask11_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="123" y="35" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M123.922 35.4058H166.895V59.7302H123.922V35.4058Z" fill="white"/> </mask> <g mask="url(#mask11_1525_39115)"> <rect x="123.188" y="33.2793" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask12_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="123" y="33" width="46" height="30"> <rect x="123.188" y="33.2793" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask12_1525_39115)"> <path d="M175.906 65.5983C175.906 65.5983 164.005 42.9652 154.999 41.1273C145.993 39.2893 142.466 57.0976 137.077 54.7357C136.076 54.297 130.557 48.0325 126.102 49.7901C122.897 51.0542 116.066 65.6811 116.066 65.6811H175.906V65.5983Z" fill="#F8F9FA"/> <path d="M136.54 48.3294C137.576 48.3294 138.416 47.4999 138.416 46.4766C138.416 45.4533 137.576 44.6238 136.54 44.6238C135.504 44.6238 134.664 45.4533 134.664 46.4766C134.664 47.4999 135.504 48.3294 136.54 48.3294Z" fill="#F8F9FA"/> </g> </g> <rect x="123.922" y="63" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="79.2163" width="25.946" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="71.1082" width="38.1082" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="67.054" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="75.1621" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <mask id="mask13_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="123" y="93" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M123.922 93.6465H166.895V117.971H123.922V93.6465Z" fill="white"/> </mask> <g mask="url(#mask13_1525_39115)"> <rect x="123.188" y="91.52" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask14_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="123" y="91" width="46" height="31"> <rect x="123.188" y="91.52" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask14_1525_39115)"> <path d="M175.906 123.839C175.906 123.839 164.005 101.206 154.999 99.368C145.993 97.53 142.466 115.338 137.077 112.976C136.076 112.538 130.557 106.273 126.102 108.031C122.897 109.295 116.066 123.922 116.066 123.922H175.906V123.839Z" fill="#F8F9FA"/> <path d="M136.54 106.57C137.576 106.57 138.416 105.741 138.416 104.717C138.416 103.694 137.576 102.865 136.54 102.865C135.504 102.865 134.664 103.694 134.664 104.717C134.664 105.741 135.504 106.57 136.54 106.57Z" fill="#F8F9FA"/> </g> </g> <rect x="123.922" y="121.241" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="137.457" width="25.946" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="129.349" width="38.1082" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="125.295" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="133.403" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <mask id="mask15_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="123" y="147" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M123.922 147.646H166.895V171.971H123.922V147.646Z" fill="white"/> </mask> <g mask="url(#mask15_1525_39115)"> <rect x="123.188" y="145.52" width="45.4287" height="29.6572" fill="#E6E9EC"/> <mask id="mask16_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="123" y="145" width="46" height="31"> <rect x="123.188" y="145.52" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask16_1525_39115)"> <path d="M175.906 177.839C175.906 177.839 164.005 155.206 154.999 153.368C145.993 151.53 142.466 169.338 137.077 166.976C136.076 166.538 130.557 160.273 126.102 162.031C122.897 163.295 116.066 177.922 116.066 177.922H175.906V177.839Z" fill="#F8F9FA"/> <path d="M136.54 160.57C137.576 160.57 138.416 159.741 138.416 158.717C138.416 157.694 137.576 156.865 136.54 156.865C135.504 156.865 134.664 157.694 134.664 158.717C134.664 159.741 135.504 160.57 136.54 160.57Z" fill="#F8F9FA"/> </g> </g> <rect x="123.922" y="175.241" width="30.8109" height="1.62163" rx="0.810813" fill="#D5DADF"/> <rect x="123.922" y="179.295" width="42.9731" height="1.62163" rx="0.810813" fill="#D5DADF"/> <path d="M21.1185 20.8586V21.6465H18.2244V20.8586H21.1185ZM18.5015 15.8821V21.6465H17.5078V15.8821H18.5015ZM21.6094 19.5521V19.4611C21.6094 19.1523 21.6543 18.8659 21.744 18.602C21.8337 18.3354 21.9631 18.1045 22.132 17.9091C22.3035 17.7112 22.5121 17.5581 22.7575 17.4499C23.0056 17.339 23.2854 17.2836 23.5968 17.2836C23.9109 17.2836 24.1907 17.339 24.4362 17.4499C24.6843 17.5581 24.8941 17.7112 25.0656 17.9091C25.2372 18.1045 25.3678 18.3354 25.4576 18.602C25.5473 18.8659 25.5922 19.1523 25.5922 19.4611V19.5521C25.5922 19.861 25.5473 20.1473 25.4576 20.4113C25.3678 20.6752 25.2372 20.9061 25.0656 21.1041C24.8941 21.2994 24.6856 21.4525 24.4401 21.5633C24.1946 21.6716 23.9162 21.7257 23.6048 21.7257C23.2907 21.7257 23.0096 21.6716 22.7615 21.5633C22.516 21.4525 22.3075 21.2994 22.1359 21.1041C21.9644 20.9061 21.8337 20.6752 21.744 20.4113C21.6543 20.1473 21.6094 19.861 21.6094 19.5521ZM22.5635 19.4611V19.5521C22.5635 19.7448 22.5833 19.9269 22.6229 20.0985C22.6625 20.2701 22.7245 20.4205 22.809 20.5498C22.8934 20.6792 23.0017 20.7808 23.1336 20.8547C23.2656 20.9286 23.4226 20.9655 23.6048 20.9655C23.7816 20.9655 23.9347 20.9286 24.064 20.8547C24.196 20.7808 24.3042 20.6792 24.3886 20.5498C24.4731 20.4205 24.5351 20.2701 24.5747 20.0985C24.6169 19.9269 24.6381 19.7448 24.6381 19.5521V19.4611C24.6381 19.2711 24.6169 19.0916 24.5747 18.9227C24.5351 18.7511 24.4718 18.5993 24.3847 18.4674C24.3002 18.3354 24.192 18.2325 24.06 18.1586C23.9307 18.082 23.7763 18.0437 23.5968 18.0437C23.4174 18.0437 23.2616 18.082 23.1297 18.1586C23.0003 18.2325 22.8934 18.3354 22.809 18.4674C22.7245 18.5993 22.6625 18.7511 22.6229 18.9227C22.5833 19.0916 22.5635 19.2711 22.5635 19.4611ZM26.2098 19.5521V19.4611C26.2098 19.1523 26.2547 18.8659 26.3444 18.602C26.4342 18.3354 26.5635 18.1045 26.7324 17.9091C26.904 17.7112 27.1125 17.5581 27.3579 17.4499C27.606 17.339 27.8858 17.2836 28.1972 17.2836C28.5113 17.2836 28.7911 17.339 29.0366 17.4499C29.2847 17.5581 29.4945 17.7112 29.6661 17.9091C29.8376 18.1045 29.9683 18.3354 30.058 18.602C30.1477 18.8659 30.1926 19.1523 30.1926 19.4611V19.5521C30.1926 19.861 30.1477 20.1473 30.058 20.4113C29.9683 20.6752 29.8376 20.9061 29.6661 21.1041C29.4945 21.2994 29.286 21.4525 29.0405 21.5633C28.7951 21.6716 28.5166 21.7257 28.2052 21.7257C27.8911 21.7257 27.61 21.6716 27.3619 21.5633C27.1164 21.4525 26.9079 21.2994 26.7364 21.1041C26.5648 20.9061 26.4342 20.6752 26.3444 20.4113C26.2547 20.1473 26.2098 19.861 26.2098 19.5521ZM27.1639 19.4611V19.5521C27.1639 19.7448 27.1837 19.9269 27.2233 20.0985C27.2629 20.2701 27.3249 20.4205 27.4094 20.5498C27.4939 20.6792 27.6021 20.7808 27.734 20.8547C27.866 20.9286 28.0231 20.9655 28.2052 20.9655C28.382 20.9655 28.5351 20.9286 28.6644 20.8547C28.7964 20.7808 28.9046 20.6792 28.9891 20.5498C29.0735 20.4205 29.1355 20.2701 29.1751 20.0985C29.2174 19.9269 29.2385 19.7448 29.2385 19.5521V19.4611C29.2385 19.2711 29.2174 19.0916 29.1751 18.9227C29.1355 18.7511 29.0722 18.5993 28.9851 18.4674C28.9006 18.3354 28.7924 18.2325 28.6605 18.1586C28.5311 18.082 28.3767 18.0437 28.1972 18.0437C28.0178 18.0437 27.8621 18.082 27.7301 18.1586C27.6008 18.2325 27.4939 18.3354 27.4094 18.4674C27.3249 18.5993 27.2629 18.7511 27.2233 18.9227C27.1837 19.0916 27.1639 19.2711 27.1639 19.4611ZM31.9504 18.1863V23.2934H30.9963V17.3628H31.8752L31.9504 18.1863ZM34.7416 19.465V19.5482C34.7416 19.8596 34.7046 20.1486 34.6307 20.4152C34.5594 20.6792 34.4525 20.9101 34.31 21.1081C34.1701 21.3034 33.9973 21.4551 33.7914 21.5633C33.5855 21.6716 33.348 21.7257 33.0788 21.7257C32.8122 21.7257 32.5786 21.6768 32.378 21.5792C32.1801 21.4789 32.0125 21.3377 31.8752 21.1556C31.738 20.9734 31.6271 20.7597 31.5426 20.5142C31.4608 20.2661 31.4028 19.9942 31.3684 19.6986V19.3779C31.4028 19.0639 31.4608 18.7788 31.5426 18.5228C31.6271 18.2668 31.738 18.0464 31.8752 17.8616C32.0125 17.6769 32.1801 17.5344 32.378 17.4341C32.576 17.3338 32.8069 17.2836 33.0708 17.2836C33.3401 17.2836 33.5789 17.3364 33.7874 17.442C33.9959 17.5449 34.1715 17.6927 34.314 17.8854C34.4565 18.0754 34.5634 18.305 34.6347 18.5743C34.7059 18.8408 34.7416 19.1378 34.7416 19.465ZM33.7874 19.5482V19.465C33.7874 19.2671 33.769 19.0837 33.732 18.9147C33.6951 18.7432 33.637 18.5927 33.5578 18.4634C33.4786 18.3341 33.377 18.2338 33.253 18.1625C33.1315 18.0886 32.9851 18.0517 32.8135 18.0517C32.6446 18.0517 32.4994 18.0807 32.378 18.1388C32.2566 18.1942 32.155 18.2721 32.0732 18.3724C31.9913 18.4726 31.928 18.5901 31.8831 18.7247C31.8383 18.8567 31.8066 19.0005 31.7881 19.1562V19.9243C31.8198 20.1143 31.8739 20.2885 31.9504 20.4469C32.027 20.6053 32.1352 20.7319 32.2751 20.827C32.4176 20.9193 32.5997 20.9655 32.8214 20.9655C32.993 20.9655 33.1395 20.9286 33.2609 20.8547C33.3823 20.7808 33.4813 20.6792 33.5578 20.5498C33.637 20.4179 33.6951 20.2661 33.732 20.0945C33.769 19.923 33.7874 19.7409 33.7874 19.5482ZM39.8487 15.8821V21.6465H38.8629V15.8821H39.8487ZM41.658 15.8821V16.6739H37.0695V15.8821H41.658ZM43.3446 17.3628V21.6465H42.3865V17.3628H43.3446ZM42.3231 16.2384C42.3231 16.0933 42.3706 15.9732 42.4657 15.8781C42.5633 15.7805 42.6979 15.7317 42.8695 15.7317C43.0384 15.7317 43.1717 15.7805 43.2694 15.8781C43.367 15.9732 43.4158 16.0933 43.4158 16.2384C43.4158 16.3809 43.367 16.4997 43.2694 16.5947C43.1717 16.6898 43.0384 16.7373 42.8695 16.7373C42.6979 16.7373 42.5633 16.6898 42.4657 16.5947C42.3706 16.4997 42.3231 16.3809 42.3231 16.2384ZM46.3535 17.3628V18.0596H43.9384V17.3628H46.3535ZM44.6352 16.3136H45.5894V20.4627C45.5894 20.5947 45.6078 20.6963 45.6448 20.7676C45.6844 20.8362 45.7385 20.8824 45.8071 20.9061C45.8757 20.9299 45.9562 20.9418 46.0486 20.9418C46.1146 20.9418 46.1779 20.9378 46.2386 20.9299C46.2993 20.922 46.3482 20.9141 46.3851 20.9061L46.3891 21.6346C46.3099 21.6584 46.2175 21.6795 46.112 21.698C46.009 21.7164 45.8902 21.7257 45.7556 21.7257C45.5366 21.7257 45.3426 21.6874 45.1737 21.6109C45.0047 21.5317 44.8728 21.4037 44.7778 21.2268C44.6827 21.05 44.6352 20.8151 44.6352 20.5221V16.3136ZM48.1113 15.5654V21.6465H47.1532V15.5654H48.1113ZM51.0449 21.7257C50.7282 21.7257 50.4418 21.6742 50.1858 21.5713C49.9324 21.4657 49.716 21.3192 49.5365 21.1318C49.3597 20.9444 49.2238 20.724 49.1287 20.4706C49.0337 20.2173 48.9862 19.9441 48.9862 19.6511V19.4928C48.9862 19.1576 49.035 18.854 49.1327 18.5822C49.2304 18.3103 49.3663 18.0781 49.5405 17.8854C49.7147 17.6901 49.9206 17.5409 50.1581 17.438C50.3956 17.3351 50.653 17.2836 50.9301 17.2836C51.2363 17.2836 51.5042 17.3351 51.7338 17.438C51.9634 17.5409 52.1535 17.6861 52.3039 17.8735C52.457 18.0583 52.5705 18.2787 52.6444 18.5347C52.7209 18.7907 52.7592 19.0731 52.7592 19.3819V19.7897H49.4494V19.1048H51.8169V19.0296C51.8117 18.858 51.7774 18.697 51.714 18.5465C51.6533 18.3961 51.5596 18.2747 51.4329 18.1823C51.3062 18.0899 51.1373 18.0437 50.9262 18.0437C50.7678 18.0437 50.6266 18.0781 50.5025 18.1467C50.3811 18.2127 50.2795 18.309 50.1977 18.4357C50.1159 18.5624 50.0525 18.7155 50.0077 18.8949C49.9654 19.0718 49.9443 19.2711 49.9443 19.4928V19.6511C49.9443 19.8385 49.9694 20.0127 50.0195 20.1737C50.0723 20.3321 50.1489 20.4706 50.2492 20.5894C50.3495 20.7082 50.4709 20.8019 50.6134 20.8705C50.7559 20.9365 50.9182 20.9695 51.1004 20.9695C51.33 20.9695 51.5345 20.9233 51.714 20.8309C51.8935 20.7385 52.0492 20.6079 52.1812 20.439L52.684 20.9259C52.5916 21.0605 52.4715 21.1899 52.3237 21.3139C52.1759 21.4353 51.9951 21.5343 51.7813 21.6109C51.5702 21.6874 51.3247 21.7257 51.0449 21.7257Z" fill="#A4AFB7"/> <mask id="mask17_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="19" y="35" width="44" height="25"> <path fill-rule="evenodd" clip-rule="evenodd" d="M19.8398 35.4058H62.813V59.7302H19.8398V35.4058Z" fill="white"/> </mask> <g mask="url(#mask17_1525_39115)"> <rect x="19.1055" y="33.2793" width="45.4287" height="29.6572" fill="#A4AFB7"/> <mask id="mask18_1525_39115" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="19" y="33" width="46" height="30"> <rect x="19.1055" y="33.2793" width="45.4287" height="29.6572" fill="white"/> </mask> <g mask="url(#mask18_1525_39115)"> <path d="M71.8242 65.5983C71.8242 65.5983 59.9234 42.9652 50.9174 41.1273C41.9113 39.2893 38.3839 57.0976 32.9946 54.7357C31.9936 54.297 26.4751 48.0325 22.0195 49.7901C18.8148 51.0542 11.9842 65.6811 11.9842 65.6811H71.8242V65.5983Z" fill="#F8F9FA"/> <path d="M32.4579 48.3294C33.4939 48.3294 34.3338 47.4999 34.3338 46.4766C34.3338 45.4533 33.4939 44.6238 32.4579 44.6238C31.4219 44.6238 30.582 45.4533 30.582 46.4766C30.582 47.4999 31.4219 48.3294 32.4579 48.3294Z" fill="#F8F9FA"/> </g> </g> <rect x="19.8398" y="63" width="30.8109" height="1.62163" rx="0.810813" fill="#A4AFB7"/> <rect x="19.8398" y="79.2163" width="25.946" height="1.62163" rx="0.810813" fill="#A4AFB7"/> <rect x="19.8398" y="71.1082" width="38.1082" height="1.62163" rx="0.810813" fill="#A4AFB7"/> <rect x="19.8398" y="67.054" width="42.9731" height="1.62163" rx="0.810813" fill="#A4AFB7"/> <rect x="19.8398" y="75.1621" width="42.9731" height="1.62163" rx="0.810813" fill="#A4AFB7"/> <rect x="17.4219" y="33.1465" width="48" height="51" stroke="#A4AFB7"/> </g> </svg> widgets/loop-carousel.php 0000666 00000014274 15165766212 0011536 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Widgets; use ElementorPro\Modules\LoopBuilder\Documents\Loop as LoopDocument; use Elementor\Controls_Manager; use Elementor\Core\Kits\Documents\Tabs\Global_Typography; use ElementorPro\Plugin; use ElementorPro\Base\Base_Carousel_Trait; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Loop_Carousel extends Base { use Base_Carousel_Trait; public function get_name() { return 'loop-carousel'; } public function get_title() { return esc_html__( 'Loop Carousel', 'elementor-pro' ); } public function get_keywords() { return [ 'loop', 'carousel', 'dynamic', 'listing', 'archive', 'blog', 'repeater', 'grid', 'products', 'posts', 'portfolio', 'cpt ', 'query', 'custom post type' ]; } public function get_icon() { return 'eicon-carousel-loop'; } protected function get_initial_config() { $config = parent::get_initial_config(); $config['add_parent_render_footer'] = false; return $config; } public function register_settings_section_controls() { $this->add_carousel_settings_controls( [ 'css_prefix' => '', ] ); } public function register_navigation_section_controls() { $this->add_carousel_navigation_controls( [ 'css_prefix' => '', 'navigation_custom_settings' => [ 'condition' => [ 'template_id!' => '', ], ], ] ); } public function register_pagination_section_controls() { $this->add_carousel_pagination_controls( [ 'css_prefix' => '', 'section_carousel_pagination' => [ 'condition' => [ 'template_id!' => '', ], ], ] ); } public function register_design_layout_controls() { $this->start_controls_section( 'section_layout_style', [ 'label' => esc_html__( 'Layout', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'image_spacing_custom', [ 'label' => esc_html__( 'Gap between slides', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 400, ], ], 'default' => [ 'size' => 10, ], 'frontend_available' => true, 'render_type' => 'none', 'selectors' => [ '{{WRAPPER}}' => '--swiper-slides-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_section(); } protected function register_design_navigation_controls() { $this->add_carousel_navigation_styling_controls( [ 'css_prefix' => '', 'navigation_styling_custom_settings' => [ 'condition' => [ 'arrows' => 'yes', 'template_id!' => '', ], ], ] ); } public function register_design_pagination_controls() { $this->add_carousel_pagination_style_controls( [ 'css_prefix' => '', ] ); } public function render_loop_header() { $has_autoplay_enabled = 'yes' === $this->get_settings_for_display( 'autoplay' ); $this->add_render_attribute( 'swiper-wrapper', [ 'class' => 'swiper-wrapper', 'aria-live' => $has_autoplay_enabled ? 'off' : 'polite', ] ); ?> <div <?php $this->print_render_attribute_string( 'swiper-wrapper' ); ?>> <?php } public function render_loop_footer() { ?> </div> </div> <?php $settings = $this->get_settings_for_display(); $this->render_carousel_footer( $settings ); } public function add_swiper_slide_attributes_to_loop_item( $attributes, $document ) { if ( LoopDocument::DOCUMENT_TYPE === $document::get_type() ) { $attributes['class'] .= ' swiper-slide'; $attributes['role'] = 'group'; $attributes['aria-roledescription'] = 'slide'; } return $attributes; } public function add_loop_header_attributes( $render_attributes ) { $settings = $this->get_settings_for_display(); if ( ! empty( $settings['direction'] ) ) { $render_attributes['dir'] = $settings['direction']; } return $render_attributes; } public function get_loop_header_widget_classes(): array { $swiper_class = Plugin::elementor()->experiments->is_feature_active( 'e_swiper_latest' ) ? 'swiper' : 'swiper-container'; return [ $swiper_class ]; } public function before_skin_render() { add_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_swiper_slide_attributes_to_loop_item' ], 10, 2 ); add_filter( 'elementor/skin/loop_header_attributes', [ $this, 'add_loop_header_attributes' ], 10, 1 ); } public function after_skin_render() { remove_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_swiper_slide_attributes_to_loop_item' ] ); remove_filter( 'elementor/skin/loop_header_attributes', [ $this, 'add_loop_header_attributes' ] ); } protected function register_layout_section() { parent::register_layout_section(); $this->start_injection( [ 'of' => 'template_id', ] ); $this->add_control( 'posts_per_page', [ 'label' => esc_html__( 'Number of slides', 'elementor-pro' ), 'type' => Controls_Manager::NUMBER, 'default' => 6, 'min' => 1, 'condition' => [ 'template_id!' => '', ], 'separator' => 'before', ] ); $this->add_carousel_layout_controls( [ 'css_prefix' => '', 'slides_to_show_custom_settings' => [ 'default' => '3', 'widescreen_default' => '3', 'laptop_default' => '3', 'tablet_extra_default' => '3', 'tablet_default' => '2', 'mobile_extra_default' => '2', 'mobile_default' => '1', 'condition' => [ 'posts_per_page!' => 1, 'template_id!' => '', ], 'selectors' => [ '{{WRAPPER}}' => '--swiper-slides-to-display: {{VALUE}}', ], ], 'slides_to_scroll_custom_settings' => [ 'default' => '1', 'condition' => [ 'posts_per_page!' => 1, 'template_id!' => '', ], ], 'equal_height_custom_settings' => [ 'condition' => [ 'posts_per_page!' => 1, 'template_id!' => '', ], 'selectors' => [ '{{WRAPPER}} .swiper-slide > .elementor-element' => 'height: 100%', ], ], 'slides_on_display' => 8, ] ); // Location for the Edit handle. $this->add_control( 'edit_handle_selector', [ 'label' => esc_html__( 'Edit Handle Selector', 'elementor-pro' ), 'type' => Controls_Manager::HIDDEN, 'default' => '.elementor-widget-container', 'render_type' => 'none', 'frontend_available' => true, ] ); $this->end_injection(); } } widgets/base.php 0000666 00000014257 15165766212 0007665 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Widgets; use Elementor\Controls_Manager; use Elementor\Core\Base\Document; use ElementorPro\Modules\LoopBuilder\Documents\Loop as LoopDocument; use ElementorPro\Modules\LoopBuilder\Module; use ElementorPro\Modules\LoopBuilder\Skins\Skin_Loop_Post; use ElementorPro\Modules\Posts\Widgets\Posts; use ElementorPro\Modules\QueryControl\Controls\Template_Query; use ElementorPro\Modules\QueryControl\Module as QueryControlModule; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Base extends Posts { public function get_group_name() { return 'loop-builder'; } /** * Get Query Name * * Returns the query control name used in the widget's main query. * * @since 3.8.0 * * @return string */ public function get_query_name() { $skin_id = str_replace( '-', '_', $this->get_current_skin_id() ); return $skin_id . '_' . Module::QUERY_ID; } protected function get_initial_config() { $config = parent::get_initial_config(); $config['is_loop'] = true; $config['add_parent_render_header'] = true; $config['add_parent_render_footer'] = true; return $config; } public function query_posts() { $skin = $this->get_current_skin(); $query = false; if ( $skin ) { $query = $skin->query_posts( $this ); } if ( $query ) { $this->query = $query; } else { parent::query_posts(); } } /** * Get Posts Per Page Value * * Returns the value of the Posts Per Page control of the widget. * * @since 3.8.0 * @access protected * * @return mixed */ public function get_posts_per_page_value() { return $this->get_settings_for_display( 'posts_per_page' ); } protected function register_skins() { $this->add_skin( new Skin_Loop_Post( $this ) ); } protected function register_controls() { $this->register_layout_section(); $this->register_query_section(); // Carousel $this->register_settings_section_controls(); $this->register_navigation_section_controls(); $this->register_pagination_section_controls(); $this->register_design_layout_controls(); $this->register_design_navigation_controls(); $this->register_design_pagination_controls(); // The `_skins` control determines the Loop's query source, so it is renamed for this to be clearer to the user. $this->update_control( '_skin', [ 'label' => esc_html__( 'Choose template type', 'elementor-pro' ), 'label_block' => true, 'frontend_available' => true, ] ); } /** * Register Layout Section * * This registers the Layout section in order to allow Skins to register their layout controls. * * @since 3.8.0 */ protected function register_layout_section() { $this->start_controls_section( 'section_layout', [ 'label' => esc_html__( 'Layout', 'elementor-pro' ), ] ); $this->add_control( 'template_id', [ 'label' => esc_html__( 'Choose a template', 'elementor-pro' ), 'type' => Template_Query::CONTROL_ID, 'label_block' => true, 'autocomplete' => [ 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, 'query' => [ 'post_status' => Document::STATUS_PUBLISH, 'meta_query' => [ [ 'key' => Document::TYPE_META_KEY, 'value' => LoopDocument::get_type(), 'compare' => 'IN', ], ], ], ], 'actions' => [ 'new' => [ 'visible' => true, 'document_config' => [ 'type' => LoopDocument::get_type(), ], ], 'edit' => [ 'visible' => true, ], ], 'frontend_available' => true, ] ); $this->end_controls_section(); } /** * Register Query Section * * This registers the Query section in order to allow Skins to register their query controls. * * @since 3.8.0 */ protected function register_query_section() { $this->start_controls_section( 'section_query', [ 'label' => esc_html__( 'Query', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->end_controls_section(); } public function register_pagination_section_controls() { parent::register_pagination_section_controls(); $this->remove_responsive_control( 'align' ); $this->start_injection( [ 'of' => 'text', ] ); $this->add_responsive_control( 'load_more_button_align', [ 'label' => esc_html__( 'Alignment', 'elementor-pro' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor-pro' ), 'icon' => 'eicon-text-align-left', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor-pro' ), 'icon' => 'eicon-text-align-center', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor-pro' ), 'icon' => 'eicon-text-align-right', ], 'justify' => [ 'title' => esc_html__( 'Justified', 'elementor-pro' ), 'icon' => 'eicon-text-align-justify', ], ], 'default' => 'center', 'condition' => [ 'pagination_type' => 'load_more_on_click', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'selectors_dictionary' => [ 'start' => '--load-more-button-align: start;', 'center' => '--load-more-button-align: center;', 'end' => '--load-more-button-align: end;', 'justify' => '--load-more-button-width: 100%;', ], ] ); $this->end_injection(); // Remove the HTML Entity arrows inherited from the Posts widget from the prev/next pagination link labels. $this->update_control( 'pagination_prev_label', [ 'default' => esc_html__( 'Previous', 'elementor-pro' ), ] ); $this->update_control( 'pagination_next_label', [ 'default' => esc_html__( 'Next', 'elementor-pro' ), ] ); } protected function register_design_layout_controls() {} protected function register_design_navigation_controls() {} protected function register_design_pagination_controls() {} public function register_settings_section_controls() {} public function register_navigation_section_controls() {} public function get_loop_header_widget_classes(): array { return []; } public function render_loop_header() {} public function render_loop_footer() {} public function before_skin_render() {} public function after_skin_render() {} } widgets/loop-grid.php 0000666 00000022075 15165766212 0010644 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Widgets; use Elementor\Controls_Manager; use Elementor\Repeater; use Elementor\Core\Base\Document; use ElementorPro\Modules\QueryControl\Controls\Template_Query; use ElementorPro\Modules\QueryControl\Module as QueryControlModule; use ElementorPro\Modules\LoopBuilder\Documents\Loop as LoopDocument; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Loop_Grid extends Base { public function get_name() { return 'loop-grid'; } public function get_title() { return esc_html__( 'Loop Grid', 'elementor-pro' ); } public function get_keywords() { return [ 'loop', 'dynamic', 'listing', 'archive', 'blog', 'repeater', 'grid', 'products', 'posts', 'portfolio', 'cpt', 'query', 'custom post type' ]; } public function get_icon() { return 'eicon-loop-builder'; } protected function register_layout_section() { parent::register_layout_section(); $this->start_injection( [ 'of' => 'template_id', ] ); $this->add_responsive_control( 'columns', [ 'label' => esc_html__( 'Columns', 'elementor-pro' ), 'type' => Controls_Manager::NUMBER, 'default' => '3', 'tablet_default' => '2', 'mobile_default' => '1', 'min' => 1, 'max' => 12, 'prefix_class' => 'elementor-grid%s-', 'frontend_available' => true, 'separator' => 'before', 'condition' => [ 'template_id!' => '', ], 'selectors' => [ '{{WRAPPER}}' => '--grid-columns: {{VALUE}}', ], ] ); $this->add_control( 'posts_per_page', [ 'label' => esc_html__( 'Items Per Page', 'elementor-pro' ), 'type' => Controls_Manager::NUMBER, 'default' => 6, 'min' => 1, 'condition' => [ 'template_id!' => '', ], ] ); $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' => [ 'columns!' => 1, 'template_id!' => '', ], 'render_type' => 'ui', 'frontend_available' => true, ] ); $this->add_control( 'equal_height', [ 'label' => esc_html__( 'Equal height', 'elementor-pro' ), 'type' => Controls_Manager::SWITCHER, 'label_off' => esc_html__( 'Off', 'elementor-pro' ), 'label_on' => esc_html__( 'On', 'elementor-pro' ), 'condition' => [ 'columns!' => 1, 'template_id!' => '', 'masonry' => '', ], 'selectors' => [ '{{WRAPPER}} .elementor-loop-container' => 'grid-auto-rows: 1fr', // `.elementor-section-wrap` exists only when editing the loop template. '{{WRAPPER}} .e-loop-item > .elementor-section, {{WRAPPER}} .e-loop-item > .elementor-section > .elementor-container, {{WRAPPER}} .e-loop-item > .e-con, {{WRAPPER}} .e-loop-item .elementor-section-wrap > .e-con' => 'height: 100%', ], ] ); $this->add_control( 'alternate_template', [ 'label' => esc_html__( 'Apply an alternate template', 'elementor-pro' ), 'type' => Controls_Manager::SWITCHER, 'label_off' => esc_html__( 'Off', 'elementor-pro' ), 'label_on' => esc_html__( 'On', 'elementor-pro' ), 'condition' => [ 'posts_per_page!' => 1, 'template_id!' => '', ], 'render_type' => 'template', 'frontend_available' => true, 'separator' => 'before', ] ); $repeater = new Repeater(); $repeater->add_control( 'template_id', [ 'label' => esc_html__( 'Choose a template', 'elementor-pro' ), 'type' => Template_Query::CONTROL_ID, 'label_block' => true, 'autocomplete' => [ 'object' => QueryControlModule::QUERY_OBJECT_LIBRARY_TEMPLATE, 'query' => [ 'post_status' => Document::STATUS_PUBLISH, 'meta_query' => [ [ 'key' => Document::TYPE_META_KEY, 'value' => LoopDocument::get_type(), 'compare' => 'IN', ], ], ], ], 'actions' => [ 'new' => [ 'visible' => true, 'document_config' => [ 'type' => LoopDocument::get_type(), ], 'after_action' => 'redirect', ], 'edit' => [ 'visible' => true, 'after_action' => 'redirect', ], ], 'frontend_available' => true, ] ); $repeater->add_control( 'repeat_template', [ 'label' => esc_html__( 'Position in grid', 'elementor-pro' ), 'type' => Controls_Manager::NUMBER, 'condition' => [ 'template_id!' => '', ], ] ); $repeater->add_control( 'repeat_template_note', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Note: Repeat the alternate template once every chosen number of items.', 'elementor-pro' ), 'content_classes' => 'elementor-descriptor', 'condition' => [ 'template_id!' => '', ], ] ); $repeater->add_control( 'show_once', [ 'label' => esc_html__( 'Apply Once', 'elementor-pro' ), 'type' => Controls_Manager::SWITCHER, 'default' => 'yes', 'label_off' => esc_html__( 'No', 'elementor-pro' ), 'label_on' => esc_html__( 'Yes', 'elementor-pro' ), 'condition' => [ 'template_id!' => '', ], 'render_type' => 'template', ] ); $repeater->add_responsive_control( 'column_span', [ 'label' => esc_html__( 'Column Span', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'default' => '1', 'options' => [ '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9', '10' => '10', '11' => '11', '12' => '12', ], 'condition' => [ 'template_id!' => '', ], 'selectors' => [ '{{WRAPPER}} {{CURRENT_ITEM}}' => 'grid-column: span min( {{VALUE}}, var(--grid-columns) );', ], ] ); $repeater->add_control( 'column_span_note', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Note: Item will span across a number of columns.', 'elementor-pro' ), 'content_classes' => 'elementor-descriptor', 'condition' => [ 'template_id!' => '', ], ] ); $repeater->add_control( 'column_span_masonry_note', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Note: The Masonry option combined with Column Span might cause unexpected results and break the layout.', 'elementor-pro' ), 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning', 'condition' => [ 'column_span!' => '1', ], ] ); $repeater->add_control( 'static_position', [ 'label' => esc_html__( 'Static item position', 'elementor-pro' ), 'type' => Controls_Manager::SWITCHER, 'label_off' => esc_html__( 'Off', 'elementor-pro' ), 'label_on' => esc_html__( 'On', 'elementor-pro' ), 'condition' => [ 'template_id!' => '', ], 'render_type' => 'template', ] ); $repeater->add_control( 'static_position_note', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Note: Static Items remain in place when new items are added to grid. Other items appear according to query settings.', 'elementor-pro' ), 'content_classes' => 'elementor-descriptor', 'condition' => [ 'static_position!' => '', 'template_id!' => '', ], ] ); $this->add_control( 'alternate_templates', [ 'label' => esc_html__( 'Templates', 'elementor-pro' ), 'type' => Controls_Manager::REPEATER, 'fields' => $repeater->get_controls(), 'title_field' => 'Alternate Template', 'condition' => [ 'alternate_template' => 'yes', 'posts_per_page!' => 1, 'template_id!' => '', ], 'default' => [ [ 'template_id' => null, ], ], ] ); // Location for the Edit handle. $this->add_control( 'edit_handle_selector', [ 'label' => esc_html__( 'Edit Handle Selector', 'elementor-pro' ), 'type' => Controls_Manager::HIDDEN, 'default' => '[data-elementor-type="loop-item"]', 'render_type' => 'none', 'frontend_available' => true, ] ); $this->end_injection(); } 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__( 'Gap between columns', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 100, ], ], 'selectors' => [ '{{WRAPPER}}' => '--grid-column-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'row_gap', [ 'label' => esc_html__( 'Gap between rows', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 100, ], ], 'frontend_available' => true, 'selectors' => [ '{{WRAPPER}}' => '--grid-row-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_section(); } } files/css/loop.php 0000666 00000002200 15165766212 0010131 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Files\Css; use ElementorPro\Modules\ThemeBuilder\Files\CSS\Template; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Loop extends Template { use Loop_Css_Trait; /** * Elementor Loop CSS file prefix. */ const FILE_PREFIX = 'loop-'; /** * Get CSS file name. * * Retrieve the CSS file name. * * @access public * * @return string CSS file name. */ public function get_name() { return 'loop'; } /** * 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 static::FILE_PREFIX . $this->post_id; } /** * Loop CSS file constructor. * * Initializing the CSS file of the loop widget. Set the post ID and initiate the stylesheet. * * @since 1.2.0 * @access public * * @param int $post_id Post ID. */ public function __construct( $loop_template_id ) { $this->post_id = $loop_template_id; parent::__construct( $loop_template_id ); } } files/css/loop-css-trait.php 0000666 00000010035 15165766212 0012045 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Files\Css; use Elementor\Core\Files\CSS\Base; use Elementor\Icons_Manager; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } trait Loop_Css_Trait { /** * Printed With CSS. * * Holds the list of printed files when `$with_css` is true. * * @access protected * * @var array */ private static $printed_with_css = []; /** * Use external file. * * Whether to use external CSS file of not. Overwrites a parent method. In the Editor, internal embedding needs * to be disabled, because it causes the Loop Document (Template) CSS to be printed inline before each loop item. * * @access protected * * @return bool True if using an external file is needed, false if not. */ protected function use_external_file() { if ( Plugin::elementor()->editor->is_edit_mode() ) { return true; } return 'internal' !== get_option( 'elementor_css_print_method' ); } /** * @param array $fonts * @return void */ private function enqueue_fonts( array $fonts ) { foreach ( $fonts as $font ) { Plugin::elementor()->frontend->enqueue_font( $font ); } } /** * @param $icon_fonts * @return void */ private function enqueue_icon_fonts( $icon_fonts ) { $icons_types = Icons_Manager::get_icon_manager_tabs(); foreach ( $icon_fonts as $icon_font ) { if ( ! isset( $icons_types[ $icon_font ] ) ) { continue; } Plugin::elementor()->frontend->enqueue_font( $icon_font ); } } private function enqueue_font_links() { $meta = $this->get_meta(); if ( Base::CSS_STATUS_EMPTY === $meta['status'] ) { return; } // First time after clear cache etc. if ( '' === $meta['status'] || $this->is_update_required() ) { $this->update(); $meta = $this->get_meta(); } // Handle fonts. if ( ! empty( $meta['fonts'] ) ) { $this->enqueue_fonts( $meta['fonts'] ); } if ( ! empty( $meta['icons'] ) ) { $this->enqueue_icon_fonts( $meta['icons'] ); } } /** * @param array $early_access_google_fonts * @return void */ private function print_early_access_google_font_link_tags( array $early_access_google_fonts ) { $early_access_google_fonts_urls = Plugin::elementor()->frontend->get_early_access_google_font_urls( $early_access_google_fonts ); foreach ( $early_access_google_fonts_urls as $font_url ) { echo '<link rel="stylesheet" type="text/css" href="' . esc_attr( $font_url ) . '">'; } } private function print_fonts_links() { $google_fonts = Plugin::elementor()->frontend->get_list_of_google_fonts_by_type(); if ( ! empty( $google_fonts['google'] ) ) { $stable_google_fonts_url = Plugin::elementor()->frontend->get_stable_google_fonts_url( $google_fonts['google'] ); echo '<link rel="stylesheet" id="fonts-' . esc_attr( $this->get_file_handle_id() ) . '" href="' . esc_attr( $stable_google_fonts_url ) . '" />'; } if ( ! empty( $google_fonts['early'] ) ) { $this->print_early_access_google_font_link_tags( $google_fonts['early'] ); } } public function enqueue_and_print_font_links() { $this->enqueue_font_links(); $this->print_fonts_links(); } public function print_all_css( int $post_id ) { // Avoid re-print CSS if ( isset( self::$printed_with_css[ $this->get_file_handle_id() ] ) ) { return; } $template_custom_css = $post_id > 0 ? $this->get_custom_css( $post_id ) : ''; echo '<style id="' . $this->get_file_handle_id() . '">' . esc_html( $template_custom_css ) . $this->get_content() . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped if ( Plugin::elementor()->editor->is_edit_mode() && method_exists( Plugin::elementor()->frontend, 'get_list_of_google_fonts_by_type' ) ) { $this->enqueue_and_print_font_links(); } // Avoid re-print CSS self::$printed_with_css[ $this->get_file_handle_id() ] = true; } private function get_custom_css( $post_id ) { $loop_doc = Plugin::elementor()->documents->get( $post_id ); return $loop_doc->get_settings( 'custom_css' ); } public function print_css() { $this->print_all_css( 0 ); } } files/css/loop-preview.php 0000666 00000000700 15165766212 0011613 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Files\Css; use Elementor\Core\Files\CSS\Post_Preview; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Loop_Preview extends Post_Preview { use Loop_Css_Trait; /** * Get CSS file name. * * Retrieve the CSS file name. * * @access public * * @return string CSS file name. */ public function get_name() { return 'loop'; } } files/css/loop-dynamic-css.php 0000666 00000001006 15165766212 0012344 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Files\Css; use Elementor\Core\DynamicTags\Dynamic_CSS; use Elementor\Core\Files\CSS\Post; class Loop_Dynamic_CSS extends Dynamic_CSS { private $post_id_for_data; public function __construct( $post_id, $post_id_for_data ) { $this->post_id_for_data = $post_id_for_data; $post_css_file = Post::create( $post_id_for_data ); parent::__construct( $post_id, $post_css_file ); } public function get_post_id_for_data() { return $this->post_id_for_data; } } skins/skin-loop-base.php 0000666 00000012534 15165766212 0011253 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Skins; use ElementorPro\Modules\LoopBuilder\Documents\Loop; use ElementorPro\Modules\LoopBuilder\Documents\Loop as LoopDocument; use ElementorPro\Modules\LoopBuilder\Module; use ElementorPro\Modules\LoopBuilder\Widgets\Base as Loop_Widget_Base; use ElementorPro\Modules\Posts\Skins\Skin_Base; use ElementorPro\Modules\QueryControl\Controls\Group_Control_Related; use ElementorPro\Plugin; use ElementorPro\Modules\LoopBuilder\Files\Css\Loop_Dynamic_CSS; use ElementorPro\Modules\LoopBuilder\Traits\Alternate_Templates_Trait; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * Loop Base * * Base Skin for Loop widgets. * * @since 3.8.0 */ class Skin_Loop_Base extends Skin_Base { use Alternate_Templates_Trait; public function get_id() { return MODULE::LOOP_BASE_SKIN_ID; } public function get_title() { return esc_html__( 'Loop Base', 'elementor-pro' ); } /** * Register Query Controls * * Registers the controls for the query used by the Loop. * * @since 3.8.0 */ public function register_query_controls( Loop_Widget_Base $widget ) { $this->parent = $widget; $this->add_group_control( Group_Control_Related::get_type(), [ 'name' => Module::QUERY_ID, 'presets' => [ 'full' ], 'exclude' => [ 'posts_per_page', // Use the one from Layout section ], ] ); } private function maybe_add_load_more_wrapper_class() { $settings = $this->parent->get_settings_for_display(); /** @var Loop_Widget_Base $widget */ $widget = $this->parent; if ( isset( $settings['pagination_type'] ) && 'load_more_on_click' === $settings['pagination_type'] ) { // If Pagination is enabled with the Load More On Click option, a class is needed for targeting. // The 'wrapper' element tag is used by the Button Widget Trait. $widget->add_render_attribute( 'wrapper', 'class', 'e-loop__load-more' ); } } public function query_posts() { return $this->query_posts_for_alternate_templates(); } public function render() { $template_id = $this->parent->get_settings_for_display( 'template_id' ); $is_edit_mode = Plugin::elementor()->editor->is_edit_mode(); /** @var Loop_Widget_Base $widget */ $widget = $this->parent; $current_document = Plugin::elementor()->documents->get_current(); if ( $template_id ) { $this->alternate_template_before_skin_render(); $this->maybe_add_load_more_wrapper_class(); $widget->before_skin_render(); parent::render(); $widget->after_skin_render(); $this->alternate_template_after_skin_render(); } elseif ( $is_edit_mode ) { $this->render_empty_view(); } if ( $current_document ) { Plugin::elementor()->documents->switch_to_document( $current_document ); } } protected function get_loop_header_widget_classes() { /** @var Loop_Widget_Base $widget */ $widget = $this->parent; $classes = $widget->get_loop_header_widget_classes(); $classes[] = 'elementor-loop-container'; return $classes; } protected function _register_controls_actions() { add_action( 'elementor/element/' . $this->parent->get_name() . '/section_query/after_section_start', [ $this, 'register_query_controls' ] ); } /** * Render Post * * Uses the chosen custom template to render Loop posts. * * @since 3.8.0 */ protected function render_post() { if ( $this->has_alternate_templates() ) { $this->render_post_if_widget_has_alternate_templates(); } else { $this->render_post_content( $this->parent->get_settings_for_display( 'template_id' ) ); } } private function render_post_content( $template_id ) { $post_id = get_the_ID(); /** @var LoopDocument $document */ $document = Plugin::elementor()->documents->get( $template_id ); if ( ! $document ) { return; } $this->print_dynamic_css( $post_id, $template_id ); $document->print_content(); } protected function print_dynamic_css( $post_id, $post_id_for_data ) { $document = Plugin::elementor()->documents->get_doc_for_frontend( $post_id_for_data ); if ( ! $document ) { return; } Plugin::elementor()->documents->switch_to_document( $document ); $css_file = Loop_Dynamic_CSS::create( $post_id, $post_id_for_data ); $post_css = $css_file->get_content(); if ( empty( $post_css ) ) { return; } $css = ''; $css = str_replace( '.elementor-' . $post_id, '.e-loop-item-' . $post_id, $post_css ); $css = sprintf( '<style id="%s">%s</style>', 'loop-dynamic-' . $post_id_for_data, $css ); echo $css; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped Plugin::elementor()->documents->restore_document(); } protected function render_loop_header() { /** @var Loop_Widget_Base $widget */ $widget = $this->parent; $config = $widget->get_config(); if ( $config['add_parent_render_header'] ) { parent::render_loop_header(); } $widget->render_loop_header(); } protected function render_loop_footer() { /** @var Loop_Widget_Base $widget */ $widget = $this->parent; $config = $widget->get_config(); if ( $config['add_parent_render_footer'] ) { parent::render_loop_footer(); } $widget->render_loop_footer(); } /** * Render Empty View * * Renders the Loop widget's view if there is no default template (empty view). * * @since 3.8.0 */ protected function render_empty_view() { ?> <div class="e-loop-empty-view__wrapper"><!-- Will be filled with JS --></div> <?php } } skins/skin-loop-post.php 0000666 00000000566 15165766212 0011330 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Skins; use ElementorPro\Modules\LoopBuilder\Module; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Skin_Loop_Post extends Skin_Loop_Base { public function get_id() { return Module::LOOP_POST_SKIN_ID; } public function get_title() { return esc_html__( 'Posts', 'elementor-pro' ); } } views/cta-template.php 0000666 00000010062 15165766212 0011010 0 ustar 00 <?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } ?> <script type="text/template" id="tmpl-loop-grid-cta"> <style> /* Hide empty view, until Shadow DOM stying has loaded. */ .e-loop-empty-view__box { opacity: 0; } </style> <div class="e-loop-empty-view__box e-loop-empty-view__box--active"> <div class="e-loop-empty-view__box-inner"> <img src="<?php echo ELEMENTOR_ASSETS_URL . 'images/information.svg'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" loading="lazy" /> <div class="e-loop-empty-view__box-title"> <?php echo esc_html__( 'Loop Grid starts with a template.', 'elementor-pro' ); ?> </div> <div class="e-loop-empty-view__box-description"> <?php echo esc_html__( 'Either choose an existing template or create a new one and use it as the main item for your loop.', 'elementor-pro' ); ?> </div> <a href="#" class="e-loop-empty-view__box-cta"> <?php echo esc_html__( 'Create a template', 'elementor-pro' ); ?> </a> </div> </div> <div class="e-loop-empty-view__box"> <div class="e-loop-empty-view__box-inner"></div> </div> <div class="e-loop-empty-view__box"> <div class="e-loop-empty-view__box-inner"></div> </div> </script> <script type="text/template" id="tmpl-loop-carousel-cta"> <style> /* Hide empty view, until Shadow DOM stying has loaded. */ .e-loop-empty-view__box, .elementor-swiper-button, .swiper-pagination { opacity: 0; } </style> <div class="e-loop-empty-view__box e-loop-empty-view__box--active"> <div class="e-loop-empty-view__box-inner"> <img src="<?php echo ELEMENTOR_ASSETS_URL . 'images/information.svg'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" loading="lazy" /> <div class="e-loop-empty-view__box-title"> <?php echo esc_html__( 'Loop Carousel starts with a template.', 'elementor-pro' ); ?> </div> <div class="e-loop-empty-view__box-description"> <?php echo esc_html__( 'Either choose an existing template or create a new one and use it as the main item for your loop.', 'elementor-pro' ); ?> </div> <a href="#" class="e-loop-empty-view__box-cta"> <?php echo esc_html__( 'Create a template', 'elementor-pro' ); ?> </a> </div> </div> <div class="e-loop-empty-view__box"> <div class="e-loop-empty-view__box-inner"></div> </div> <div class="e-loop-empty-view__box"> <div class="e-loop-empty-view__box-inner"></div> </div> <div class="elementor-swiper-button elementor-swiper-button-prev" tabindex="0" role="button"> <svg aria-hidden="true" class="e-font-icon-svg e-eicon-chevron-left" viewBox="0 0 1000 1000" width="1" height="1" xmlns="http://www.w3.org/2000/svg"><path d="M646 125C629 125 613 133 604 142L308 442C296 454 292 471 292 487 292 504 296 521 308 533L604 854C617 867 629 875 646 875 663 875 679 871 692 858 704 846 713 829 713 812 713 796 708 779 692 767L438 487 692 225C700 217 708 204 708 187 708 171 704 154 692 142 675 129 663 125 646 125Z"></path></svg> <span class="elementor-screen-only">Previous slide</span> </div> <div class="elementor-swiper-button elementor-swiper-button-next" tabindex="0" role="button"> <svg aria-hidden="true" class="e-font-icon-svg e-eicon-chevron-right" viewBox="0 0 1000 1000" width="1" height="1" xmlns="http://www.w3.org/2000/svg"><path d="M696 533C708 521 713 504 713 487 713 471 708 454 696 446L400 146C388 133 375 125 354 125 338 125 325 129 313 142 300 154 292 171 292 187 292 204 296 221 308 233L563 492 304 771C292 783 288 800 288 817 288 833 296 850 308 863 321 871 338 875 354 875 371 875 388 867 400 854L696 533Z"></path></svg> <span class="elementor-screen-only">Next slide</span> </div> <div class="swiper-pagination swiper-pagination-clickable swiper-pagination-bullets"> <span class="swiper-pagination-bullet swiper-pagination-bullet-active" tabindex="0" role="button" aria-label="Go to slide 1"></span> <span class="swiper-pagination-bullet" tabindex="0" role="button" aria-label="Go to slide 2"></span> <span class="swiper-pagination-bullet" tabindex="0" role="button" aria-label="Go to slide 3"></span> </div> </script> module.php 0000666 00000020326 15165766212 0006564 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder; use Elementor\Controls_Manager; use Elementor\Core\Documents_Manager; use Elementor\TemplateLibrary\Source_Local; use ElementorPro\Base\Module_Base; use ElementorPro\Plugin; use Elementor\Core\Base\Document; use ElementorPro\Modules\LoopBuilder\Documents\Loop as LoopDocument; use Elementor\Core\Experiments\Manager; use ElementorPro\Core\Utils; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Module extends Module_Base { /** * Elementor template-library taxonomy slug. */ const TEMPLATE_LIBRARY_TYPE_SLUG = 'loop-item'; const EXPERIMENT_NAME = 'loop'; const LOOP_BASE_SKIN_ID = 'base'; const LOOP_POST_SKIN_ID = 'post'; const QUERY_ID = 'query'; const LOOP_WIDGETS = [ 'loop-grid', 'loop-carousel', ]; public function get_name() { return 'loop-builder'; } protected function get_widgets() { return [ 'Loop_Grid', 'Loop_Carousel' ]; } public function __construct() { parent::__construct(); add_action( 'elementor/documents/register', function ( $documents_manager ) { $this->register_documents( $documents_manager ); }, 11 /* After WC documents */ ); add_filter( 'elementor/finder/categories', [ $this, 'add_finder_items' ] ); add_action( 'elementor/template-library/create_new_dialog_fields', [ $this, 'add_posts_type_to_template_popup' ], 10 ); add_filter( 'elementor/frontend/builder_content_data', [ $this, 'filter_content_data' ], 10, 2 ); add_action( 'manage_' . Source_Local::CPT . '_posts_columns', function ( $columns ) { return $this->manage_posts_columns( $columns ); } ); add_action('elementor/editor/init', function () { Plugin::elementor()->common->add_template( __DIR__ . '/views/cta-template.php' ); }); if ( $this->is_loop_theme_builder() ) { add_filter( 'template_include', [ $this, 'filter_template_to_canvas_view' ], 999 ); add_filter( 'body_class', [ $this, 'filter_body_class' ] ); } // Prevent enqueue default dynamic CSS for loop item templates add_filter( 'elementor/css-file/dynamic/should_enqueue', function ( $should_enqueue, $post_id ) { if ( static::TEMPLATE_LIBRARY_TYPE_SLUG === get_post_meta( $post_id, Document::TYPE_META_KEY, true ) ) { $should_enqueue = false; } return $should_enqueue; }, 10, 2 ); add_filter( 'elementor/editor/localize_settings', function ( $config ) { $config['admin_url'] = admin_url(); return $config; }); } public function filter_template_to_canvas_view() { return ELEMENTOR_PATH . 'modules/page-templates/templates/canvas.php'; } private function get_preview_loop_item_id() { $post_id = false; // Editor preview. $post_id = Utils::_unstable_get_super_global_value( $_GET, 'elementor-preview' ); if ( ! $post_id ) { $library_type = Utils::_unstable_get_super_global_value( $_GET, 'elementor_library' ); if ( 'elementor-' . self::TEMPLATE_LIBRARY_TYPE_SLUG === $library_type ) { // Frontend Loop Item template preview. $post_id = get_the_ID(); } } return $post_id; } public function filter_body_class( $classes ) { $classes[] = 'e-loop-template-canvas'; $post_id = $this->get_preview_loop_item_id(); if ( $post_id && 'product' === $this->get_source_type_from_post_meta( $post_id ) ) { $classes[] = 'woocommerce'; } return $classes; } /** * Add to the experiments * * @return array */ public static function get_experimental_data() { return [ 'name' => static::EXPERIMENT_NAME, 'title' => esc_html__( 'Loop', 'elementor-pro' ), 'description' => sprintf( esc_html__( 'Create powerful & repeating templates and populate each one with dynamic content like text or images. Great for listings, posts, portfolios and more! %1$sLearn More%2$s', 'elementor-pro' ), '<a href="https://go.elementor.com/wp-dash-loop/" target="_blank">', '</a>' ), 'release_status' => Manager::RELEASE_STATUS_STABLE, 'default' => Manager::STATE_ACTIVE, ]; } /** * Filter content data. * * Determine whether we are in the Editor and are trying to Edit an empty loop template. * * If this is the case, we add some elements to the $data array in order for frontend.php * to not 'return' an empty string and reach the print_elements_with_wrapper() function. * * We then override print_elements_with_wrapper() in the loop document using the variables * we added here. * * @since 3.8.0 * * @param array $data * @param int $post_id * * @return mixed */ public function filter_content_data( $data, $post_id ) { if ( empty( $data ) && LoopDocument::get_type() === get_post_meta( $post_id, Document::TYPE_META_KEY, true ) && wp_doing_ajax() ) { $data['empty_loop_template'] = true; $data['empty_loop_template_id'] = $post_id; } return $data; } public static function is_active(): bool { return Plugin::elementor()->experiments->is_feature_active( static::EXPERIMENT_NAME ); } public function add_finder_items( array $categories ) { $categories['create']['items']['loop-template'] = [ 'title' => esc_html__( 'Add New Loop Template', 'elementor-pro' ), 'icon' => 'plus-circle-o', 'url' => $this->get_admin_templates_url() . '#add_new', 'keywords' => [ 'template', 'theme', 'new', 'create', 'loop', 'dynamic', 'listing', 'archive', 'repeater' ], ]; return $categories; } public function add_posts_type_to_template_popup( $form ) { if ( empty( $form ) ) { return; } $form->add_control( '_elementor_source', [ 'type' => Controls_Manager::SELECT, 'label' => esc_html__( 'Choose source type', 'elementor-pro' ), 'options' => [ self::LOOP_POST_SKIN_ID => esc_html__( 'Posts', 'elementor-pro' ) ], 'section' => 'main', 'required' => true, 'conditions' => [ 'template-type' => self::TEMPLATE_LIBRARY_TYPE_SLUG, ], ] ); } public function get_source_type_from_post_meta( $post_id ) { $source_type = get_post_meta( intval( $post_id ), '_elementor_source', true ); return empty( $source_type ) ? 'post' : $source_type; } private function is_editing_existing_loop_item() { //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. $elementor_library = Utils::_unstable_get_super_global_value( $_GET, 'elementor_library' ) ?? ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. $post_id = Utils::_unstable_get_super_global_value( $_GET, 'elementor-preview' ); return ! empty( $elementor_library ) && $this->is_loop_item_document_type_meta_key( $post_id ); } private function is_creating_new_loop_item() { //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. $post_type = Utils::_unstable_get_super_global_value( $_GET, 'post_type' ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. $post_id = Utils::_unstable_get_super_global_value( $_GET, 'p' ); return 'elementor_library' === $post_type && $this->is_loop_item_document_type_meta_key( $post_id ); } private function is_loop_item_document_type_meta_key( $post_id ) { return static::TEMPLATE_LIBRARY_TYPE_SLUG === get_post_meta( $post_id, Document::TYPE_META_KEY, true ); } private function is_loop_theme_builder() { return $this->is_editing_existing_loop_item() || $this->is_creating_new_loop_item(); } /** * @param Documents_Manager $documents_manager */ private function register_documents( $documents_manager ) { $documents_manager->register_document_type( Documents\Loop::get_type(), Documents\Loop::get_class_full_name() ); } private function get_admin_templates_url( $relative = false ) { $base_url = Source_Local::ADMIN_MENU_SLUG; if ( ! $relative ) { $base_url = admin_url( $base_url ); } return add_query_arg( [ 'tabs_group' => 'theme', 'elementor_library_type' => self::TEMPLATE_LIBRARY_TYPE_SLUG, ], $base_url ); } private function manage_posts_columns( $columns ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required. $taxonomy_type_slug = Utils::_unstable_get_super_global_value( $_REQUEST, Source_Local::TAXONOMY_TYPE_SLUG ); if ( self::TEMPLATE_LIBRARY_TYPE_SLUG === $taxonomy_type_slug ) { unset( $columns['instances'] ); } return $columns; } } traits/alternate-templates-trait.php 0000666 00000045403 15165766212 0013704 0 ustar 00 <?php namespace ElementorPro\Modules\LoopBuilder\Traits; use ElementorPro\Plugin; use ElementorPro\Modules\QueryControl\Module as QueryControlModule; trait Alternate_Templates_Trait { private $current_post_index = 0; private $alternate_templates = []; private $rendered_alternate_templates = []; private $has_alternate_templates = false; private $has_static_alternate_templates = false; private $query_contains_static_alternate_templates = false; private $static_alternate_template_query_data = []; /** * Query Posts For Alternate Templates * * Construct `static_alternate_template_query_data` and modify the widget query if the user has * added any valid 'static' alternate templates. If we do need to display any static templates - based on the existing * query - we do a new custom query that uses the `static_alternate_template_query_data['page_settings']` to * handle pagination by specifying an `offset` in the query, and we increase the resulting `$query->found_posts` * and `$query->max_num_pages` so the pagination, and any standard WP Query elements, display as expected. * * @return false|\WP_Query */ public function query_posts_for_alternate_templates() { // If there are no static alternate templates, no need to modify the query. if ( ! $this->has_static_alternate_templates() ) { return false; } $widget = $this->parent; /** @var QueryControlModule $elementor_query */ $elementor_query = QueryControlModule::instance(); /** * Construct the `static_alternate_template_query_data` used for the new query and when rendering each post. */ $query = $elementor_query->get_query( $widget, $widget->get_query_name(), [ 'posts_per_page' => 1, 'paged' => 1, ] ); $this->init_static_alternate_template_query_data( $query->found_posts ); if ( ! $this->query_contains_static_alternate_templates() ) { return false; } $adjusted_found_posts = $this->get_static_alternate_template_adjusted_found_posts(); $adjusted_max_num_pages = $this->get_static_alternate_template_adjusted_max_num_pages(); /** * New query using `offset` in place of `paged`. */ $query = $elementor_query->get_query( $widget, $widget->get_query_name(), [ 'posts_per_page' => $widget->get_posts_per_page_value(), 'offset' => $this->get_static_alternate_template_query_offset(), ] ); /** * Increase size of the query using the adjusted values calculated after * constructing `static_alternate_template_query_data`. */ $query->found_posts = $adjusted_found_posts; $query->max_num_pages = $adjusted_max_num_pages; return $query; } /** * Init Alternate Templates Settings * * Improve performance by storing the `alternate_templates` repeater settings, so we don't use * `get_settings_for_display()` each time we check if a post should use an alternate template. * * At the same time we store `$has_alternate_templates` and `$has_static_alternate_templates` used by their * accompanying helper functions, for efficiency, and so we don't need to loop through the `alternate_templates` * each time we check. * * We also re-arrange the `alternate_templates` array for two reasons: * (1) The last template added by the user should take preference, so we reverse the array so that when we loop * through the repeater settings array to check if a post should use an alternate template, we find the last * added template first. * (2) 'Static' alternate templates should take preference over 'non-static' templates, so we group all static * templates before first, so when we loop through the array to check if a post should use an alternate template * we find the 'static' template first. * * @return void */ private function init_alternate_template_settings(): void { $settings = $this->parent->get_settings_for_display(); if ( empty( $settings['alternate_templates'] ) || empty( $settings['template_id'] ) ) { return; } // Note that the user has added alternate templates. $this->has_alternate_templates = true; // Store the alternate templates. $alternate_templates = $settings['alternate_templates']; // Reverse the alternate template settings so the last template added by the user takes preference over // the previous templates. $alternate_templates = array_reverse( $alternate_templates ); // Rearrange the alternate template settings to group all static templates before the standard templates. $static_alternate_templates = []; $standard_alternate_templates = []; foreach ( $alternate_templates as $alternate_template ) { // Skip the alternate template from any calculations until a repeat template number is specified. if ( empty( $alternate_template['repeat_template'] ) ) { continue; } if ( $this->is_alternate_template_static_position( $alternate_template ) ) { $static_alternate_templates[ $alternate_template['_id'] ] = $alternate_template; // Note that the user has added 'static' alternate templates. $this->has_static_alternate_templates = true; } else { $standard_alternate_templates[ $alternate_template['_id'] ] = $alternate_template; } } $this->alternate_templates = $static_alternate_templates + $standard_alternate_templates; } private function alternate_template_before_skin_render(): void { // Init Alternate Template Settings should occur as early in the load order as possible as it's responsible // for setting flags used in helper functions, that can be called quite early in the widget // e.g. `has_alternate_templates()` & `has_static_alternate_templates()`. $this->init_alternate_template_settings(); if ( ! $this->has_alternate_templates() ) { return; } $this->maybe_add_alternate_template_wrapper_classes(); } private function alternate_template_after_skin_render(): void { if ( ! $this->has_alternate_templates() ) { return; } $this->reset_alternate_template_data(); $this->maybe_remove_alternate_template_wrapper_classes(); } /** * @return void */ private function reset_alternate_template_data() { // Reset all the Alternate Template data at the end of the widget so that it does not affect any widgets // below the current widget. $this->current_post_index = 0; $this->alternate_templates = []; $this->rendered_alternate_templates = []; $this->has_alternate_templates = false; $this->has_static_alternate_templates = false; $this->query_contains_static_alternate_templates = false; $this->static_alternate_template_query_data = []; } /** * @return void */ private function maybe_add_alternate_template_wrapper_classes(): void { add_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_wrapper_classes' ] ); if ( Plugin::elementor()->editor->is_edit_mode() ) { add_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_editor_wrapper_classes' ] ); } } /** * @return void */ private function maybe_remove_alternate_template_wrapper_classes(): void { remove_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_wrapper_classes' ] ); if ( Plugin::elementor()->editor->is_edit_mode() ) { remove_filter( 'elementor/document/wrapper_attributes', [ $this, 'add_alternate_template_editor_wrapper_classes' ] ); } } /** * @param $attributes * @return array */ public function add_alternate_template_wrapper_classes( $attributes ): array { $template = $this->get_template_data_for_current_post(); if ( $this->is_alternate_template( $template ) ) { $attributes['class'] .= ' elementor-repeater-item-' . $template['_id']; } return $attributes; } /** * @param $attributes * @return array */ public function add_alternate_template_editor_wrapper_classes( $attributes ): array { $template = $this->get_template_data_for_current_post(); if ( $this->is_alternate_template( $template ) && $this->is_alternate_template_first_occurrence( $template ) ) { $attributes['class'] .= ' e-loop-alternate-template'; } return $attributes; } /** * @return void */ private function render_post_if_widget_has_alternate_templates(): void { // If any static templates are rendered they will result in this function being called recursively, so we need // to make sure we don't render more posts than the user has chosen in their `posts_per_page` widget control. if ( $this->current_post_index >= $this->parent->get_posts_per_page_value() ) { return; } $template = $this->get_template_data_for_current_post(); $this->render_post_content( $template['template_id'] ); $this->store_rendered_alternate_templates( $template['template_id'] ); $this->current_post_index++; // If the current post has a 'static' alternate template the above will render an extra empty post in the loop. // We need to render this post again (with incremented `current_post_index`) so it is not skipped as a result // of a static template. if ( $this->is_alternate_template_static_position( $template ) ) { $this->render_post_if_widget_has_alternate_templates(); } } /** * @param $template_id * @return void */ private function store_rendered_alternate_templates( $template_id ): void { if ( ! in_array( $template_id, $this->rendered_alternate_templates, true ) ) { $this->rendered_alternate_templates[] = $template_id; } } /** * Has Alternate Templates * * Has the user added any alternate templates to the widget. * * Improve performance by storing `has_alternate_templates` once when the widget is rendered * to avoid using `get_settings_for_display()` each time. * * @return bool */ private function has_alternate_templates(): bool { return $this->has_alternate_templates; } /** * Has Static Alternate Templates * * Has the user added any 'static' alternate templates to the widget. * * Improve performance by storing `has_static_alternate_templates` once when the widget is rendered * to avoid iterating through the repeater settings each time. * * @return bool */ private function has_static_alternate_templates(): bool { return $this->has_static_alternate_templates; } /** * Query Contains Static Alternate Templates * * After constructing the `init_static_alternate_template_query_data` array, we want to make sure - based on * the alternate templates settings - that we definitely have valid 'static' alternate templates to display. * * This flag is used to avoid modifying the query or re-rendering posts if we don't have to. * * @return bool */ private function query_contains_static_alternate_templates(): bool { return $this->query_contains_static_alternate_templates; } /** * Init Static Alternate Template Query Data * * Construct `static_alternate_template_query_data` if the user has added any valid 'static' alternate templates. * Used to modify the widget query and when rendering a post. * * @param $query * @return void */ private function init_static_alternate_template_query_data( $required_posts_count ): void { $this->static_alternate_template_query_data = [ 'templates' => [], 'page_settings' => [], ]; $posts_per_page = $this->parent->get_posts_per_page_value(); $static_alternate_template_count = 0; for ( $current_index = 0; $current_index < $required_posts_count; $current_index++ ) { $template = $this->get_template_data_by_index( $current_index ); $this->set_static_alternate_template_query_data_item( $template, $current_index, $static_alternate_template_count, $posts_per_page ); if ( 'yes' === $template['static_position'] ) { $static_alternate_template_count++; $required_posts_count++; } } // Note if any valid 'static' alternate templates need to be displayed, after the above calculations. $this->query_contains_static_alternate_templates = 0 < $static_alternate_template_count; } /** * Set Static Alternate Template Query Data Item * * Store which template to use when each post is rendered. * * `init_static_alternate_template_query_data()` stores each template's data in the * `static_alternate_template_query_data['templates']` and, at the same time, stores * `static_alternate_template_query_data['page_settings']` used to adjust the query. * * @param $template * @param $current_post_index * @param $static_alternate_template_count * @param $posts_per_page * @return void */ private function set_static_alternate_template_query_data_item( $template, $current_post_index, $static_alternate_template_count, $posts_per_page ): void { // Store template - used when we render the post. $this->static_alternate_template_query_data['templates'][ $current_post_index ] = $template; // Store `page_settings`. // `query_offset` is used when we query posts and `start_index` when we render posts. $current_page = ceil( $current_post_index / $posts_per_page ) + 1; $this->static_alternate_template_query_data['page_settings'][ $current_page ] = [ 'query_offset' => $current_post_index - $static_alternate_template_count, 'start_index' => $current_post_index, ]; } /** * Get Static Alternate Template Current Page Settings * * Used to modify the widget query and when rendering a post. * * @return array|bool */ private function get_static_alternate_template_current_page_settings() { $current_page = $this->parent->get_current_page(); if ( empty( $this->static_alternate_template_query_data['page_settings'][ $current_page ] ) ) { return false; } return $this->static_alternate_template_query_data['page_settings'][ $current_page ]; } /** * Get Static Alternate Template Query Offset * * Used to modify the widget query. * * @return array|bool */ private function get_static_alternate_template_query_offset() { $current_page_settings = $this->get_static_alternate_template_current_page_settings(); if ( ! $current_page_settings ) { return false; } return $current_page_settings['query_offset']; } /** * Get Static Alternate Template Start Index * * Used when calling `render_post_if_widget_has_alternate_templates()`. * * @return array|bool */ private function get_static_alternate_template_start_index() { $current_page_settings = $this->get_static_alternate_template_current_page_settings(); if ( ! $current_page_settings ) { return false; } return $current_page_settings['start_index']; } /** * Get Static Alternate Template Adjusted Found Posts * * Used to modify the widget query. * * @return int */ private function get_static_alternate_template_adjusted_found_posts(): int { if ( empty( $this->static_alternate_template_query_data['templates'] ) ) { return 0; } return count( $this->static_alternate_template_query_data['templates'] ); } /** * Get Static Alternate Template Adjusted Max Num Pages * * Used to modify the widget query. * * @return float */ private function get_static_alternate_template_adjusted_max_num_pages(): float { return ceil( $this->get_static_alternate_template_adjusted_found_posts() / $this->parent->get_posts_per_page_value() ); } /** * Get Data For Static Alternate Template * * Used when rendering the current post. * * @param $index * @return array */ private function get_data_for_static_alternate_template( $index ): array { if ( ! empty( $this->static_alternate_template_query_data['templates'][ $index ] ) ) { return $this->static_alternate_template_query_data['templates'][ $index ]; } return $this->get_default_template(); } /** * @return array */ private function get_template_data_for_current_post() { $current_post_index = $this->get_current_post_index(); if ( $this->query_contains_static_alternate_templates() ) { return $this->get_data_for_static_alternate_template( $current_post_index ); } return $this->get_template_data_by_index( $current_post_index ); } /** * @return int */ private function get_current_post_index() { if ( $this->query_contains_static_alternate_templates() ) { return $this->get_static_alternate_template_start_index() + $this->current_post_index; } return $this->parent->get_query()->current_post; } /** * @param $index * @return array */ private function get_template_data_by_index( $index ): array { if ( ! $this->has_alternate_templates() ) { return $this->get_default_template(); } foreach ( $this->alternate_templates as $alternate_template ) { $found_alternate_template = $this->is_alternate_template_show_once( $alternate_template ) ? $this->should_show_alternate_template_once( $alternate_template, $index ) : $this->should_show_repeating_alternate_template( $alternate_template, $index ); if ( $found_alternate_template ) { return [ 'template_id' => $alternate_template['template_id'], 'alternate_template' => 'yes', 'static_position' => $alternate_template['static_position'] ? 'yes' : 'no', '_id' => $alternate_template['_id'], ]; } } return $this->get_default_template(); } /** * @return array */ private function get_default_template(): array { return [ 'template_id' => $this->parent->get_settings_for_display( 'template_id' ), 'alternate_template' => 'no', 'static_position' => 'no', '_id' => '-', ]; } /** * @param $alternate_template * @return bool */ private function is_alternate_template( $alternate_template ): bool { return isset( $alternate_template['alternate_template'] ) && 'yes' === $alternate_template['alternate_template']; } /** * @param $alternate_template * @return bool */ private function is_alternate_template_static_position( $alternate_template ): bool { return isset( $alternate_template['static_position'] ) && 'yes' === $alternate_template['static_position']; } /** * @param $alternate_template * @return bool */ private function is_alternate_template_show_once( $alternate_template ): bool { return isset( $alternate_template['show_once'] ) && 'yes' === $alternate_template['show_once']; } /** * @param $number_to_check * @param $multiple_to_check * @return bool */ private function is_repeating_alternate_template_multiple_of( $number_to_check, $multiple_to_check ): bool { return 0 === $multiple_to_check % $number_to_check; } /** * @param $template * @return bool */ private function is_alternate_template_first_occurrence( $template ): bool { return ! in_array( $template['template_id'], $this->rendered_alternate_templates, true ); } /** * @param $alternate_template * @param $current_item_index * @return bool */ private function should_show_alternate_template_once( $alternate_template, $current_item_index ): bool { return $alternate_template['repeat_template'] === $current_item_index + 1; } /** * @param $alternate_template * @param $current_item_index * @return bool */ private function should_show_repeating_alternate_template( $alternate_template, $current_item_index ): bool { return $this->is_repeating_alternate_template_multiple_of( $alternate_template['repeat_template'], $current_item_index + 1 ); } }
| ver. 1.4 |
Github
|
.
| PHP 5.4.45 | Generation time: 0 |
proxy
|
phpinfo
|
Settings