0 comments on “How Locator Styles Work”

How Locator Styles Work

The following information explains how the Locator Style server works and the process used to change the styles.

Locator Styles Server

Locator Styles provides a way for users of the Store Locator Plus® app to load a style definition from the Store Locator Plus® style server.

The Style Server URL is: https://storelocatorplus.com/wp-json/wp/v2/slp_style_gallery
This is a standard WordPress site with a custom plugin named style-gallery-service that provides REST endpoints for processing queries about Locator Styles.

Locator Styles are a custom post type with an key set to ‘slp_style_gallery’.

slp_style_gallery attributes include:

  • post content : the HTML-like description of the style that is rendered in the Style selector in the Store Locator Plus® plugin on the Style page.
  • custom fields : this include a set of Store Locator Plus® settings (csl-slplus-options and csl-slplus-options_nojs WordPress option JSON object keys) as the name and the value is the value of the option that will be set when the style is selected.
    • css : the option key that is provided on ALL Locator Styles which is the CSS that is used when the Store Locator Plus map or directory display is rendered on the users website
    • layout : an HTML + WordPress shortcode string that is processed to generate the overall wireframe of the Store Locator Plus display with a user site.
      • Default: [slp_search][slp_map][slp_results]
    • searchlayout : an HTML + WordPress shortcode string that is processed to generate the HTML output for the [slp_search] shortcode
    • resultslayout : an HTML + WordPress shortcode string that is processed to generate the HTML output for the [slp_results] shortcode
    • maplayout : an HTML + WordPress shortcode string that is processed to generate the HTML output for the [slp_map] shortcode
    • bubblelayout: an HTML + WordPress shortcode string that is processed to generate the HTML output for the Google Maps info bubble that appears when a user clicks on a map icon
    • google_map_style : Google Maps specific JSON map styling for embedded styles.

Fetching Style Data

\SLP_Service::get_styles – fetches styles from the style server.

  • via $this->get_and_validate_json_response( $this->slplus->url_main_slp_site . ‘/’ . SLP_Style_Manager::REST_ENDPOINT . $style_selector . $request_params );
  • $this->slplus->url_main_slp_site = https://storelocatorplus.com
  • SLP_Style_Manager::REST_ENDPOINT = ‘wp-json/wp/v2/slp_style_gallery’

Building A Locator Style

Crafting The Initial CSS and settings.json

To help keep track of the settings and the more complex CSS construction, files are stored in the style-gallery-service plugin.

The wp-content/plugins/style-gallery-service/assets/styles directory contains a subdirectory for each Locator Style.
The subdirectories correspond to Locator Styles (slp_style_gallery) posts on the main Store Locator Plus® website.
Each subdirectory has content that includes:

settings.json : a JSON file the represents all the settings this Locator Style will change when activated on a user’s Store Locator Plus® plugin or SaaS account.

An example settings.json for the Default Locator Style follows:

