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.

SaaS Returning Wrong Location List

Users are seeing the wrong locations when logged in, seeing the main super admin locations.

Reproduction

  • Login as a super admin (CiCi?)
  • Bring up the customer list and switch to any customer (bhinson)

The default location list is not correct. For bhinson it shows 2 locations that belong to super admin. bhinson has 0 locations.

Testing Update : 2024.11.14

  • Correct locations show up on MySLP menu, but NOT the SLP menu.

Dev Notes

SaaS is using a custom REST endpoint to find locations: …/myslp/v2/locations

This is running a custom interface in the MySLP Dashboard defined wp-content/plugins/myslp-dashboard/include/class.myslp.rest.api.php which fetches locations from include/location/MySLP_Location.php get_locations();

This leverages wp-content/plugins/store-locator-plus/include/module/location/SLP_Location_Utilities.php to provide the location data interface.

In MySLP_Locations the slptbl is set in __construct() which may be too early.
it is coming back as wp_store_locator which is incorrect
It appears switch_to_blog() has not yet been called.
The call stack at this point..

switch_to_blog is called in MySLP class via perform_init_actions()
which is attached to the WP hook ‘init’ with no order of precedence set

SLP not returning proper locations

The SLP Location Manager is rendered via SLP_Admin_Locations::display_manage_locations_table()
./wp-content/plugins/store-locator-plus/include/module/admin/SLP_Admin_Locations.php

It is pulling locations vis the $slplus->database property which is an instantiation of SLP_Data::
./wp-content/plugins/store-locator-plus/include/module/data/SLP_Data.php

SLP_Data::initialize is setting the SLP_Data::from_slp_table property via SLP_Data::set_database_meta()
This pulls from SLP_Data::db which is an instantiation of the WordPress wpdb class. The table is set by adding wpdb->prefix to the ‘store_locator’ fixed string.

wpdb->prefix is not pulling the proper user prefix , likely due to switch_to_blog() not being called yet.

Call Sequence

  • store-locator-plus.php is loaded…
  • slp_setup_environment() is called…
  • SLPLUS::initialize() is executed
    • $this->database = new SLP_Data()
  • SLP_Actions::initialize()
    • add_action( ‘init’, array( $this, ‘init’ ), 11 );
  • SLP_Actions::init() via WP init hook
  • MySLP::perform_init_actions()

Resolution

MySLP

The main MySLP dashboard loader (wp-content/plugins/myslp-dashboard/myslp-dashboard.php) was initializing the MySLP_Location class which immediate set the properties for SLP table and Extendo table names.

This needed to be deferred until after the MySLP::perform_init_actions() was called.

Added a new method in MySLP_Location::perform_init_actions() that sets the slp and extendo class properties. It is called from within the MySLP::perform_init_actions() to ensure proper timing on the reading of those table names.

SLP

Initialize the $slplus->data (SLP_Data) object AFTER the user login, performing the setup in the init action hook within SLP_Actions.

Stripe Critical Error While Viewing Profile

Non-SA user, go to view profile.

https://local.storelocatorplus.com/bhinson_at_ccgpro_dot_com/wp-admin/admin.php?page=myslp_profile

Fatal error: Uncaught Error sending request to Stripe: (Status 404) (Request req_yEwVtdefl7oeir) 
No upcoming invoices for customer: cus_QqKOolopTiTwM5 Stripe\Exception\InvalidRequestException: 

No upcoming invoices for customer: cus_QqKOolopTiTwM5 in /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/Exception/ApiErrorException.php:38 Stack trace: #0 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/Exception/InvalidRequestException.php(35): Stripe\Exception\ApiErrorException::factory('No upcoming inv...', 404, '{\n "error": {\n...', Array, Object(Stripe\Util\CaseInsensitiveArray), 'invoice_upcomin...') #1 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/ApiRequestor.php(230): Stripe\Exception\InvalidRequestException::factory('No upcoming inv...', 404, '{\n "error": {\n...', Array, Object(Stripe\Util\CaseInsensitiveArray), 'invoice_upcomin...', NULL) #2 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/ApiRequestor.php(191): Stripe\ApiRequestor::_specificV1APIError('{\n "error": {\n...', 404, Object(Stripe\Util\CaseInsensitiveArray), Array, Array) #3 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/ApiRequestor.php(637): Stripe\ApiRequestor->handleErrorResponse('{\n "error": {\n...', 404, Object(Stripe\Util\CaseInsensitiveArray), Array, 'v1') #4 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/ApiRequestor.php(136): Stripe\ApiRequestor->_interpretResponse('{\n "error": {\n...', 404, Object(Stripe\Util\CaseInsensitiveArray), 'v1') #5 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/ApiOperations/Request.php(111): Stripe\ApiRequestor->request('get', '/v1/invoices/up...', Array, Array, 'v1', Array) #6 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/Invoice.php(407): Stripe\ApiResource::_staticRequest('get', '/v1/invoices/up...', Array, NULL) #7 /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/MySLP_Stripe_Payments.php(416): Stripe\Invoice::upcoming(Array) #8 /var/www/html/wp-content/plugins/myslp-customer-profile/views/view.profile-subscription.php(20): stripe\MySLP_Stripe_Payments->get_upcoming_invoice('cus_QqKOolopTiT...') #9 /var/www/html/wp-content/plugins/myslp-customer-profile/views/view.profile.php(80): include('/var/www/html/w...') #10 /var/www/html/wp-content/plugins/myslp-customer-profile/include/MySLP_Customer_Profile.php(259): require_once('/var/www/html/w...') #11 /var/www/html/wp-includes/class-wp-hook.php(324): MySLP_Customer_Profile->page_profile('') #12 /var/www/html/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters('', Array) #13 /var/www/html/wp-includes/plugin.php(517): WP_Hook->do_action(Array) #14 /var/www/html/wp-admin/admin.php(259): do_action('toplevel_page_m...') #15 {main} thrown in /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/Exception/ApiErrorException.php on line 38
[19-Nov-2024 19:52:42 UTC] Stripe Notice: Undefined property of Stripe\Invoice instance: date

