WordPress / SaaS Menu Ordering

WordPress

add_menu_page()


add_menu_page( 
string $page_title, 
string $menu_title, 
string $capability, 
string $menu_slug, 
callable $callback = ”, 
string $icon_url = ”, 
int|float $position = null 
): string
WordPress Default Admin Menu Positions
StandardNetwork Admin
2 – Dashboard2 – Dashboard
4 – Separator4 – Separator
5 – Posts5 – Sites
10 – Media10 – Users
15 – Links15 – Themes
20 – Pages20 – Plugins
25 – Comments25 – Settings
30 – Updates
59 – Separator
60 – Appearance
65 – Plugins
70 – Users
75 – Tools
80 – Settings
99 – Separator99 – Separator

add_submenu_page()

SaaS Menu Positions

Standard / UserNetwork Admin
1.10 – MySLP
myslp-dashboard
1.10 – Manage
MYSLP_MANAGE_MENU_SLUG
‘myslp-manage-menu’

10 – Customers
30 – History Log
50 – Cron : System
51 – Cron : User
80 – Database
70 – Addressess
1.20 – Store Locator Plus®
csl-slplus
1.30 – Config
MYSLP_CONFIG_MENU_SLUG
‘myslp-config-menu’

10 – Plans
15 – Plan Limits
20 – Plugins
25 – Email Settings
30 – Payments
70 – System Settings
80- Cache
2 – Dashboard2 – Dashboard
4 – Separator4 – Separator
5 – Posts5 – Sites
10 – Media10 – Users
15 – Links15 – Themes
20 – Pages20 – Plugins
25 – Comments25 – Settings
30 – Updates
59 – Separator
60 – Appearance
65 – Plugins
70 – Users
75 – Tools
80 – Settings
99 – Separator99 – Separator

SITE_ID_CURRENT_SITE vs BLOG_ID_CURRENT_SITE

This is a code consistency/standard thing.

SITE_ID_CURRENT_SITE vs BLOG_ID_CURRENT_SITE vs BLOGID_CURRENT_SITE

Why different options?

MySLP uses a lot of SITE_ID_CURRENT_SITE.
looks to be related to get_network_option() calls.

Some places use BLOG_ID_CURRENT_SITE.
Mostly, but not exclusively, for swith_to_blog() calls.

Both are defined in the Docker composer and both are set to 1.

Looks to be a legacy thing where there was (is?) a plan to have a network (the SITE ID) of blogs (the blog ID). In our case they will be one and the same.

MySLP/v2/locations REST Route Registration Incorrect

Resolved in MySLP Dashboard 2502.05.01

[14-Nov-2024 19:52:23 UTC] PHP Notice:  Function register_rest_route was called incorrectly. 

The REST API route definition for myslp/v2/locations/(?P\d+) is missing the required permission_callback argument.

For REST API routes that are intended to be public, use __return_true as the permission callback.

MySLP_REST_API->register_routes(class WP_REST_Server { protected $namespaces = ['oembed/1.0' => [...], 'myslp/v2' => [...]]; protected $endpoints = ['/' => [...], '/batch/v1' => [...], '/oembed/1.0' => [...], '/oembed/1.0/embed' => [...], '/oembed/1.0/proxy' => [...], '/myslp/v2' => [...], '/myslp/v2/locations' => [...], '/myslp/v2/locations-limit' => [...]]; protected $route_options = []; protected $embed_cache = [] })
/var/www/html/wp-includes/class-wp-hook.php:324

register_rest_route($route_namespace = 'myslp/v2', $route = '/locations/(?P<id>\\d+)', $args = [0 => ['methods' => 'GET', 'callback' => [...], 'permission_callback' => class Closure { ... }], 1 => ['methods' => 'POST, PUT, PATCH', 'callback' => [...]], 2 => ['methods' => 'DELETE', 'callback' => [...], 'args' => [...]]], $override = *uninitialized*)
/var/www/html/wp-content/mu-plugins/myslp-dashboard/include/class.myslp.rest.api.php:110

Google Maps Dequeue/Deregister Incorrect Call

[14-Nov-2024 19:25:43 UTC] PHP Notice:  Function wp_dequeue_script was called incorrectly. Scripts and styles should not be registered or enqueued until the wp_enqueue_scripts, admin_enqueue_scripts, or login_enqueue_scripts hooks. This notice was triggered by the google_maps handle. Please see <a>Debugging in WordPress</a> for more information. (This message was added in version 3.3.0.) in /var/www/html/wp-includes/functions.php on line 6031