{
  "CustomFields": {
    "bubblelayout": "<div id=\"slp_info_bubble_[slp_location id]\" class=\"slp_info_bubble [slp_location featured]\">\n    <span id=\"slp_bubble_name\"><strong>[slp_location name  suffix  br]</strong></span>\n    <span id=\"slp_bubble_address\">[slp_location address       suffix  br]</span>\n    <span id=\"slp_bubble_address2\">[slp_location address2      suffix  br]</span>\n    <span id=\"slp_bubble_city\">[slp_location city          suffix  comma]</span>\n    <span id=\"slp_bubble_state\">[slp_location state suffix    space]</span>\n    <span id=\"slp_bubble_zip\">[slp_location zip suffix  br]</span>\n    <span id=\"slp_bubble_country\"><span id=\"slp_bubble_country\">[slp_location country       suffix  br]</span></span>\n    <span id=\"slp_bubble_directions\">[html br ifset directions]\n    [slp_option label_directions wrap directions]</span>\n    <span id=\"slp_bubble_website\">[html br ifset url][slp_location web_link][html br ifset url]</span>\n    <span id=\"slp_bubble_email\">[slp_location email         wrap    mailto ][slp_option label_email ifset email][html closing_anchor ifset email][html br ifset email]</span>\n    <span id=\"slp_bubble_phone\">[html br ifset phone]\n    <span class=\"location_detail_label\">[slp_option   label_phone   ifset   phone]</span>[slp_location phone         suffix    br]</span>\n    <span id=\"slp_bubble_fax\"><span class=\"location_detail_label\">[slp_option   label_fax     ifset   fax  ]</span>[slp_location fax           suffix    br]<span>\n    <span id=\"slp_bubble_description\"><span id=\"slp_bubble_description\">[html br ifset description]\n    [slp_location description raw]</span>[html br ifset description]</span>\n    <span id=\"slp_bubble_hours\">[html br ifset hours]\n    <span class=\"location_detail_label\">[slp_option   label_hours   ifset   hours]</span>\n    <span class=\"location_detail_hours\">[slp_location hours         suffix    br]</span></span>\n    <span id=\"slp_bubble_img\">[html br ifset img]\n    [slp_location image         wrap    img]</span>\n    <span id=\"slp_tags\">[slp_location tags]</span>\n</div>",
    "dropdown_style": "none",
    "google_map_style": "[]",
    "label_phone": "Phone",
    "layout": "<div id=\"sl_div\">[slp_search][slp_map][slp_results]</div>",
    "loading_indicator": "",
    "map_height": "480",
    "map_height_units": "px",
    "map_width": "100",
    "map_width_units": "%",
    "maplayout": "[slp_mapcontent][slp_maptagline]",
    "no_homeicon_at_start": "1",
    "pagination_enabled": "0",
    "results_header": "",
    "results_no_wrapper": "0",
    "resultslayout": "<div id=\"slp_results_[slp_location id]\" class=\"results_entry location_primary [slp_location featured]\">\n    <div class=\"results_row_left_column\"   id=\"slp_left_cell_[slp_location id]\"   >\n        [slp_addon section=primary position=first]\n        <span class=\"location_name\">[slp_location name] [slp_location uml_buttons] [slp_location gfi_buttons]</span>\n        <span class=\"location_distance\">[slp_location distance_1] [slp_location distance_unit]</span>\n        [slp_addon section=primary position=last]\n    </div>\n    <div class=\"results_row_center_column location_secondary\" id=\"slp_center_cell_[slp_location id]\" >\n        [slp_addon section=secondary position=first]\n        <span class=\"slp_result_address slp_result_street\">[slp_location address]</span>\n        <span class=\"slp_result_address slp_result_street2\">[slp_location address2]</span>\n        <span class=\"slp_result_address slp_result_citystatezip\">[slp_location city_state_zip]</span>\n        <span class=\"slp_result_address slp_result_country\">[slp_location country]</span>\n        <div class=\"contact_info\">\n            <span class=\"slp_result_address slp_result_phone\">[slp_location phone_with_label]</span>\n            <span class=\"slp_result_address slp_result_fax\">[slp_location fax]</span>\n        </div>\n        [slp_addon section=secondary position=last]\n    </div>\n    <div class=\"results_row_right_column location_tertiary\"  id=\"slp_right_cell_[slp_location id]\"  >\n        [slp_addon section=tertiary position=first]\n        <div class=\"online_info\">\n        <span class=\"slp_result_contact slp_result_website\">[slp_location web_link]</span>\n        <span class=\"slp_result_contact slp_result_email\">[slp_location email_link]</span>\n        <span class=\"slp_result_contact slp_result_directions\"><a href=\"https://[slp_option map_domain]/maps?saddr=[slp_location search_address]&daddr=[slp_location location_address]\" target=\"_blank\" class=\"storelocatorlink\">[slp_location directions_text]</a></span>\n        </div>\n        <span class=\"slp_result_contact slp_result_hours textblock\">[slp_location hours]</span>\n        [slp_location pro_tags]\n        [slp_location iconarray wrap=\"fullspan\"]\n        [slp_location eventiconarray wrap=\"fullspan\"]\n        [slp_location socialiconarray wrap=\"fullspan\"]\n        [slp_addon section=tertiary position=last]\n    </div>\n</div>",
    "search_by_name": "0",
    "searchlayout": "<div id=\"address_search\" class=\"slp search_box\">\n    [slp_addon location=\"very_top\"]\n    [slp_search_element input_with_label=\"name\"]\n    [slp_search_element input_with_label=\"address\"]\n    [slp_search_element dropdown_with_label=\"city\"]\n    [slp_search_element dropdown_with_label=\"state\"]\n    [slp_search_element dropdown_with_label=\"country\"]\n    [slp_search_element selector_with_label=\"tag\"]\n    [slp_search_element dropdown_with_label=\"category\"]\n    [slp_search_element dropdown_with_label=\"gfl_form_id\"]\n    [slp_addon location=\"before_radius_submit\"]\n    <div class=\"search_item\">\n        [slp_search_element dropdown_with_label=\"radius\"]\n        [slp_search_element button=\"submit\"]\n    </div>\n    [slp_addon location=\"after_radius_submit\"]\n    [slp_addon location=\"very_bottom\"]\n</div>\n",
    "show_country": "1",
    "show_hours": "1",
    "show_icon_array": "0",
    "tag_output_processing": "as_entered"
  }
}

