Ignore Radius Breaks Search Button

Search Button Not Working (User: Teivin)

From their website, search button does not fire.

Analysis

If Settings | Search | Radius Behavior is set to “Do not use” it will trigger the problem.

The backend code in the Experience codebase has a bug that is not using a properly initialized AJAX class. This is a side effect of the PHP upgrade.

It is resolved in the 2502.25.01 release of Experience.

MySLP_REST_API Ajax Not Initialized

Generate Embed is not populating the embed map as the REST API call is generating an internal error.

The MySLP_REST_API is expecting the SLP AJAX property to be set and it is null.

Related Call Stack

class.myslp.rest.api.php:438, MySLP_REST_API->get_options()
class.myslp.rest.api.php:279, MySLP_REST_API->get_map_options()
MySLP.php:257, MySLP->rest_dispatch_request_filter()
class-wp-hook.php:324, WP_Hook->apply_filters()
plugin.php:205, apply_filters()
class-wp-rest-server.php:1187, WP_REST_Server->respond_to_request()
class-wp-rest-server.php:1041, WP_REST_Server->dispatch()
class-wp-rest-server.php:431, WP_REST_Server->serve_request()
rest-api.php:424, rest_api_loaded()
class-wp-hook.php:324, WP_Hook->apply_filters()
class-wp-hook.php:348, WP_Hook->do_action()
plugin.php:565, do_action_ref_array()
class-wp.php:418, WP->parse_request()
class-wp.php:813, WP->main()
functions.php:1336, wp()
wp-blog-header.php:16, require()
index.php:17, {main}()

Dev Notes

Stack tracing…

  1. On the initial call the store-locator-plus.php MUP is called first an is loading with the initMySLP _jsonp call from the embed script.
  2. the SLPlus::initialize_after_plugins_loaded() is being called.
    • DOING_AJAX is not set, so createobject_AJAX() is not called.
  3. MySLP_REST_API->get_options() is called, which expects SLP->AJAX to be set, but it is not.

The front-end/locations.js is using jQuery.ajax() but it is calling a REST API url, not an AJAX listener endpoint… from the locations.js call…

Update the MySLP_REST_API->get_options() method to use SLP_Ajax::get_instance() to ensure that object is instantiated before use when not doing an AJAX call.

Use the main MySLP::getUserBlogId() to fetch blog IDs.
Use SLP_Ajax::get_instance() versus the uninitialized slplus->AjaxHandler to get the SLP AJAX instance.

slplus->AJAX (slplus->AJAX_Handler) was not initialized because the call is a REST call not an AJAX call.

Architecture: Locations Bulk Action

Location bulk actions allow for users to work on multiple locations at once. Export a list of locations, delete select locations from the location table, geocode locations ,and more.

2209.12

Export locations is our first attempt at making bulk actions work more like a single page application.

Instead of doing a form post and submitting the location list, then reading in some PHP-driven variables to set the JavaScript behavior — in 2209.12 the export process bypasses the form submit.

Bypass is handled by using a pub/sub module with a dynamic filter name “exportBulkAction”. This tells the main SLP JavaScript process to execute the callback stack instead of pushing a form submit. This allows the AJAX call to happen immediately without a full page re-render.

2208.15

The apply button processing is handled in the main plugin’s admin.js file.

It uses jQuery to process form variables and prepare the #locationForm to be submitted.

When submitted it sends the request back to WordPress for standard “full page load” processing.

Architecture: Location Download

A feature provided in the Professional level service is a CSV export of locations via the Location Manager page.

2209.12

This process is being revised so that export bulk action works more like a single page application.

Also see the Bulk Action processor page.

2208.15

The process flow for the Locations : Export CSV…

The process starts with a standard form submit from the Bulk Actions “Apply” button.

WordPress sets up a JavaScript variable location_import that has info about the action “export”.

This triggers a jQuery(document).ready() call that fires another request via AJAX back to WordPress with some new parameters in place.

That AJAX call eventually sends back a CSV file that is stored in a DOM div that was prepared with the jQuery() document loader when the refreshed page (post original submit) was rendered.

0 comments on “Debugging: Location Category Not Filtering”

Debugging: Location Category Not Filtering

The User Action : Filtering Locations With Category on the Testing : Location Categories article is not limiting output to only the specified category.

Tech Overview

The front end sends a request back to WordPress via AJAX to request a list of locations. Part of that request encodes all of the form field entries, including the category drop down selection, into a query-encoded (key/value pairs with an & delimiter) string that is send in the formData property to the backend.

The category ID is not being parsed and filtered properly.

Looking At The Data Request

Via browser developer tools and the network I/O inspector, XHR filter…

HTTP Method: POST
URL: /wp-admin/admin-ajax.php
Request Payload…

You can see the formdata property in the POST body along with the encoded form data including the cat=5 entry. This is where the requests tells the back end to limit results to those that have WordPress category ID #5 attached to the location.

Diving Into The Code

On the back end the AJAX is routed through the Store Locator Plus® base plugin via the standard WordPress hooks and filters. These are setup in the SLP_AJAX class via this add_ajax_hooks method:

    /**
     * Add our AJAX hooks.
     *
     * @uses \SLP_AJAX::slp_delete_location for the AJAX 'slp_delete_location' action
     */
    public function add_ajax_hooks() {

    	// -- priv (WP logged in) and no priv (guest) AJAX calls
        add_action( 'wp_ajax_csl_ajax_search', array( $this, 'csl_ajax_search' ) );
        add_action( 'wp_ajax_nopriv_csl_ajax_search', array( $this, 'csl_ajax_search' ) );

        add_action( 'wp_ajax_csl_ajax_onload', array( $this, 'csl_ajax_onload' ) );
        add_action( 'wp_ajax_nopriv_csl_ajax_onload', array( $this, 'csl_ajax_onload' ) );

	    // -- priv only AJAX calls (WP logged in)
	    add_action( 'wp_ajax_slp_delete_location', array( $this, 'slp_delete_location' ) );
        add_action( 'wp_ajax_slp_change_option', array( $this, 'slp_change_option' ) );
    }

Where the csl_ajax_search eventually finds its way to the find_locations method.

Debugging the find_locations method it looks like the formdata variable coming in via the superglobal $_REQUEST variable is not being set properly.

Digging Into The Malformed Variables

Looking at the call stack the form data is mishandled causing the & in the query string to be encoded as &amp, this causes wp_parse_args() to set the property name incorrect as you can see in the debugging screenshot below.

Turns out the wp_kses_post() is too aggressive with the data munging and converts & to & which then throws wp_parse_args() for a loop.

Instead we need to use esc_url_raw() to leave most URL entities intact.

0 comments on “SLP Settings : Quick Save”

SLP Settings : Quick Save

Originally all of the SLP settings forms had to be submitted by clicking a save button. This runs a typical form post + full page render… old-school web 1.X style.

Along the way work was done to utilize JavaScript triggers and save data with an onBlur on settings form fields. The tech uses jQuery using an AJAX-type model to send a single form field to the WordPress AJAX processor to manage saving a single field without re-rendering the entire page or sending the entire form.

How It Is Triggered

SLP base plugin’s admin.js initializes and hooks a change_option JS function to any INPUT type field with a quick_save CSS class attached.

    var qs_inputs = jQuery('.quick_save').find(':input');
    qs_inputs.on('change', function (e) {
      change_option(e.currentTarget);
    });
    qs_inputs.on('blur', function (e) {
      var start_val = e.currentTarget.defaultValue;
      if (e.currentTarget.value !== start_val) {
        change_option(e.currentTarget);
      }
    });