10up / ElasticPress

A fast and flexible search and query engine for WordPress.
https://elasticpress.io
GNU General Public License v2.0
1.25k stars 311 forks source link

Ajax Load More functionality integration issue #2023

Closed danielecelsa closed 3 years ago

danielecelsa commented 3 years ago

Hello,

if in the Search Results page, instead of using default Pagination, I use the Ajax Load More functionality that lots of plugins come with, the results in the first page are correctly handled by ElasticPress, BUT the additional results (after Load More call) are handled by default WP search engine.

It seem that default WP query is used instead of ES query when Ajax is called. This does not happen with default pagination (guess because Ajax is not called).

Just as an example, the plugin I am actually using for the Ajax Load more functionality is using this code to handle pagination:

`public function get_document_data(){

global $wp_query;

$id = $this->widget_id;

$post_id = $this->post_id;
$theme_id = $this->theme_id;
$old_query = $wp_query->query_vars;

$this->query['paged'] = $this->current_page; // we need current(next) page to be loaded
$this->query['post_status'] = 'publish';

$wp_query = new \WP_Query($this->query);
wp_reset_postdata();//this fixes some issues with some get_the_ID users.
if (is_archive()){
  $post_id = $theme_id;
}

    $document = \Elementor\Plugin::$instance->documents->get_doc_for_frontend( $post_id );
    $theme_document = \Elementor\Plugin::$instance->documents->get_doc_for_frontend( $theme_id );

$data[] = $this->get_element_data($id,$theme_document->get_elements_data());

    // Change the current post, so widgets can use documents->get_current.
    \Elementor\Plugin::$instance->documents->switch_to_document( $document );

ob_start();
    $document->print_elements_with_wrapper( $data );
$content = ob_get_clean();
echo $this->clean_response($content,$id);

\Elementor\Plugin::$instance->documents->restore_document();
$wp_query->query_vars = $query_vars;

die;

}`

Any idea how to make this work?

Thanks, Daniele

brandwaffle commented 3 years ago

@danielecelsa at a high level, ElasticPress works by "taking over" certain queries. This includes the "s" parameter in a query, which indicates a search. Depending on how the Ajax plugin you're using works (they're all different), the author might be creating a special non-search query, or they could be even issuing a direct SQL query, which means ElasticPress can't "see" the query to take it over.

In order to make any query use ElasticPress, you just need to add a parameter of ep_integrate and set it to true. This can be done with a filter like pre_get_posts if you can't directly add it to the WP_Query call (likely the case with a custom plugin).

brandwaffle commented 3 years ago

@danielecelsa I hear you're working on this with our support team as well, in which case I'll go ahead and close the ticket in 3 days unless you want me to keep this open. Thanks!

danielecelsa commented 3 years ago

Hello @brandwaffle , please do not close this ticket yet. I think it is better to talk about this here in public, maybe someone could find it helpful. Also I will not go on with the conversation with support team, so let's continue here if it is possible :)

About the issue, I did what you suggested and I got some results, I guess the issue now is about 'pagination'.

I tried this code as per your suggestion: `

$this->query['paged']= 1; // did not work with $this->query['paged'] = $this->current_page;
$this->query['post_status'] = 'publish';
$this->query['ep_integrate'] = 'true';  // EP support suggestion

$wp_query = new \WP_Query($this->query);

`

and when I click on Load More button to load the page 2, in this new page I see the same results of the first page. Means EP query is called, but it shows its first results, as if it was called for the first time!

So could you suggest how can I do to make the query show its results 'related' to the page shown?

Thanks in advance! Daniele

danielecelsa commented 3 years ago

Hello @brandwaffle,

did you by chance have the opportunity to deepen my request?

Thanks a lot, Daniele

danielecelsa commented 3 years ago

@brandwaffle I have an update

Actually pagination works, and I got which is the real issue: The problem is ONLY in the LAST page.

The Ajax Load More plugin handle well the query through ElasticPress and also pagination, it only fails in the last page because it shows the last results of ElasticPress query PLUS some results of WP query, if the number of post per page is more than the last results of EP query.

Little example to easily explain: EP query gives 11 results Number of post per page = 5 So I search and I see the first 5 results of EP, then I click Load More and I see other 5 results of EP, then I click again Load More and i should see the 11th result, only that. BUT... I see that result PLUS 4 more, and these 4 come by the default WP query I guess.

This does NOT happen with the default WP pagination. I mean, if instead of 'Load More' I use 'Numbers' or 'Next/Previous' in pagination settings of Posts widget, in the last page I will see only the 11th result.

So I would like to ask you:

do you know what part of code/parameter of EP deals with this feature of the last results in pagination? I guess somewhere it must be specified.

And I guess that the Load More plugin in someway overwrites this parameter (because without plugin, in normal pagination, this does not happen), but I really don't know how to identify it.

Thank you in advance, Daniele

felipeelia commented 3 years ago

Hi @danielecelsa ,