[14-Nov-2024 19:25:43 UTC] PHP Notice: Function wp_deregister_script was called incorrectly. Scripts and styles should not be registered or enqueued until the wp_enqueue_scripts, admin_enqueue_scripts, or login_enqueue_scripts hooks. This notice was triggered by the google_maps handle. Please see <a>Debugging in WordPress</a> for more information. (This message was added in version 3.3.0.) in /var/www/html/wp-includes/functions.php on line 6031

Locator Style Architecture

This setting is stored in the options_nojs[‘style’] option setting.

It is managed by an instantiation of the SLP_Settings_style_vision_list object.

  • data_field: options_nojs[style]
  • label: ‘Locator Style’
  • type: ‘style_vision_list’

This is a “smart option” managed by SLP_SmartOptions.
It is defined in \SLP_SmartOptions:view_appearance()
It has a ‘vue’ object type for rendering assistance.

Stack Trace : SLP | Settings

Triggered selecting – Menu : Store Locator Plus® | Settings

Most recent to oldest…

  • SLP_Settings_card_list.php:42, SLP_Settings_card_list->display()
    • SLP_Settings_Group.php:88, SLP_Settings_Group->display()
      • SLP_Settings_Section.php:94, SLP_Settings_Section->display()
        • SLP_Settings.php:384, SLP_Settings->render_settings_page()
          • SLP_Admin_Settings.php:289, SLP_Admin_Settings->display()
  • SLP_Settings_style_vision_list.php:15, SLP_Settings_style_vision_list->get_items()
    • SLP_Settings_card_list.php:28, SLP_Settings_card_list->at_startup()
      • SLP_Setting.php:48, SLP_Setting->initialize()
        • SLP_Admin_Settings.php:213, SLP_Admin_Settings->add_view()

Showing The Currently Selected Style

\SLP_Settings_card_list::display()

Renders the list of styles.

When $this->value === $item->clean_title it marks it as selected.

$item is from the list of locator styles fetched from the SLP locator style service (see \SLP_Settings_style_vision_list->items below).

Stack Trace : setting the initial value of the ‘style’ property (option)

\SLP_Setting->set_value() appears to have value already set to “”.

Track down the SLP_SmartOptions and how it loads the default values from options/options_nojs into the Smart Option properties.

  • SLP_SmartOptions.php:1784, SLP_SmartOptions->set_valid_option()
    • SLP_SmartOptions.php:1832, SLP_SmartOptions->set_valid_options_nojs()
      • SLP_SmartOptions.php:1661, array_walk()
        • SLP_SmartOptions.php:1661, SLP_SmartOptions->slp_specific_setup()
          • SLP_SmartOptions.php:1513, SLP_SmartOptions->initialize_after_plugins_loaded() SLPlus.php:386
            • SLPlus->initialize_after_plugins_loaded()

Class : SLP_Setting

Methods

set_value()

Sets the value of a setting.

Fetches the value of a setting from \WPOption_Manager::get_wp_option() if necessary.

Class : SLP_Settings_card_list

Extends SLP_Setting.

SLP_Setting type: “style_vision_list”

Class: SLP_Settings_style_vision_list

Extends SLP_Settings_card_list.

Properties

items : SLP_Setting_item[]

Contains the array of styles retrieved from the SLP Locator Styles service (REST call to external service).

value

Contains the currently set/selected style for the user.

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.

Categories Bug

Reproduce

  • Login as Super Admin (SA)
  • menu item : MySLP / Customers
  • switch to <???>
  • menu item: SLP / Categories

Report

See screen shot as how it shows in Staging , on the Categories page,

a bunch of script under locations but only the first one Named Distribution shows the script

[19-Nov-2024 15:11:30 UTC] 
PHP Deprecated:  Creation of dynamic property SLP_Power_Category_Manager::$wp_categories_by_id is deprecated 
in /var/www/html/wp-content/
plugins/slp-power/include/module/category/SLP_Power_Category_Manager.php 
on line 274

SaaS Hooks Calls

See Lance Cleveland’s WordPress Hooks Order Of Precedence.