Payment Method undefined In SysAdmin View Profile

Deprecated: str_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 275

Reproduction

  • Login as SA
  • List Customers
  • Choose a customer , highlight the row and click “view profile” under the menu.

When the page renders the debug.log entry will show the message above.

Resolution

Stop Gap

A stop-gap measure is in place to default to text “stripe”, but we need to find out why the myslp->User object does not have a payment method set.

$payment_method = $myslp->User->payment_method ?? 'stripe';

Various PHP 8 Errors

Related To Blog ID Not Set

  • [08-Nov-2024 20:28:43 UTC] PHP Warning: Undefined variable $user_blog in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 387
  • [08-Nov-2024 20:28:43 UTC] PHP Warning: Attempt to read property “blog_id” on null in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 387
  • [08-Nov-2024 20:28:43 UTC] PHP Warning: Undefined variable $user_blog in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 391
  • [08-Nov-2024 20:28:43 UTC] PHP Warning: Attempt to read property “path” on null in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 391
  • [08-Nov-2024 20:28:43 UTC] PHP Warning: Undefined variable $user_blog in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 393
  • [08-Nov-2024 20:28:43 UTC] PHP Warning: Attempt to read property “path” on null in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 393
  • [08-Nov-2024 20:41:38 UTC] PHP Warning: Attempt to read property “path” on int in /var/www/html/wp-content/plugins/myslp-customer-maintenance/include/MySLP_SysAdmin_View_Profile.php on line 409