stylesheet.scss : the SCSS (Sass CSS compiler) file that compiles into stylesheet.min.css.
It is a separate file so we can use SCSS to build more sophisticated stylesheets in a managed fashion as well as to share CSS constructs between different locator styles if desired.

An example stylesheet.scss for the Default Locator Style follows:

.store_locator_plus {
  // -- Meta
  #style_name {
    content: "Default"
  }
  #style_version {
    content: "2502.21.01"
  }

  .container {
    &.fluid {
      max-width: 100%;
    }

    .grid-list-lg {
      padding: 16px;
    }
  }

  .card {
    display: block;
    border-radius: 2px;
    min-width: 0;
    position: relative;
    text-decoration: none;

    -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2),
    0 2px 2px rgba(0, 0, 0, 0.14),
    0 3px 1px -2px rgba(0, 0, 0, 0.12);
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2),
    0 2px 2px rgba(0, 0, 0, 0.14),
    0 3px 1px -2px rgba(0, 0, 0, 0.12);

    &__title {
      -webkit-box-align: center;
      -ms-flex-align: center;
      align-items: center;
      display: -webkit-box;
      display: -ms-flexbox;
      display: flex;
      -ms-flex-wrap: wrap;
      flex-wrap: wrap;
      padding: 16px;

      &--primary {
        padding-top: 24px;
      }
    }
  }

  .headline {
    font-size: 24px !important;
    font-weight: 400;
    line-height: 32px !important;
    letter-spacing: normal !important;
  }

  &.tagline {
    font-size: 0.75em;
    text-align: right;
  }

  // Search forms
  .slp_search_form {
    .search_box {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
      align-items: flex-start;
      align-content: stretch;

      .search_item {
        display: flex;
        flex-direction: row;
        flex-wrap: nowrap;
        justify-content: flex-start;
        flex: 1 0 auto;
        min-width: 1em;
        margin-bottom: 0.25em;
        width: 100%;

        label {
          text-align: right;
          margin-right: 0.25em;
          flex-basis: 20%;
        }

        input[type="text"] {
          min-width: 1em;
          flex-basis: 80%;

          &.label_empty {
            flex-basis: 100%;
          }
        }

        div {
          flex: 1 0 50%;
          display: flex;
          flex-direction: row;
          flex-wrap: nowrap;
          justify-content: flex-start;
        }

        #addy_in_radius {
          label {
            flex-basis: 40%;
          }

          select,
          span.ui-widget {
            flex-basis: 60%;
            display: flex;
            flex-direction: row;
            align-items: center;
            padding: 0.25em;

            span {
              display: inline-block;
            }
          }
        }

        #radius_in_submit {
          display: flex;
          flex-direction: row-reverse;
        }

        .slp_ui_button {
          margin: 0.25em 0;
          padding: 0.25em;
        }
      }
    }
  }

  // -- Results
  .slp_results_container {
    .results_wrapper {
      margin: 0.5em 0;
      padding: 0.5em;
      border: solid 1px lightgrey;
      border-radius: 0.5em;

      &:hover {
        background-color: lightgrey;
        border: solid 1px lightgrey;
        cursor: pointer;
      }

      .location_name {
        font-size: 1.15em;
      }

      .location_distance {
        float: right;
        vertical-align: text-top;
        color: grey;
      }

      .contact_info,
      .online_info {
        display: flex;
      }
    }
  }

  //-- Map
  div#map {
    img {
      background-color: transparent;
      box-shadow: none;
      border: none;
      max-width: none;
      opacity: 1.0;

      &[src='http://maps.gstatic.com/mapfiles/iws3.png'] {
        display: none;
      }
    }

    div {
      overflow: visible;
    }

    .gm-style-cc > div {
      word-wrap: normal;
    }
  }
  // Widgets and form elements
  #slp_widget\[city\],
  #map_box_map {
    display: none;
  }

  #map_box_image {
    display: block;
  }

  // -- Map | Bubble | Outermost Wrapper
  .slp_bubble_level_6 {
    .slp_bubble_level_1 {
      color: black;
    }
  }

  // -- Directory
  .slp_directory {
    &.landing_page {
      .results_entry {
        margin-bottom: 1.5em;
      }
    }
  }
}