ElasticPress should not be changing anything on the last page. I have a suggestion to help you to debug what is going on.

  1. Enable WP_DEBUG and WP_DEBUG_LOG constants in your wp-config.php file. Set WP_DEBUG_DISPLAY as false as well.
  2. Inside your get_document_data() function, right before you call $wp_query = new \WP_Query($this->query);, insert error_log( var_export( $this->query, true ) );
  3. Try to load the pages

It will store the parameters in a debug.log file inside your wp-content folder. Hopefully, that can give us an idea of what WordPress (and ElasticPress) will be looking for. If you'd like, you can also place a error_log( var_export( $wp_query->posts, true ) ); there too, to check what is coming back.

Thanks

danielecelsa commented 3 years ago

Hello @felipeelia,

first, really thank you very much for you kind help!

Then, I tried your suggestion and here my results:

In this last screenshot, I highlighted the row 442 where the debug.log file shows there is an error. I do not know if it is related to the issue (I just asked to the owner of the plugin).

To help you understanding:

I really hope you can find it helpful in the investigation of the issue, I tried to find the problem but unfortunately I could not end up with any solution..

Thanks in advance for your kind help, Daniele

plugin_function wp_config

debug_log_github.txt

felipeelia commented 3 years ago

Hi @danielecelsa ,

The error_log( var_export( $wp_query->posts, true ) ); should be placed after the $wp_query = new \WP_Query... line. Can you please make that change, try again and share the results? Thanks

danielecelsa commented 3 years ago

Hi @felipeelia,

ok sorry, it was a misunderstanding.

Here new debug.log file, hope this could help: debug_log_github_2.txt

Thank you in advance for your kind help! Regards, Daniele

danielecelsa commented 3 years ago

Hello @felipeelia

did you maybe have the chance to have a look to my last message/log file? Or maybe it was incomplete and you could not make any investigation?

Kindly let me know if you need further info to help me :)

Thank you in advance, Daniele

felipeelia commented 3 years ago

Hi @danielecelsa ,

Looking at the logs, it looks like ElasticPress may be looking for "sushi" in other fields or taxonomies. Did you already try to disable everything in the Weighting Dashboard and leaving only "Title" enabled?

danielecelsa commented 3 years ago

Hi @felipeelia ,

EP looking for 'sushi' in other fields or taxonomy is exactly what I implemented and what I'd like to have. And it is working fine in all the pages, except the last one.

The problem is that, in the last page, the results are coming like they are searched with the default 'WP search', so maybe only in the 'Title' or however it works.

Hope to have been clear, kindly let me know if you need further details. And thanks again for your time! :) Daniele

danielecelsa commented 3 years ago

Hi @felipeelia ,

did you maybe had the chance to have a look at my last comment?

Feel free to let me know if I have not been clear, so I will try to explain better.

Thanks! Daniele

felipeelia commented 3 years ago

Hi @danielecelsa ,

We'll need to give a look at the queries sent to the Elasticsearch server to figure out why it is returning those results. Do you have any kind of log that contains it? Could you please share it with us?

The regular WP search will use just the product title and content. ElasticPress, on the other hand, will use those fields and some taxonomies too. Did you try disabling everything in the Weighting Dashboard and leaving just "Title" enabled?

felipeelia commented 3 years ago

As we didn’t have any movement in this issue for a while now, we are now closing it. If you’re still facing the issue, please let us know. Otherwise, if you’ve managed to get an answer to your problem, we’d highly appreciate it if you could get back to us here and leave the solution in a comment. Thanks!

danielecelsa commented 3 years ago

Hello again @felipeelia and all,

sorry I have been very busy and could not answer, so you correctly closed the case. Wonder if you can open it again and help me out.

Besides the test you asked to make, I would like to ask you a basic thing that maybe is what I am missing: How/where exactly should I set ep_integrate = true ?

I got that this is the main issue, Load More plugin use the WP query and not the ES query. So the goal is telling Load More to use ES query.

I tried it on the plugin itself (the one of Load More), but it is not working (I add screenshot at the end of this post)

You suggested to use _pre_getposts, but actually I don't know how. I tried to write this in function.php:

Schermata 2021-04-08 alle 20 48 16

But it did not work, actually I receive a Page Not Found when I try to reach my website, so I deleted it.

[EDIT]: I add the condition: if ( $query->is_search ) and now I do not get anymore Page Not Found, but still the issue is not solved [/EDIT]