Invalid submenu call

  • [08-Nov-2024 20:28:43 UTC] PHP Deprecated: strip_tags(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/html/wp-admin/admin-header.php on line 36

Reproduction

  • Login as SA
  • Customers | All Users
  • Search CiCi
  • Hover and click profile
  • errors are in the debug.log

Resolution

Related To Blog ID Not Set

These errors are all related to an issue in the MySLP Customer Maintenance plugin where the $user_blog var was not set to a proper WP_Site object when the user was an SA.

Added a line to fetch WP_Site based on the $myslp->User->blog_id in those cases.

Invalid Submenu Call

This strip_tags() error is coming from WP core admin functions. Turns out the admin-header rendering expected a call to add_submenu_page to actually name a menu in the first param, not pass ” as a string. Apparently you need to attach all submenus to a parent menu of some kind.


Attached the View Profile admin page to the ‘users.php’ (Customers) menu as a submenu.

Deprecated Return Types In Stripe Library

Deprecated:
Return type of Stripe\Util\CaseInsensitiveArray::offsetExists($offset) 
Return type of Stripe\Util\CaseInsensitiveArray::offsetGet($offset)
Return type of Stripe\Util\CaseInsensitiveArray::offsetSet($offset, $value)
Return type of Stripe\Util\CaseInsensitiveArray::offsetUnset($offset)
Return type of Stripe\Util\CaseInsensitiveArray::count() 
Return type of Stripe\Util\CaseInsensitiveArray::getIterator() 
Return type of Stripe\Util\Set::getIterator() 
Return type of Stripe\StripeObject::offsetExists($k) 
Return type of Stripe\StripeObject::offsetGet($k) 
Return type of Stripe\StripeObject::offsetSet($k, $v) 
Return type of Stripe\StripeObject::offsetUnset($k) 
Return type of Stripe\StripeObject::count() 
Return type of Stripe\StripeObject::jsonSerialize() 
Return type of Stripe\Collection::getIterator()

Detailed Example

Deprecated: 
Return type of Stripe\Util\CaseInsensitiveArray::offsetExists($offset) 
should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, 
or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in 
/var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/Util/CaseInsensitiveArray.php on line 44 

Call Stack: 0.0001 464728 
1. {main}() /var/www/html/wp-admin/admin.php:0 0.4073 9234784 
2. do_action($hook_name = 'toplevel_page_myslp_profile') /var/www/html/wp-admin/admin.php:259 0.4073 9235000 
3. WP_Hook->do_action($args = [0 => '']) /var/www/html/wp-includes/plugin.php:517 0.4073 9235000 
4. WP_Hook->apply_filters($value = '', $args = [0 => '']) /var/www/html/wp-includes/class-wp-hook.php:348 0.4073 9235432 
5. MySLP_Customer_Profile->page_profile('') /var/www/html/wp-includes/class-wp-hook.php:324 0.4079 9250552 
6. require_once('/var/www/html/wp-content/plugins/myslp-customer-profile/views/view.profile.php') /var/www/html/wp-content/plugins/myslp-customer-profile/include/MySLP_Customer_Profile.php:259 0.4079 9250552 
7. MySLP_Recurring_Payments->get_subscription_details($user_id = 592, $force = TRUE) /var/www/html/wp-content/plugins/myslp-customer-profile/views/view.profile.php:19 0.4113 9252720 
8. MySLP_Stripe_Payments->get_subscription_details($subscription_id = 'sub_Gycmzo5SSaE1Wd') /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/MySLP_Recurring_Payments.php:154 0.4113 9252720 
9. MySLP_Stripe_Payments->load_lib() /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/MySLP_Stripe_Payments.php:418 0.4115 9253216 
10. require_once('/var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/init.php') /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/MySLP_Stripe_Payments.php:435 0.4119 9253760 
11. require('/var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/lib/Util/CaseInsensitiveArray.php') /var/www/html/wp-content/mu-plugins/myslp-payments/include/module/stripe/init.php:7 
Deprecated: Return type of Stripe\Util\CaseInsensitiveArray::offsetGet($offset)

System Info

Dev Notes

Related

Resolution

The Stripe API library included in the myslp-payments plugin was outdated.

This was already updated and in the develop branch, however the staging branch in myslp-payments was not updated to point to this latest updated.

  1. Merged the develop branch into staging on the myslp-payments repo (ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/myslp-payments) and pushed up to AWS CodeCommit.
  2. Check out staging branch on the myslp_aws_ecs_kit on local dev box.
    • Ran ./tools/update_code.sh on the local dev box.

This pushed code and committed it to the staging branch on the myslp_aws_ecs kit. This triggered CodePipline to build the new ECR image , fetching the staging branch on the myslp_aws_ecs_kit and all submodules before performing the build.

New Stylesheet for Feeding America San Diego

They are looking for a Store Locator Plus® locator style that matches the UX on Oregon Food Bank.

“…since most of the people viewing our map are looking at it from their phone that is the priority”

Our current map has several layout issues including cut off of the search bar and overflow of location information on mobile (examples attached.)

We would like the primary colors of the map to be:

Blue: HEX#005487
Orange: HEX#DE7C00

Oregon Food Bank : Mobile

Oregon Food Bank : Desktop

Requested by Feeding America San Diego (dwilliams_at_).

Dev Notes

Blockers

UX Improvement Ideas

Store Locator Plus® Staging Embedded Map

ECS Container Cannot Connect To Internet

cables in a tunnel by chatgpt

Recently stood up a new ECS container for the Store Locator Plus® staging app. We can connect to the app and login, but the app cannot talk to the outside world. It is not connecting to WordPress news service and cannot validate a WP SMTP Pro license.

This is the notebook for resolving that issue.

Research

Use Private Subnets : Stack Overflow

A Stack Overflow article that nearly describes our situation.

You are using the awsvpc network mode. This means that each ECS task gets its own Elastic Network Interface (ENI). With this configuration, the ECS tasks do not use the underlying EC2 instance’s network connection, they have their own network connection, with their own IP addresses.

  • You are currently disabling public IP assignment to your ECS tasks in the ECS service network_configuration block. You will need to change assign_public_ip to true in order to have ECS assign public IP addresses to the ECS Task’s ENIs, so that the ECS tasks can access resources outside of the VPC.
    • I forgot you can’t use public IP with awsvpc ECS deployed to EC2. You can only do that with Fargate deployments. So your options are to use a different network mode, so you can use the EC2 instance’s public IP from your ECS task: docs.aws.amazon.com/AmazonECS/latest/bestpracticesguide/… or switch to private subnets and a NAT Gateway.

Amazon ECS Networking Documents

Enable VPC internet access using internet gateways

“An internet gateway enables resources in your public subnets (such as EC2 instances) to connect to the internet if the resource has a public IPv4 address or an IPv6 address. “

Configuration for Internet Access

  • Add a route to the route table for the subnet that directs internet-bound traffic to the internet gateway.
  • Ensure that instances in your subnet have a public IPv4 address or IPv6 address. For more information, see Instance IP addressing in the Amazon EC2 User Guide.
  • Ensure that your security groups and network access control lists allow the desired internet traffic to flow to and from your instances.

Connect Amazon ECS applications to the internet

ECS Stack

EC2 Instance

  • ID i-0a1fa80a12dda9903
  • VPC: vpc-1b5e2c7c (slp-cluster-vpc)
    • Route Table: rtb-f0a96096 (slp-cluster-default-router)
      • 0.0.0.0/0 => igw-ab334cf
  • Security Group: sg-020cdb78 (default)
  • Network ACL: acl-e088aa87 (slp-cluster-acl)
  • EC2 Subnet: subnet-7b232951 (slp-cluster-east-1c)

ECS Service Details

Task Definition: slp_saas_staging:4
arn:aws:ecs:us-east-1:744590032041:task-definition/slp_saas_staging:4

Load Balancer: application load balancer myslp-staging-alb
arn:aws:elasticloadbalancing:us-east-1:744590032041:loadbalancer/app/myslp-staging-alb/2eae5893f2db5c1b

Target Group: ecs-myslp-staging-target
arn:aws:elasticloadbalancing:us-east-1:744590032041:targetgroup/ecs-myslp-staging-target/331cd16e4b3c52e1

VPC: slp-cluster-vpc

From AWS Support

Summary of problem

ECS instances using AWSVPC have no public IP address on the running instance/container and thus cannot route through the Internet Gateway despite it being on the VPC and subnet where the EC2 instance and container are attached.

Resolution Summary

To resolve this issue, we need to separate the subnets for your ECS tasks and the ALB, and configure the routing appropriately.

Keep Existing ALB / IG Subnets Unchanged

Keep the existing public subnet(s) for your ALB unchanged, with the Internet Gateway attached.

myslp-staging-alb

DNS Name: myslp-staging-alb-1533129727.us-east-1.elb.amazonaws.com

VPC: slp-cluster-vpc (vpc-1b5e2c7c)

Subnets:
slp-cluster-east-1a : subnet-7c8a8124 us-east-1a (use1-az6)
slp-cluster-east-1c : subnet-7b232951 us-east-1c (use1-az2)
slp-cluster-east-1d : subnet-5213e91b us-east-1d (use1-az4)
slp-cluster-east-1e : subnet-d00210ed us-east-1e (use1-az3)

Internet Gateway: slp-cluster-gateway (igw-ab3d34cf)
attached to slp-cluster-vpc

Create A NAT Gateway

Create New Private Subnets To Match The Public Subnets

Create A New Route Table And Associate With The Private Subnets

And add a route to the general internet (0.0.0.0/0) that goes through the NAT gateway.

ECS Service Changes

Note: You cannot update the network configuration of an existing ECS service. Therefore, you need to recreate the service.

Put the new service on the private subnets only.

Autoscaling Group Updates

Update the auto scaling group to add the private subnet with the NAT gateway.

Resolution Summary

The container using AWSVPC will not have a public IP address. That means the automatic routing for outbound connections will never use the Internet Gateway.

You need to setup a VPC with a public subnet (we have 4 zones , A C D E) and private subnet in those same zones.

The cluster will setup an automatic scaling group, something like Infra-ECS-Cluster….* which will define the Auto Scaling group via the infrastructure subcomponent of the cluster.

The Auto Scaling Group(ASG) needs to include both the private and public subnets.

The EC2 instances it spins up can be in the private subnet (let ASG decide).

The cluster service will setup the application load balancer (ALB) and target group. The service must be placed in the private subnet only. This will ensure the subsequent tasks (container instances) run on the private subnet. The ALB that is created must be assigned to the public subnets on the VPC to allow general inbound traffic from the internet to find its way over to the container on the private subnet. As a side note, the target group listens on HTTPS port 443 and routes to the container HTTP port 80. Use the service to create the ALB and target groups.

On the VPC…

Make sure the default routing table is explicitly assigned to the public subnets and NOT the private subnets.

Create an Internet Gateway (IG) and attach it to the VPC. This will allow inbound traffic from the internet to any service on the VPC with a public IP, in our case the application load balancer listener on port 443.

Create a NAT Gateway. Assign all the private subnets to be part of the NAT Gateway.

Create a second routing table and assign the private subnets to this table. Add a route to 0.0.0.0/0 that goes through the NAT Gateway.

If there are any tasks, containers, or EC2 instances already running stop and reinstantiate each of them. If the service was created originally on the public subnet of the VPC it will need to be deleted and recreated on the private VPC subnets.