Note the starting CSS rules:

.store_locator_plus {
  // -- Meta
  #style_name {
    content: "Default"
  }
  #style_version {
    content: "2502.21.01"
  }

The #style_name should always match the name of the Locator Style (the post name).
When the file is changed the #style_version should be updated to match the SLP standard version semver-like system of YYMM.DD.## for the date and iteration number of the latest change to the file.

stylesheet.min.css : the resulting CSS code that ends up in the Locator Style custom meta field named “css”.
This is kept separate from settings.json to keep settings.json from being bloated with the CSS code.

Putting The Style Into Production

Once the settings have been defined and the new CSS style is ready, it is time to put the updates in production.

  • Login to the main Store Locator Plus® website.
  • Go to Locator Styles on the sidebar
  • To create a new style:
    • Add Post
      • Enter a reasonable, but short, description.
      • Add a screen shot.
    • Add custom meta fields
      • The css file should contain the contents of the stylesheet.min.css that was generated.
      • All other settings name in the settings.json should be added as individual Custom Meta fields. With the example above:
        • dropdown_style set to “none”
        • google_map_style set to “[]”
        • label_phone set to “Phone”
        • etc.
  • To update an existing style:
    • Select the style to be changed and edit it.
    • If the CSS changed , replace the existing screenshot.
    • Compare the settings.json CustomFields and update fields that changed. Add any new custom fields.

Select STYLE or Make Changes Deprecated Error

If you change settings in Staging , Under Map or View get errors , example:

Deprecated: Calling get_class() without arguments is deprecated in
mu-plugins/store-locator-plus/include/module/admin_tabs/SLP_BaseClass_Admin.php on line 592 

Reproduction

  • Login or switch to Feeding America San Diego on local develop (or staging)
  • Select a new Locator Style (“Feeding America”)
  • Click Select

Dev Notes

Added get_class($this) to the method in SLP_BaseClass_Admin on line 592 as required by PHP 8.

User Selected Locator Style Not Highlighted

Problem

On staging the user selected styles are not highlighted.

For example , Feeding America San Diego —
On production the “Feeding America” style is highlighted.
On staging NONE are highlighted.

Reproduction

  • Login as a user , or switch to a user from a SA account.
  • Go to menu item Store Locator Plus | Settings
  • Click “View” tab

The user’s selected style should appear – “Feeding America” for the *_feedingsandiego* map for instance. It is not highlighted as selected.

Dev Notes

related architecture notes about style