So, could you kindly help me make all the queries to be integrated with ES, so that maybe also Load More will 'see' it (since I don't find how to set it only on the plugin)?

Hope to have been clear, kindly let me know if not.

Thanks in advance! Daniele

Schermata 2021-04-08 alle 20 42 38
danielecelsa commented 3 years ago

Hello,

did you have the chance to have a look to my comment?

I am writing you also to ask if you need debug.log file, which configuration you would need me to set?

Consider that, with or without '_epintegrate =true' in the plugin, I see this in the debug.log:

Schermata 2021-04-14 alle 20 41 04

That part in debug.log is shown thanks to command: error_log( var_export( $wp_query->posts, true ) );

Furthermore, if I put '_epintegrate =true' in the plugin, I also see this in the debug.log

Schermata 2021-04-14 alle 20 44 22

in the part shown thanks to command: error_log( var_export( $this->query, true ) );

Anyway nothing change, the results shown are the same with or without '_epintegrate =true'

The way I insert '_epintegrate =true' in the file ajax-pagination.php is: $this->query['ep_integrate'] = true;

Thanks in advance for your kind help, Daniele

pinguluk commented 3 years ago

Hey @danielecelsa, are you using Elementor Custom Skin for the load more? I ran into the same or similar issue as well these days.

I use JetSmartFilters to filter some posts (Elementor Posts Widget) on a page and it seems that after I filter the posts, it renders the posts, but just based on its query.

I tested out the load more from Elementor Custom Skin and it seems that when you click the Load More button, it sends an ajax request, to retrieve the posts. In this ajax request, it sends these parameters:

action: ecsload

query: {
   "page":0,
   "pagename":"news-and-events",
   "error":"",
   "m":"",
   "p":0,
   "post_parent":"",
   "subpost":"",
   "subpost_id":"",
   "attachment":"",
   "attachment_id":0,
   "name":"news-and-events",
   "page_id":0,
   "second":"",
   "minute":"",
   "hour":"",
   "day":0,
   "monthnum":0,
   "year":0,
   "w":0,
   "category_name":"",
   "tag":"",
   "cat":"",
   "tag_id":"",
   "author":"",
   "author_name":"",
   "feed":"",
   "tb":"",
   "paged":0,
   "meta_key":"",
   "meta_value":"",
   "preview":"",
   "s":"",
   "sentence":"",
   "title":"",
   "fields":"",
   "menu_order":"",
   "embed":"",
   "category__in":[

   ],
   "category__not_in":[

   ],
   "category__and":[

   ],
   "post__in":[

   ],
   "post__not_in":[

   ],
   "post_name__in":[

   ],
   "tag__in":[

   ],
   "tag__not_in":[

   ],
   "tag__and":[

   ],
   "tag_slug__in":[

   ],
   "tag_slug__and":[

   ],
   "post_parent__in":[

   ],
   "post_parent__not_in":[

   ],
   "author__in":[

   ],
   "author__not_in":[

   ],
   "post_type":[
      "post",
      "page",
      "e-landing-page"
   ],
   "ignore_sticky_posts":false,
   "suppress_filters":false,
   "cache_results":true,
   "update_post_term_cache":true,
   "lazy_load_term_meta":true,
   "update_post_meta_cache":true,
   "posts_per_page":10,
   "nopaging":false,
   "comments_per_page":"50",
   "no_found_rows":false,
   "order":"DESC"
}
ecs_ajax_settings: {
   "current_page":1,
   "max_num_pages":99,
   "load_method":"loadmore",
   "widget_id":"6c79da20",
   "post_id":"4120",
   "theme_id":"5499"
}

I tried to change the query parameter, but it seems that it doesn't matter at all! It ignores it completely! After more digging, I found out that the widget_id, post_id and theme_id matter. widget_id is the id from the Elementor Post Widget post_id is the id of the page where the post widget is theme_id is the id of the template that you are using to display the custom template for the post widget

Then I've looked into the includes/ajax-pagination.php file to try to find the problem, but could not pinpoint the problem exactly. It seems that it just ignores the query parameter from the ajax request and it doesn't affect the final query. It will query based of the settings of the elementor post widget (e.g. if you edit the widget and set the query to include the terms 'sushi', then the load more function will only load posts that include the term (taxonomy) 'sushi'

I've edited the file and added these lines:

$this->customFilterQuery = json_decode(
    stripslashes($_POST['custom_filter_term_ids']),
    true
);

before these lines:

if ($this->current_page > $args['max_num_pages']) {
    return;
}

and

if (isset($this->customFilterQuery) && $this->customFilterQuery["term_ids"] != "") {
    $data[0]["settings"]["posts_include"] = ["terms"];
    $data[0]["settings"]["posts_include_term_ids"] = explode(
        ",",
        $this->customFilterQuery["term_ids"]
    );
}

before these lines:

// Change the current post, so widgets can use `documents->get_current`.
\Elementor\Plugin::$instance->documents->switch_to_document($document);

I've also edited the assets/js/ecs_ajax_pagination.js file and added a parameter called custom_filter_term_ids, that has a list of terms ids (from the selectors from the filter). Basically it will send the ids for the terms that you want, to the backend function and will "override" the post widget settings. It's a hacky workaround, but it works.

I'm bit in a hurry, so I will update my answer later for more info. Long story short: there's a bug (?) in the elementor custom skin plugin, that ignores the query parameter from the ajax and there's an incompability with any plugin that affects the rendering (elasticpress, jetsmartfilters etc) and you need to come with a workaround, by passing the terms or anything you want to filter, in the ajax and use them to override the post widget settings, because the WP_Query also doesn't work (?).

pinguluk commented 3 years ago

Here are my patches that I made. The patches are marked with a "HOTFIX" label. Feel free to ask me any questions.

// HOTFIX: detect if posts are changed (because of the jetsmartfilters rendering, load more button needs to be reinitialized again) function detectFilteredPostsChange() { // Detect if posts are changed jQuery('.elementor-tab-content .elementor-widget-posts').bind('DOMSubtreeModified', function () { // Get load more button let load_more_button = jQuery(this).find('.ecs-load-more-button a'); if (load_more_button.length) { // If load more button is not initalized correctly let href = jQuery(load_more_button).attr('href'); if (href.indexOf('javascript') == -1) { updateLoadMoreButtons(); } } }); }

// HOTFIX: Get selected filters terms ids function getFilterIds() { let ids = []; // Loop selects and get selected values jQuery.each(jQuery('.jet-filters-group:visible').find('select'), function (index, select) { let id = jQuery(select).select2('data')[0].id; if (id != "") { ids.push(id); } }); return ids; }

// HOTFIX: (Re)init load more buttons function updateLoadMoreButtons() { jQuery(".ecs-load-more-button").each(function () { widget = jQuery(this); settings = widget.attr("data-settings"); args = JSON.parse(settings); widget.children(".elementor-button").attr("href", "javascript:ECS_load_next_page('" + args.widget_id + "');"); // (Re)init post id & theme id updatePostAndThemeIds(widget); }); }

// HOTFIX: (Re)init post id & theme id (because of the jetsmartfilters rendering, the post_id is false and theme_id is empty) function updatePostAndThemeIds(widget) { // Retrieve post id & theme id let section = jQuery(widget).closest("div[data-elementor-type='section']"); let post_id = jQuery(section).attr('data-post-id'); let theme_id = jQuery(section).attr('data-elementor-id');

// Retrieve posts data-settings (they have post_id=false and theme_id="")
let ecs_posts = jQuery(section).find('.ecs-posts');
let ecs_settings = jQuery(ecs_posts).attr('data-settings');
if (ecs_settings == undefined || ecs_settings == "" ) {
    return;
}
// Replace post_id=false and theme_id="" with the correct ones
let ecs_settings_updated = JSON.parse(ecs_settings);
ecs_settings_updated.post_id = post_id;
ecs_settings_updated.theme_id = theme_id;
ecs_settings_updated = JSON.stringify(ecs_settings_updated);
jQuery(ecs_posts).attr('data-settings', ecs_settings_updated);

}

function ECS_load_next_page(id) {

widget = jQuery(".elementor-element[data-id='" + id + "'] .ecs-posts");
settings = widget.attr("data-settings");
args = JSON.parse(settings);
// HOTFIX: retrieve filters term ids
custom_filter_term_ids = getFilterIds();

posts = jQuery(".elementor-element[data-id='" + args.widget_id + "'] .ecs-posts");

if (args.load_method == 'loadmore') {
    button_text = jQuery(".elementor-element[data-id='" + args.widget_id + "'] .ecs-load-more-button .elementor-button");// add this .elementor-element[data-id='" + args.widget_id + "']
    button = jQuery(".elementor-element[data-id='" + args.widget_id + "'] .ecs-load-more-button");
    attb = JSON.parse(button.attr("data-settings"));
}
if (args.load_method == 'lazyload') {
    animation = jQuery(".elementor-element[data-id='" + args.widget_id + "'] .ecs-lazyload");
}
data = {
    'action': 'ecsload',
    'query': ecs_ajax_params.posts,
    'ecs_ajax_settings': settings,
    // HOTFIX: added custom filter term ids into ajax, so we can query them from the backend, because the "query" parameter doesn't do anything at ALL
    // Because there's a bug? and the load more button loads posts based on the Elementor Widget settings
    'custom_filter_term_ids': '{"term_ids":"' + custom_filter_term_ids.join(',') + '"}'
};

jQuery.ajax({
    url: ecs_ajax_params.ajaxurl, // AJAX handler
    data: data,
    type: 'POST',
    beforeSend: function (xhr) {
        if (args.load_method == 'loadmore') button_text.html(attb.loading_text); // change the button text, you can also add a preloader image
        canBeLoaded = false;
    },
    success: function (data) {
        if (data) {
            posts.append(data); // insert new posts
            args.current_page++;
            if (args.load_method == 'loadmore') {
                button_text.html(attb.text);
                button_text.blur();
            }
            newsettings = JSON.stringify(args);
            widget.attr("data-settings", newsettings);

            if (args.load_method == 'lazyload') { jQuery(animation).addClass("animation-hidden"); }
            //here you need to take care of linkable items and masonry !!!!!!!!!!!!!!!!!
            ECS_do_action('ajax', args);

            if (args.current_page == args.max_num_pages) {
                if (args.load_method == 'loadmore') button.remove(); // if last page, remove the button
                if (args.load_method == 'lazyload') animation.remove();
            }
            canBeLoaded = true;

            if (typeof ECScheckInView !== 'undefined') ECScheckInView();
            // you can also fire the "post-load" event here if you use a plugin that requires it
            // $( document.body ).trigger( 'post-load' );

            // HOTFIX: (Re)init post id & theme id (because of the jetsmartfilters rendering, the post_id is false and theme_id is empty)  
            updatePostAndThemeIds();
        } else {
            if (args.load_method == 'loadmore') { button.remove(); } // if no data, remove the button as well
            if (args.load_method == 'lazyload') { animation.remove(); }
        }
    }
});

}

jQuery(function ($) { $('.ecs-lazyload').addClass("animation-hidden"); $('.ecs-lazyload a').css("display", "none"); });

// HOTFIX: removed EleCustomSkinChangeUrlPage function, because we don't need pagination to be added in the url / history


- includes/ajax-paginaiton.php:

<?php if (!defined('ABSPATH')) { exit(); } // Exit if accessed directly class ECS_Ajax_Load { private $post_id = ''; private $current_page = 1; private $widget_id = ''; private $theme_id = ''; private $query = [];

public function __construct($args = [])
{
    //["post_id"=>2,"current_page"=>2,"max_num_pages"=>5,"widget_id"=>"65054a0"]
    $this->init();

    // HOTFIX: (Re)init variables
    $this->update_variables();

    $this->init_ajax();
}

// HOTFIX: (Re)init variables (before they were initalized only in the constructor)
public function update_variables()
{
    if (!isset($args['post_id'])) {
        if (!isset($_POST['ecs_ajax_settings'])) {
            return;
        } else {
            $args = json_decode(
                stripslashes($_POST['ecs_ajax_settings']),
                true
            );
        }
    }

    $this->post_id = $args['post_id'];
    $this->current_page = $args['current_page'] + 1;
    $this->widget_id = $args['widget_id'];
    $this->theme_id = isset($args['theme_id'])
        ? $args['theme_id']
        : $args['post_id'];
    $this->query = json_decode(stripslashes($_POST['query']), true);
    $this->customFilterQuery = json_decode(
        stripslashes($_POST['custom_filter_term_ids']),
        true
    );

    if ($this->current_page > $args['max_num_pages']) {
        return;
    }
}

public function init()
{
    add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts'], 99);
    add_action(
        'elementor/element/before_section_end',
        [$this, 'post_pagination'],
        10,
        3
    );
    add_action(
        'elementor/element/after_section_end',
        [$this, 'button_pagination_style'],
        10,
        3
    );
}

public function init_ajax()
{
    //add_action( 'wp_footer',[$this,'get_document_data'],99);// debug line comment it
    add_action('wp_ajax_ecsload', [$this, 'get_document_data']);
    add_action('wp_ajax_nopriv_ecsload', [$this, 'get_document_data']);
}

public function post_pagination($element, $section_id = '', $args = '')
{
    if (
        ('archive-posts' === $element->get_name() ||
            'posts' === $element->get_name()) &&
        'section_pagination' === $section_id
    ) {
        $element->remove_control('pagination_type');

        $element->add_control('pagination_type', [
            'label' => __('Pagination', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::SELECT,
            'default' => '',
            'options' => [
                '' => __('None', 'elementor-pro'),
                'numbers' => __('Numbers', 'elementor-pro'),
                'loadmore' => __(
                    'Load More (Custom Skin)',
                    'ele-custom-skin'
                ),
                'lazyload' => __(
                    'Infinite Load (Custom Skin Pro)',
                    'ele-custom-skin'
                ),
                'prev_next' => __('Previous/Next', 'elementor-pro'),
                'numbers_and_prev_next' =>
                    __('Numbers', 'elementor-pro') .
                    ' + ' .
                    __('Previous/Next', 'elementor-pro'),
            ],
        ]);
        /* lazyload stuff*/
        $element->add_control('lazyload_title', [
            'label' => __('Infinite Load', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::HEADING,
            'separator' => 'before',
            'condition' => ['pagination_type' => 'lazyload'],
        ]);

        $element->add_control('lazyload_animation', [
            'label' => __('Loading Animation', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::SELECT,
            'default' => 'default',
            'options' => ECS_Loading_Animation::get_lazy_load_animations_list(),
            'condition' => ['pagination_type' => 'lazyload'],
        ]);
        $element->add_control('lazyload_color', [
            'label' => __('Animation Color', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .ecs-lazyload .ecs-ll-brcolor' =>
                    'border-color: {{VALUE}};',
                '{{WRAPPER}} .ecs-lazyload .ecs-ll-bgcolor' =>
                    'background-color: {{VALUE}} !important;',
            ],
            'condition' => ['pagination_type' => 'lazyload'],
        ]);

        $element->add_control('lazyload_spacing', [
            'label' => __('Animation Spacing', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::SLIDER,
            'range' => ['px' => ['max' => 250]],
            'default' => ['unit' => 'px', 'size' => '20'],
            'selectors' => [
                '{{WRAPPER}} .ecs-lazyload' =>
                    'margin-top: {{SIZE}}{{UNIT}};',
            ],
            'condition' => ['pagination_type' => 'lazyload'],
        ]);
        $element->add_control('lazyload_size', [
            'label' => __('Animation Size', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::SLIDER,
            'range' => ['px' => ['max' => 50]],
            'selectors' => [
                '{{WRAPPER}} .ecs-lazyload .ecs-lazy-load-animation' =>
                    'font-size: {{SIZE}}{{UNIT}};',
            ],
            'condition' => ['pagination_type' => 'lazyload'],
        ]);

        /* load more button stuff */

        $element->add_control('loadmore_title', [
            'label' => __('Load More Button', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::HEADING,
            'separator' => 'before',
            'condition' => ['pagination_type' => 'loadmore'],
        ]);

        $element->add_control('loadmore_text', [
            'label' => __('Text', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::TEXT,
            'default' => __('Load More', 'elementor'),
            'placeholder' => __('Load More', 'elementor'),
            'condition' => ['pagination_type' => 'loadmore'],
        ]);

        $element->add_control('loadmore_loading_text', [
            'label' => __('Loading Text', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::TEXT,
            'default' => __('Loading...', 'elementor'),
            'placeholder' => __('Loading...', 'elementor'),
            'condition' => ['pagination_type' => 'loadmore'],
        ]);

        $element->add_control('loadmore_spacing', [
            'label' => __('Button Spacing', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::SLIDER,
            'range' => ['px' => ['max' => 250]],
            'default' => ['unit' => 'px', 'size' => '20'],
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button' =>
                    'margin-top: {{SIZE}}{{UNIT}};',
            ],
            'condition' => ['pagination_type' => 'loadmore'],
        ]);
    }
}

public function button_pagination_style(
    $element,
    $section_id = '',
    $args = ''
) {
    if (
        ('archive-posts' === $element->get_name() ||
            'posts' === $element->get_name()) &&
        'section_pagination_style' === $section_id
    ) {
        $element->start_controls_section('loadmore_section_style', [
            'label' => __('Load More Button', 'ele-custom-skin'),
            'tab' => \Elementor\Controls_Manager::TAB_STYLE,
            'condition' => ['pagination_type' => 'loadmore'],
        ]);

        $element->add_group_control(
            \Elementor\Group_Control_Typography::get_type(),
            [
                'name' => 'loadmore_typography',
                'scheme' =>
                    \Elementor\Core\Schemes\Typography::TYPOGRAPHY_4,
                'selector' =>
                    '{{WRAPPER}} .ecs-load-more-button .elementor-button',
            ]
        );

        $element->add_group_control(
            \Elementor\Group_Control_Text_Shadow::get_type(),
            [
                'name' => 'loadmore_text_shadow',
                'selector' =>
                    '{{WRAPPER}} .ecs-load-more-button .elementor-button',
            ]
        );

        $element->start_controls_tabs('tabs_button_style');

        $element->start_controls_tab('loadmore_tab_button_normal', [
            'label' => __('Normal', 'ele-custom-skin'),
        ]);

        $element->add_control('loadmore_button_text_color', [
            'label' => __('Text Color', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::COLOR,
            'default' => '',
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button' =>
                    'fill: {{VALUE}}; color: {{VALUE}};',
            ],
        ]);

        $element->add_control('loadmore_background_color', [
            'label' => __('Background Color', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::COLOR,
            'scheme' => [
                'type' => \Elementor\Core\Schemes\Color::get_type(),
                'value' => \Elementor\Core\Schemes\Color::COLOR_4,
            ],
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button' =>
                    'background-color: {{VALUE}};',
            ],
        ]);

        $element->end_controls_tab();

        $element->start_controls_tab('loadmore_tab_button_hover', [
            'label' => __('Hover', 'ele-custom-skin'),
        ]);

        $element->add_control('loadmore_hover_color', [
            'label' => __('Text Color', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button:hover, {{WRAPPER}} .ecs-load-more-button .elementor-button:focus' =>
                    'color: {{VALUE}};',
                '{{WRAPPER}} .ecs-load-more-button .elementor-button:hover svg, {{WRAPPER}} .ecs-load-more-button .elementor-button:focus svg' =>
                    'fill: {{VALUE}};',
            ],
        ]);

        $element->add_control('loadmore_button_background_hover_color', [
            'label' => __('Background Color', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::COLOR,
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button:hover, {{WRAPPER}} .elementor-button:focus' =>
                    'background-color: {{VALUE}};',
            ],
        ]);

        $element->add_control('loadmore_button_hover_border_color', [
            'label' => __('Border Color', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::COLOR,
            'condition' => ['border_border!' => ''],
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button:hover, {{WRAPPER}} .elementor-button:focus' =>
                    'border-color: {{VALUE}};',
            ],
        ]);

        $element->add_control('loadmore_hover_animation', [
            'label' => __('Hover Animation', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::HOVER_ANIMATION,
        ]);

        $element->end_controls_tab();

        $element->end_controls_tabs();

        $element->add_group_control(
            \Elementor\Group_Control_Border::get_type(),
            [
                'name' => 'loadmore_border',
                'selector' => '{{WRAPPER}} .elementor-button',
                'separator' => 'before',
            ]
        );

        $element->add_control('loadmore_border_radius', [
            'label' => __('Border Radius', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::DIMENSIONS,
            'size_units' => ['px', '%'],
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button' =>
                    'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
            ],
        ]);

        $element->add_group_control(
            \Elementor\Group_Control_Box_Shadow::get_type(),
            [
                'name' => 'loadmore_button_box_shadow',
                'selector' =>
                    '{{WRAPPER}} .ecs-load-more-button .elementor-button',
            ]
        );

        $element->add_responsive_control('loadmore_text_padding', [
            'label' => __('Padding', 'ele-custom-skin'),
            'type' => \Elementor\Controls_Manager::DIMENSIONS,
            'size_units' => ['px', 'em', '%'],
            'selectors' => [
                '{{WRAPPER}} .ecs-load-more-button .elementor-button' =>
                    'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
            ],
            'separator' => 'before',
        ]);
        $element->end_controls_section();
    }
}

private function get_element_data($id, $data)
{
    foreach ($data as $element) {
        //echo "[".$element['id']."] == (".$id.")";
        if (isset($element['id']) && $element['id'] == $id) {
            return $element;
        } else {
            //echo $element['id']." - ".count($element['elements'])." > ";//print_r($element['elements']);
            if (count($element['elements'])) {
                $element_children = $this->get_element_data(
                    $id,
                    $element['elements']
                );
                if ($element_children) {
                    return $element_children;
                }
            }
            //echo"am ajuns aici?";
        }
    }
    return false;
}

public function get_document_data()
{
    global $wp_query;

    // HOTFIX: (Re)init variables
    $this->update_variables();

    $id = $this->widget_id;

    $post_id = $this->post_id;
    $theme_id = $this->theme_id;
    $old_query = $wp_query->query_vars;

    $this->query['paged'] = $this->current_page; // we need current(next) page to be loaded
    $this->query['post_status'] = 'publish';

    $wp_query = new \WP_Query($this->query);
    wp_reset_postdata(); //this fixes some issues with some get_the_ID users.
    if (is_archive()) {
        $post_id = $theme_id;
    }

    $document = \Elementor\Plugin::$instance->documents->get_doc_for_frontend(
        $post_id
    );
    $theme_document = \Elementor\Plugin::$instance->documents->get_doc_for_frontend(
        $theme_id
    );

    $data[] = $this->get_element_data(
        $id,
        $theme_document->get_elements_data()
    );

    // HOTFIX: Check for the custom_filter_term_ids from the ajax if they are set and if they are not empty
    // Because the "query" parameter from the ajax doesn't do anything / doesn't matter AT ALL, we will hack the Elementor Widget settings
    // to force to query by "terms" and by corresponding term ids from the filters (hacky workaround to work with the Jetsmartfilters)
    if (isset($this->customFilterQuery) && $this->customFilterQuery["term_ids"] != "") {
        $data[0]["settings"]["posts_include"] = ["terms"];
        $data[0]["settings"]["posts_include_term_ids"] = explode(
            ",",
            $this->customFilterQuery["term_ids"]
        );
    }

    // Change the current post, so widgets can use `documents->get_current`.
    \Elementor\Plugin::$instance->documents->switch_to_document($document);

    ob_start();
    $document->print_elements_with_wrapper($data);
    $content = ob_get_clean();
    echo $this->clean_response($content, $id);

    \Elementor\Plugin::$instance->documents->restore_document();
    $wp_query->query_vars = $query_vars;

    die();
}

private function clean_response($html, $id)
{
    $content = "";
    $dom = new DOMDocument();
    libxml_use_internal_errors(true);
    $dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
    //$dom->loadHTML($html);
    $xpath = new DOMXPath($dom);
    $childs = $xpath->query(
        '//div[@data-id="' .
            $id .
            '"]/div[@class="elementor-widget-container"]/div/* | //div[@data-elementor-type="custom_grid"]'
    );
    //    $childs = $xpath->query('//div[@data-elementor-type="custom_grid"]');
    //return $dom->saveHTML($childs->item(0));
    foreach ($childs as $child) {
        $content .= $dom->saveHTML($child);
    }
    //$div = $div->item(0);
    return $content;
}

public function enqueue_scripts()
{
    global $wp_query;

    wp_register_script(
        'ecs_ajax_load',
        plugin_dir_url(__DIR__) . 'assets/js/ecs_ajax_pagination.js',
        ['jquery'],
        ELECS_VER
    );

    wp_localize_script('ecs_ajax_load', 'ecs_ajax_params', [
        'ajaxurl' => site_url() . '/wp-admin/admin-ajax.php', // WordPress AJAX
        'posts' => json_encode($wp_query->query_vars),
    ]);

    wp_enqueue_script('ecs_ajax_load');
}

}

class ECS_Loading_Animation { private static function animations() { return [ 'default' => [ 'label' => ('Default', 'ele-custom-skin'), 'html' => '

', ], 'progress_bar' => [ 'label' => __('Progress Bar', 'ele-custom-skin'), 'html' => '
', ], 'running_dots' => [ 'label' =>
('Running Dots', 'ele-custom-skin'), 'html' => '
', ], 'ball_slide' => [ 'label' => __('Ball Slide', 'ele-custom-skin'), 'html' => '
', ], ]; }

public static function get_lazy_load_animations_html($animation)
{
    $arrs = self::animations();
    return $arrs[$animation]['html'];
}

public static function get_lazy_load_animations_list()
{
    $arrs = self::animations();
    foreach ($arrs as $key => $arr) {
        $options[$key] = $arr['label'];
    }
    return $options;
}

}

new ECS_Ajax_Load();

danielecelsa commented 3 years ago

Hello @pinguluk

WOW thank you for sharing!

I am in hurry too, so I will check better later all your really interesting material :)

Anyway Yes, I am using Ele Custom Skin and there is for sure a bug to fix, but unfortunately the creator of this plugin is always busy..

For now, I just want to add that I was wondering if the issue was about these lines in ajax-pagination.php: $old_query = $wp_query->query_vars;

and

$wp_query->query_vars = $query_vars;

The second one throws an error if you check the file debug.log. To get it, you need too add the following lines that @felipeelia kindly suggested at the beginning of this thread: error_log( var_export( $this->query, true ) ); or error_log( var_export( $wp_query->posts, true ) );

I don't remember which one is actually printing the log I meant.

The error should be something like '$query_vars is not defined'

Soo maybe the right line should be: $wp_query->query_vars = $old_query;

otherwise $old_query is not used in the function.

So I was wondering if maybe the creator wanted to use that approach but it made an error in the syntax. Maybe the 'old' query_vars, that should be the vars of the original first query, should be somehow used in the creation of the new query.

Something like: $this->query['query_vars'] = $old_query;

but I still did not have time to try, and anyway I am sure the syntax is incorrect :)

Anyway I will try to use your HOTFIX in order to workaround also my situation with ElasticSearch, thank you very much for sharing! I will let you know :)

Daniele

pinguluk commented 3 years ago

I will look again into that thing with $wp_query->query_vars later, maybe I will find something interesting. Also, I think you'll need to adapt the hotfix, because I was querying by terms (tag taxonomy) and you are querying by search (which is querying the posts if they include the searched string) and I don't know if you can set somewhere in the post widget settings to filter by that (try to var_dump the $data variable and look what else can you modify)

Junior774 commented 2 years ago

Hi @pinguluk

I think I'm having the same problem and I'm trying to implement your solution, where do I set the ids terms? and where I can see the ids of my terms, I'm using advanced query options dinamyc related posts.

pinguluk commented 2 years ago

Hi @pinguluk

I think I'm having the same problem and I'm trying to implement your solution, where do I set the ids terms? and where I can see the ids of my terms, I'm using advanced query options dinamyc related posts.

From what I remember and read from my comment, on initial load and every load next page, I call the custom getFilterIds function, which retrieves the ids of the posts and set them in the data, for the ajax request:

data = {
        'action': 'ecsload',
        'query': ecs_ajax_params.posts,
        'ecs_ajax_settings': settings,
        // HOTFIX: added custom filter term ids into ajax, so we can query them from the backend, because the "query" parameter doesn't do anything at ALL
        // Because there's a bug? and the load more button loads posts based on the Elementor Widget settings
        'custom_filter_term_ids': '{"term_ids":"' + custom_filter_term_ids.join(',') + '"}'
    };

then in the php file, I retrieve and assign the data to a new variable

$this->customFilterQuery = json_decode(
    stripslashes($_POST['custom_filter_term_ids']),
    true
);

and then override the data, to be queried.

if (isset($this->customFilterQuery) && $this->customFilterQuery["term_ids"] != "") {
    $data[0]["settings"]["posts_include"] = ["terms"];
    $data[0]["settings"]["posts_include_term_ids"] = explode(
        ",",
        $this->customFilterQuery["term_ids"]
    );
}

which basically sets the posts_include_term_ids to the specified term ids.

Just follow the code and try to figure it out

raulillana commented 1 year ago

Hey @danielecelsa! Did you solve this with the patch from @pinguluk?

Actually using searchandfilter query and messing around this function too...

If any of you guys has a patch, please, make a PR in the plugin github so others like me can benefit from it.

https://github.com/dudaster/ele-custom-skin/

Thanks in advance!