Processing Modes

Base Loop

Generic WordPress + MySLP processing of a page request.

  • WordPress : muplugins_loaded
  • WordPress : registered_taxonomy
  • WordPress : registered_post_type
  • WordPress : plugins_loaded
  • WordPress : set_current_user
  • WordPress : init
    • SLP : slp_init_complete
      • SLP: start_slp_specific_setup
      • SLP: finish_slp_specific_setup
  • WordPress : parse_request
  • WordPress : rest_api_init
  • WordPress : send_headers

WordPress : plugins_loaded

Fires once activated plugins have loaded. The hook is generally used for immediate filter setup, or plugin overrides. The plugins_loaded action hook fires early, and precedes the setup_theme, after_setup_theme, init and wp_loaded action hooks.

Used By

  • MySLP_Dashboard_Controller :: load_hooks()
    • add_action( ‘plugins_loaded’, array( CLASS, ‘plugins_loaded’ ) );

WordPress : set_current_user

WordPress : init

Fires after WordPress has finished loading but before any headers are sent. Most of WP is loaded at this stage, and the user is authenticated.

Used By

  • Site_Architect::__construct()
    • add_action( ‘init’, array( $this, ‘sa_start_session’ ) );
  • SLP_Actions:initialize()
    • add_action( ‘init’, array( $this, ‘init’ ), 11 );

      Sets up the slplus->database object with slplus->database = SLP_Data::get_instance().
      This needs to happen AFTER the WoredPress:set_current_user is called so MySLP has the proper user meta to set the database attributes.
  • MySLP::add_hooks()
    • add_action( ‘init’, array( $this, ‘perform_init_actions’ ) );

User Logged In Traced Calls

  • Site_Architect::sa_start_session()
  • SLP_Actions::init()
  • MySLP::perform_init_actions()

SLP : slp_init_complete

Executed during WordPress : init hook via SLP_Actions::init() after all of the main SLP plugin init stuff has been executed.

Used By

  • \MySLP_Dashboard_Controller::load_hooks()
    • add_action( 'slp_init_complete', array( __CLASS__, 'slp_init_complete' ) );
  • SLP_BaseClass_Addon :: initialize()
    • add_action( ‘slp_init_complete’, array( $this, ‘slp_init’ ) );
  • SLP_Power_Pages :: add_hooks_and_filters()
    • add_action( ‘slp_init_complete’ , array( $this , ‘do_when_slp_ready’ ) , 11 ); // 11 ensures the base slp_init has run for the addon

WordPress : parse_request

Used By

  • WordPress : rest_api_loaded
    in wordpress/wp-includes/rest-api.php
    this eventually calls the REST custom endpoint permission_callback

WordPress : rest_api_init

Called when processing a REST API call.

slp_ajax_response

Used By

\SLP_Premier_AJAX::add_ajax_hooks()

Invoked By

\SLP_AJAX::renderJSON_Response()

slp_ajaxsql_fullquery

Used By

\SLP_Experience_AJAX::add_ajax_hooks()
\SLP_Premier_AJAX::add_ajax_hooks()

Invoked By

\SLP_AJAX::execute_location_query()

slp_rest_geocode_invalid_referer

Invoked By

\SLP_REST_Handler::valid_referer() for any site that is not coming from get_site_url() or get_home_url(). This is run via the WordPress REST API handler via the register_rest_route() for the readable request for geocoding at the wp-json/store-locator-plus/v2/geocode/<address> endpoint. This happens via the permission_callback property.

google.maps.Marker is deprecated

You will see this message in Generate Embed when reviewing the interface for any user.

As of February 21st, 2024, google.maps.Marker is deprecated. Please use google.maps.marker.AdvancedMarkerElement instead. At this time, google.maps.Marker is not scheduled to be discontinued, but google.maps.marker.AdvancedMarkerElement is recommended over google.maps.Marker. While google.maps.Marker will continue to receive bug fixes for any major regressions, existing bugs in google.maps.Marker will not be addressed. At least 12 months notice will be given before support is discontinued. Please see https://developers.google.com/maps/deprecations for additional details and https://developers.google.com/maps/documentation/javascript/advanced-markers/migration for the migration guide.

Dev Notes

https://storelocatorplus.youtrack.cloud/issue/SLP-14/google.maps.Marker-is-deprecated