Production data for FA SD from options_nojs[]…

options_nojs[‘style_id’] is set to 4599 = the post ID for the style we have selected
options_nojs[‘style’] is set to “” = empty string

Somehow production is using the style_id NOT the style name. A better design, but staging is not tracking that for some reason.


options_nojs[‘style_id’]

The ‘style_id’ Smart Option is a hidden type on the Settings | View page.
It fires the JavaScript “change_style_id()” when the value changes.

This is setup via \SLP_SmartOptions::view_appearance()


\MySLP_Customer_Management->fix_active_style_css()

This method uses the options_nojs[‘style_id’] when it loops over all users and set the active_style_css. It uses the \SLP_Style_Manager->apply_style( $style_id , ‘active_style_css’ ) to set the value, then saves it.

This is called from the Admin UI via a Super Admin on menu MySLP | Manage Customers and clicking “Fix Active Style CSS” via the Customers section.

Notes

On the develop database (outdated) the ‘style’ setting is “” (like production) but the ‘style_id’ is set to 0 (does not match production, on production it is something like 4599 — the post id for the “Feeding America” style).

This indicates the develop database is out of sync , as happens during development and testing.

As such the production database definitely needs to be copied to develop to re-test this update and make sure it retains existing customer settings and renders properly. This MAY need to happen on staging as well.

Development Database Updated, Problem Persisted

Turns out the SLP Smart Options were being loaded via \SLP_SmartOptions::initialize_after_plugins_loaded() which is called from…

  • SLP_SmartOptions.php:1515, SLP_SmartOptions->initialize_after_plugins_loaded()
    SLPlus.php:386, SLPlus->initialize_after_plugins_loaded()

    WordPress :: plugins_loaded

This is BEFORE the multisite user is logged in, which loads the smart options prematurely (from the main site) and thus is wrong with the current underlying platform (WP 6.4.X, PHP 8, MySQL 8).

Resolution

The options_nojs[‘style_id’] seems to hold the actual post ID of the selected style.
For some reason on production options_nojs[‘style’] is empty indicating this method of saving the locator style is partly deprecated in the codebase.

For the 2411.XX.YY releases, starting with SLP 2411.19.01 the code now uses the style_id to determine which locator style is the active one when rendering the vision list.

This required a new is_selected() method in the \SLP_Settings_card_list class which is overridden in the extended \SLP_Settings_style_vision_list class. In \SLP_Settings_style_vision_list it compares the options_nojs[‘style_id’] against the post-id for each locator style listed, and if they match returns true.

This seems to address the issue on local develop box.

This did NOT fix the issue — see notes about Smart Options being loaded prematurely above.

Had to create a new method – \SLP_SmartOptions::slp_init_complete() that fires after the WordPress :: init hook and load the user options then.

revised in SLP version: 2411.21.01

Recap

The code that worked for years to load a users settings (talking multisite i.e. SaaS specifically) has been firing off too early.

I have no idea WHEN this started happening, but probably when the SaaS was upgraded to run on WordPress 6.0 (18 months ago) — at least partially.

WordPress 6.4.X changed when users are activated in their startup cycle, it is much later than before. Always.

The impact –

TONS (all) of user settings on staging are loading from the main site (site #1) instead of their own site.

SOME user settings on production are likely doing the same. No idea why not ALL users, but my guess is WordPress 6.0 was a partial change. 6.4 finished that change (it is undocumented as far as I can tell).

The update –

SLP 2411.21.01 which should be on staging soon should address that problem.

All of the SLP options (user settings) are now loaded later, after WordPress 6.4.5 finished logging the user in.

This should resolve a lot of oddities we are seeing in testing with user settings.

Update : 2025.01.06 (Jan 6th)

It does not look like the admin-settings-tab.js file is being loaded in the new SaaS interface, likely due to changing of the menu position which changes the WordPress hook name for the page.

This is very fragile.

The new hook name is toplevel_page_slp_experience

The scripts are enqueued from a WP hook that calls

Related

See saving/changing style id.