From d7e3500f4ccfa476467051355a96e9e23cfb1faa Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 11:56:24 +0300 Subject: [PATCH 01/22] Save network site details from checkout to order meta --- pmpro-network.php | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pmpro-network.php b/pmpro-network.php index d7aa1df..6db5982 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -182,6 +182,43 @@ function pmpron_pmpro_checkout_boxes() } add_action('pmpro_checkout_boxes', 'pmpron_pmpro_checkout_boxes'); +/** + * Save site details to order meta when an order is added for a network site level. + * + * @since TBD + * + * @param MemberOrder $order The order object. + */ +function pmpron_pmpro_added_order( $order ) { + global $current_user, $pmpro_network_non_site_levels; + + // If we don't have an order, bail. + if ( empty( $order ) || empty( $order->id ) ) { + return; + } + + // If the order level is not set or is in the non site levels array, bail. + if ( empty( $order->membership_id ) || in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) { + return; + } + + // Get site name, site title, and blog id from request. + $sitename = ! empty( $_REQUEST['sitename'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitename'] ) ) : ''; + $sitetitle = ! empty( $_REQUEST['sitetitle'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitetitle'] ) ) : ''; + $blog_id = ! empty( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : ''; + + // Missing network site checkout params, bail. + if ( empty( $sitename ) && empty( $blog_id ) ) { + return; + } + + // Save site details to order meta for use later. + update_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', $sitename ); + update_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', $sitetitle ); + update_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', $blog_id ); +} +add_action( 'pmpro_added_order', 'pmpron_pmpro_added_order' ); + //update the user after checkout function pmpron_update_site_after_checkout( $user_id, $order ) { From 1ea6de7041677a32a5b654f046682b3d17d52fc9 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 13:22:32 +0300 Subject: [PATCH 02/22] Add $user_id param to pmpron_addSite, docblock, WPCS --- pmpro-network.php | 85 ++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 6db5982..7814a53 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -392,53 +392,68 @@ function pmpron_pmpro_membership_level_after_other_settings() { } add_action( 'pmpro_membership_level_after_other_settings', 'pmpron_pmpro_membership_level_after_other_settings' ); -/* - Function to add a site. - Takes sitename and sitetitle - Returns blog_id -*/ -function pmpron_addSite($sitename, $sitetitle) -{ +/** + * Function to add a site. + * + * @since unknown + * @since TBD Added $user_id arg. + * + * @param string $sitename The name of the site to add. + * @param string $sitetitle The title of the site to add. + * + * @return bool|WP_Error The blog id of the site on success, WP_Error on failure. + */ +function pmpron_addSite( $sitename, $sitetitle, $user_id = null ) { global $current_user, $current_site; - - //figure out the new domain + + // If no user ID was provided, default to the current user. + if ( empty( $user_id ) ) { + $user = $current_user; + } else { + $user = get_userdata( $user_id ); + } + + // Figure out the new domain. $site_domain = preg_replace( '|^www\.|', '', $current_site->domain ); - if ( !is_subdomain_install() ) - { + if ( ! is_subdomain_install() ) { $site = $current_site->domain; $path = $current_site->path . $sitename; - } - else - { + } else { $site = $sitename . '.' . $site_domain; $path = $current_site->path; } - //alright create the blog - $meta = apply_filters('signup_create_blog_meta', array ('lang_id' => 'en', 'public' => 0)); - $blog_id = wpmu_create_blog($site, $path, $sitetitle, $current_user->ID, $meta); - - do_action("pmpro_network_new_site", $blog_id, $current_user->ID); + // Alright create the blog. + $meta = apply_filters( + 'signup_create_blog_meta', + array( + 'lang_id' => 'en', + 'public' => 0, + ) + ); + $blog_id = wpmu_create_blog( $site, $path, $sitetitle, $user->ID, $meta ); - if ( is_a($blog_id, "WP_Error") ) { - return new WP_Error('blogcreate_failed', __('ERROR: Site creation failed.')); + do_action( 'pmpro_network_new_site', $blog_id, $user->ID ); + + if ( is_a( $blog_id, 'WP_Error' ) ) { + return new WP_Error( 'blogcreate_failed', __( 'ERROR: Site creation failed.' ) ); } - - //save array of all blog ids - $blog_ids = pmpron_getBlogsForUser($current_user->ID); - if(!in_array($blog_id, $blog_ids)) - { + + // Save array of all blog ids. + $blog_ids = pmpron_getBlogsForUser( $user->ID ); + if ( ! in_array( $blog_id, $blog_ids ) ) { $blog_ids[] = $blog_id; - update_user_meta($current_user->ID, "pmpron_blog_ids", $blog_ids); - - //if this is the first site, set it as the main site - if(count($blog_ids) == 1) - update_user_meta($current_user->ID, "pmpron_blog_id", $blog_id); - } - - do_action('wpmu_activate_blog', $blog_id, $current_user->ID, $current_user->user_pass, $sitetitle, $meta); - + update_user_meta( $user->ID, 'pmpron_blog_ids', $blog_ids ); + + // If this is the first site, set it as the main site. + if ( count( $blog_ids ) === 1 ) { + update_user_meta( $user->ID, 'pmpron_blog_id', $blog_id ); + } + } + + do_action( 'wpmu_activate_blog', $blog_id, $user->ID, $user->user_pass, $sitetitle, $meta ); + return $blog_id; } From ff079d54a73367ae159da37e1313d3e0404e6b35 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 13:29:46 +0300 Subject: [PATCH 03/22] Remove PayPal Express specific field storage Saving in order meta now --- pmpro-network.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 7814a53..ab7779f 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -457,18 +457,6 @@ function pmpron_addSite( $sitename, $sitetitle, $user_id = null ) { return $blog_id; } -/* -These bits are required for PayPal Express only. -*/ -function pmpron_pmpro_paypalexpress_session_vars() -{ - //save our added fields in session while the user goes off to PayPal - $_SESSION['sitename'] = $_REQUEST['sitename']; - $_SESSION['sitetitle'] = $_REQUEST['sitetitle']; - $_SESSION['blog_id'] = $_REQUEST['blog_id']; -} -add_action("pmpro_paypalexpress_session_vars", "pmpron_pmpro_paypalexpress_session_vars"); - //require the fields and check for dupes function pmpron_pmpro_registration_checks($pmpro_continue_registration) { From a30250f332da04b6146270ed78fd813c922aeabb Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 15:21:44 +0300 Subject: [PATCH 04/22] Refactor update site function to use order meta values --- pmpro-network.php | 104 +++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 57 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index ab7779f..769c5ce 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -219,70 +219,60 @@ function pmpron_pmpro_added_order( $order ) { } add_action( 'pmpro_added_order', 'pmpron_pmpro_added_order' ); -//update the user after checkout -function pmpron_update_site_after_checkout( $user_id, $order ) -{ - global $current_user, $current_site, $pmpro_network_non_site_levels; - - if(isset($_REQUEST['sitename'])) - { - //new site, on-site checkout - $sitename = $_REQUEST['sitename']; - $sitetitle = $_REQUEST['sitetitle']; - if(!empty($_REQUEST['blog_id'])) - $blog_id = intval($_REQUEST['blog_id']); +/** + * Update the user after checkout + * + * @since unknown + * @since TBD Fetching site details from order meta instead of request/session + * + * @param int $user_id The ID of the user who completed checkout. + * @param MemberOrder $order The order object. + */ +function pmpron_update_site_after_checkout( $user_id, $order ) { + global $current_user, $current_site, $pmpro_network_non_site_levels; + + // If we don't have an order, bail. + if ( empty( $order ) || empty( $order->id ) ) { + return; } - elseif(isset($_REQUEST['blog_id'])) - { - //reclaiming, on-site checkout - $blog_id = intval($_REQUEST['blog_id']); + + // Membership level ID not set, or completed checkout is for a non-network site level, bail. + if ( empty( $order->membership_id ) || in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) { + return; } - elseif(isset($_SESSION['sitename'])) - { - //new site, off-site checkout - $sitename = $_SESSION['sitename']; - $sitetitle = $_SESSION['sitetitle']; - if(!empty($_SESSION['blog_id'])) - $blog_id = intval($_SESSION['blog_id']); - } - elseif(isset($_SESSION['blog_id'])) - { - //reclaiming, off-site checkout - $blog_id = intval($_SESSION['blog_id']); + + // Pull site details from order. + $sitename = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', true ); + $sitetitle = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', true ); + $blog_id = get_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', true ); + + // No network site details in the order, bail. + if ( empty( $sitename ) && empty( $blog_id ) ) { + return; } - - $r = false; //default return value - - if(!empty($blog_id)) - { - //reclaiming, first check that this id is associated with the user - $all_blog_ids = pmpron_getBlogsForUser($user_id); - if(in_array($blog_id, $all_blog_ids)) - { - //activate the blog + + if ( ! empty( $blog_id ) ) { + // Reclaiming, first check that this id is associated with the user. + $all_blog_ids = pmpron_getBlogsForUser( $user_id ); + if ( in_array( $blog_id, $all_blog_ids ) ) { + // Activate the blog. update_blog_status( $blog_id, 'deleted', '0' ); do_action( 'activate_blog', $blog_id ); - $r = true; - } - else - { - //uh oh, were they trying to claim someone else's blog? - $r = new WP_Error('pmpron_reactivation_failed', __('ERROR: Site reactivation failed.')); + } else { + // Someone else's blog, not reactivated. Write to order notes. + /* translators: %d: Numeric Blog ID. */ + $order->notes .= sprintf( __( 'Site reactivation failed. Blog ID: %d.', 'pmpro-network' ), $blog_id ); + $order->saveOrder(); + } + } elseif ( pmpron_getSiteCredits( $order->membership_id ) > 0 ) { + $blog_id = pmpron_addSite( $sitename, $sitetitle, $user_id ); + if ( is_wp_error( $blog_id ) ) { + // Error activating the blog. Write to order notes. + /* translators: %1$s: Network Site Name, %2$s: Network Site Title. */ + $order->notes .= sprintf( __( 'Site creation failed. Site Name: %1$s. Site Title: %2$s.', 'pmpro-network' ), $sitename, $sitetitle ); + $order->saveOrder(); } } - elseif( ! empty( $order->membership_id ) && ! in_array( $order->membership_id, $pmpro_network_non_site_levels ) && pmpron_getSiteCredits( $order->membership_id ) > 0 ) - { - $blog_id = pmpron_addSite($sitename, $sitetitle); - if(is_wp_error($blog_id)) - $r = $blog_id; - } - - //clear session vars - unset($_SESSION['sitename']); - unset($_SESSION['sitetitle']); - unset($_SESSION['blog_id']); - - return $r; } add_action( 'pmpro_after_checkout', 'pmpron_update_site_after_checkout', 10, 2 ); From afcbe17263ddd7279bd4b3cd72ee8671ee4383c3 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 15:41:13 +0300 Subject: [PATCH 05/22] Bail from blog creation if no user, docblock update --- pmpro-network.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pmpro-network.php b/pmpro-network.php index 769c5ce..0bb608e 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -390,8 +390,9 @@ function pmpron_pmpro_membership_level_after_other_settings() { * * @param string $sitename The name of the site to add. * @param string $sitetitle The title of the site to add. + * @param int $user_id The user ID. * - * @return bool|WP_Error The blog id of the site on success, WP_Error on failure. + * @return mixed blog id (int) on success, WP_Error on blog creation failure, false if no valid user. */ function pmpron_addSite( $sitename, $sitetitle, $user_id = null ) { global $current_user, $current_site; @@ -403,6 +404,11 @@ function pmpron_addSite( $sitename, $sitetitle, $user_id = null ) { $user = get_userdata( $user_id ); } + // No user, bail. + if ( empty( $user ) ) { + return false; + } + // Figure out the new domain. $site_domain = preg_replace( '|^www\.|', '', $current_site->domain ); From 944d916529cd21e285e111967a7a4b74139b0556 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 15:46:12 +0300 Subject: [PATCH 06/22] Check for error or empty blog_id Update pmpron_update_site_after_checkout to consider an empty $blog_id as an error when creating network sites. The code now checks is_wp_error($blog_id) || empty($blog_id) and adds an order note on failure, preventing silent success when pmpron_addSite returns an empty/false value. --- pmpro-network.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmpro-network.php b/pmpro-network.php index 0bb608e..19a3eca 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -266,7 +266,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { } } elseif ( pmpron_getSiteCredits( $order->membership_id ) > 0 ) { $blog_id = pmpron_addSite( $sitename, $sitetitle, $user_id ); - if ( is_wp_error( $blog_id ) ) { + if ( is_wp_error( $blog_id ) || empty( $blog_id ) ) { // Error activating the blog. Write to order notes. /* translators: %1$s: Network Site Name, %2$s: Network Site Title. */ $order->notes .= sprintf( __( 'Site creation failed. Site Name: %1$s. Site Title: %2$s.', 'pmpro-network' ), $sitename, $sitetitle ); From afaf9c6f77810dfc2f37690fa098d4349c3ce9bf Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 17:15:56 +0300 Subject: [PATCH 07/22] Move pmpro_network_new_site after error check Ensure the pmpro_network_new_site action is only fired when wpmu_create_blog succeeds. The hook was previously triggered before checking for a WP_Error, which could cause the action to run on failed site creation. --- pmpro-network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 19a3eca..d786429 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -430,12 +430,12 @@ function pmpron_addSite( $sitename, $sitetitle, $user_id = null ) { ); $blog_id = wpmu_create_blog( $site, $path, $sitetitle, $user->ID, $meta ); - do_action( 'pmpro_network_new_site', $blog_id, $user->ID ); - if ( is_a( $blog_id, 'WP_Error' ) ) { return new WP_Error( 'blogcreate_failed', __( 'ERROR: Site creation failed.' ) ); } + do_action( 'pmpro_network_new_site', $blog_id, $user->ID ); + // Save array of all blog ids. $blog_ids = pmpron_getBlogsForUser( $user->ID ); if ( ! in_array( $blog_id, $blog_ids ) ) { From 2a74e6c55e254c124f9faa5866532b26b1b76a12 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 17:27:10 +0300 Subject: [PATCH 08/22] Remove unused globals Remove the unused $current_user from the global declarations in pmpron_pmpro_added_order and pmpron_update_site_after_checkout in pmpro-network.php. --- pmpro-network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index d786429..088d8d0 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -190,7 +190,7 @@ function pmpron_pmpro_checkout_boxes() * @param MemberOrder $order The order object. */ function pmpron_pmpro_added_order( $order ) { - global $current_user, $pmpro_network_non_site_levels; + global $pmpro_network_non_site_levels; // If we don't have an order, bail. if ( empty( $order ) || empty( $order->id ) ) { @@ -229,7 +229,7 @@ function pmpron_pmpro_added_order( $order ) { * @param MemberOrder $order The order object. */ function pmpron_update_site_after_checkout( $user_id, $order ) { - global $current_user, $current_site, $pmpro_network_non_site_levels; + global $current_site, $pmpro_network_non_site_levels; // If we don't have an order, bail. if ( empty( $order ) || empty( $order->id ) ) { From 2bf192d76e3fe68cd686d394632f2fce7fbad335 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 17:34:58 +0300 Subject: [PATCH 09/22] Append newline to order notes --- pmpro-network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 088d8d0..cf480c3 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -261,7 +261,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { } else { // Someone else's blog, not reactivated. Write to order notes. /* translators: %d: Numeric Blog ID. */ - $order->notes .= sprintf( __( 'Site reactivation failed. Blog ID: %d.', 'pmpro-network' ), $blog_id ); + $order->notes .= sprintf( __( 'Site reactivation failed. Blog ID: %d.', 'pmpro-network' ), $blog_id ) . "\n"; $order->saveOrder(); } } elseif ( pmpron_getSiteCredits( $order->membership_id ) > 0 ) { @@ -269,7 +269,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { if ( is_wp_error( $blog_id ) || empty( $blog_id ) ) { // Error activating the blog. Write to order notes. /* translators: %1$s: Network Site Name, %2$s: Network Site Title. */ - $order->notes .= sprintf( __( 'Site creation failed. Site Name: %1$s. Site Title: %2$s.', 'pmpro-network' ), $sitename, $sitetitle ); + $order->notes .= sprintf( __( 'Site creation failed. Site Name: %1$s. Site Title: %2$s.', 'pmpro-network' ), $sitename, $sitetitle ) . "\n"; $order->saveOrder(); } } From 2a06f60d11bee790f8744ab9709597423fe03462 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 17:41:15 +0300 Subject: [PATCH 10/22] Add textdomain to error message Include the 'pmpro_network' text domain in the __() call for the site creation error message in pmpro-network.php so the string can be translated. --- pmpro-network.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmpro-network.php b/pmpro-network.php index cf480c3..395ca27 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -431,7 +431,7 @@ function pmpron_addSite( $sitename, $sitetitle, $user_id = null ) { $blog_id = wpmu_create_blog( $site, $path, $sitetitle, $user->ID, $meta ); if ( is_a( $blog_id, 'WP_Error' ) ) { - return new WP_Error( 'blogcreate_failed', __( 'ERROR: Site creation failed.' ) ); + return new WP_Error( 'blogcreate_failed', __( 'ERROR: Site creation failed.', 'pmpro_network' ) ); } do_action( 'pmpro_network_new_site', $blog_id, $user->ID ); From 57d514d848d8aa86226790d01e31fc6d323eef62 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 17:49:23 +0300 Subject: [PATCH 11/22] Handle falsy blog_id when creating site via manage sites page Just in case --- pages/manage-sites.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/manage-sites.php b/pages/manage-sites.php index b73e42c..340b7e7 100644 --- a/pages/manage-sites.php +++ b/pages/manage-sites.php @@ -43,7 +43,7 @@ function pmpron_manage_sites_shortcode($atts, $content=null, $code="") { if ( pmpron_checkSiteName( $sitename, $sitetitle ) ) { $blog_id = pmpron_addSite( $sitename, $sitetitle ); - if ( is_wp_error( $blog_id ) ) { + if ( is_wp_error( $blog_id ) || empty( $blog_id ) ) { $pmpro_msg = __( 'Error creating site.', 'pmpro-network' ); $pmpro_msgt = "pmpro_error"; } else { From 40592c7be11f33197688694a040d28ad3a7b4027 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 18:08:53 +0300 Subject: [PATCH 12/22] Save network site order meta only if set Remove early bail-out that required both sitename and blog_id. Instead, update order meta for pmpron_sitename, pmpron_sitetitle, and pmpron_blog_id only when each value is non-empty, avoiding storing empty meta values and allowing partial network site data to be saved. --- pmpro-network.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 395ca27..f8bfb98 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -207,15 +207,16 @@ function pmpron_pmpro_added_order( $order ) { $sitetitle = ! empty( $_REQUEST['sitetitle'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitetitle'] ) ) : ''; $blog_id = ! empty( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : ''; - // Missing network site checkout params, bail. - if ( empty( $sitename ) && empty( $blog_id ) ) { - return; - } - // Save site details to order meta for use later. - update_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', $sitename ); - update_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', $sitetitle ); - update_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', $blog_id ); + if ( ! empty( $sitename ) ) { + update_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', $sitename ); + } + if ( ! empty( $sitetitle ) ) { + update_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', $sitetitle ); + } + if ( ! empty( $blog_id ) ) { + update_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', $blog_id ); + } } add_action( 'pmpro_added_order', 'pmpron_pmpro_added_order' ); From 21847b9fecea350a6a2a725fb511b6e637f979ca Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 20 Mar 2026 18:14:49 +0300 Subject: [PATCH 13/22] Normalize blog_id default and fix textdomain --- pmpro-network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index f8bfb98..8378bd5 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -205,7 +205,7 @@ function pmpron_pmpro_added_order( $order ) { // Get site name, site title, and blog id from request. $sitename = ! empty( $_REQUEST['sitename'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitename'] ) ) : ''; $sitetitle = ! empty( $_REQUEST['sitetitle'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitetitle'] ) ) : ''; - $blog_id = ! empty( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : ''; + $blog_id = ! empty( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : 0; // Save site details to order meta for use later. if ( ! empty( $sitename ) ) { @@ -432,7 +432,7 @@ function pmpron_addSite( $sitename, $sitetitle, $user_id = null ) { $blog_id = wpmu_create_blog( $site, $path, $sitetitle, $user->ID, $meta ); if ( is_a( $blog_id, 'WP_Error' ) ) { - return new WP_Error( 'blogcreate_failed', __( 'ERROR: Site creation failed.', 'pmpro_network' ) ); + return new WP_Error( 'blogcreate_failed', __( 'ERROR: Site creation failed.', 'pmpro-network' ) ); } do_action( 'pmpro_network_new_site', $blog_id, $user->ID ); From 99dee717fc745fe86f3c07423c84ec3d23b4eacf Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 17 Apr 2026 14:15:40 +0300 Subject: [PATCH 14/22] Verifying the $pmpro_network_non_site_levels global is an array --- pmpro-network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 8378bd5..fcd21ea 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -198,7 +198,7 @@ function pmpron_pmpro_added_order( $order ) { } // If the order level is not set or is in the non site levels array, bail. - if ( empty( $order->membership_id ) || in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) { + if ( empty( $order->membership_id ) || ( ( is_array( $pmpro_network_non_site_levels ) && in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) ) ) { return; } @@ -238,7 +238,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { } // Membership level ID not set, or completed checkout is for a non-network site level, bail. - if ( empty( $order->membership_id ) || in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) { + if ( empty( $order->membership_id ) || ( ( is_array( $pmpro_network_non_site_levels ) && in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) ) ) { return; } From 59c211bbe3656dfbcd3a588889c45b5c1e7e852f Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Fri, 17 Apr 2026 14:19:47 +0300 Subject: [PATCH 15/22] Cast $blog_id to int --- pmpro-network.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmpro-network.php b/pmpro-network.php index fcd21ea..705a6e2 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -245,7 +245,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { // Pull site details from order. $sitename = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', true ); $sitetitle = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', true ); - $blog_id = get_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', true ); + $blog_id = absint( get_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', true ) ); // No network site details in the order, bail. if ( empty( $sitename ) && empty( $blog_id ) ) { From f00676f73de0d2ad045b945b1d80853069c7e018 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Mon, 20 Apr 2026 12:14:01 +0300 Subject: [PATCH 16/22] Log site creation failure reason --- pmpro-network.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 705a6e2..404f2c1 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -267,12 +267,12 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { } } elseif ( pmpron_getSiteCredits( $order->membership_id ) > 0 ) { $blog_id = pmpron_addSite( $sitename, $sitetitle, $user_id ); - if ( is_wp_error( $blog_id ) || empty( $blog_id ) ) { - // Error activating the blog. Write to order notes. - /* translators: %1$s: Network Site Name, %2$s: Network Site Title. */ - $order->notes .= sprintf( __( 'Site creation failed. Site Name: %1$s. Site Title: %2$s.', 'pmpro-network' ), $sitename, $sitetitle ) . "\n"; - $order->saveOrder(); + if ( is_wp_error( $blog_id ) ) { + $order->notes .= sprintf( __( 'Site creation error: %s', 'pmpro-network' ), $blog_id->get_error_message() ) . "\n"; + } elseif ( empty( $blog_id ) ) { + $order->notes .= __( 'Site creation failed: User not found.', 'pmpro-network' ) . "\n"; } + $order->saveOrder(); } } add_action( 'pmpro_after_checkout', 'pmpron_update_site_after_checkout', 10, 2 ); From 6797c282f22ec995d409d311b3c2a05b13776dc6 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Mon, 20 Apr 2026 12:21:15 +0300 Subject: [PATCH 17/22] Strict comparison for $blog_id --- pmpro-network.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmpro-network.php b/pmpro-network.php index 404f2c1..3f7baa5 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -255,7 +255,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { if ( ! empty( $blog_id ) ) { // Reclaiming, first check that this id is associated with the user. $all_blog_ids = pmpron_getBlogsForUser( $user_id ); - if ( in_array( $blog_id, $all_blog_ids ) ) { + if ( in_array( $blog_id, $all_blog_ids, true ) ) { // Activate the blog. update_blog_status( $blog_id, 'deleted', '0' ); do_action( 'activate_blog', $blog_id ); From d7237f9c18e69705da99733e6594ed558cb34c29 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Mon, 20 Apr 2026 12:41:37 +0300 Subject: [PATCH 18/22] Remove unused session reads --- pmpro-network.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 3f7baa5..c06c7fb 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -94,11 +94,6 @@ function pmpron_pmpro_checkout_boxes() $sitename = $_REQUEST['sitename']; $sitetitle = $_REQUEST['sitetitle']; } - elseif(!empty($_SESSION['sitename'])) - { - $sitename = $_SESSION['sitename']; - $sitetitle = $_SESSION['sitetitle']; - } else { $sitename = ''; $sitetitle = ''; From 9dc4404432bd9458c90ed0bcb7e7d4928139b0b1 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Mon, 20 Apr 2026 12:56:42 +0300 Subject: [PATCH 19/22] Sanitize $_REQUEST values --- pmpro-network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index c06c7fb..1fa68a6 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -91,8 +91,8 @@ function pmpron_pmpro_checkout_boxes() if(!empty($_REQUEST['sitename'])) { - $sitename = $_REQUEST['sitename']; - $sitetitle = $_REQUEST['sitetitle']; + $sitename = sanitize_text_field( wp_unslash( $_REQUEST['sitename'] ) ); + $sitetitle = sanitize_text_field( wp_unslash( $_REQUEST['sitetitle'] ) ); } else { $sitename = ''; From 5ddca844e5c94d8e309eaa54a183312269687b00 Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Mon, 20 Apr 2026 13:01:51 +0300 Subject: [PATCH 20/22] Remove strict comparison Values in $all_blog_ids are strings --- pmpro-network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 1fa68a6..49ebecf 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -240,7 +240,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { // Pull site details from order. $sitename = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', true ); $sitetitle = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', true ); - $blog_id = absint( get_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', true ) ); + $blog_id = get_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', true ); // No network site details in the order, bail. if ( empty( $sitename ) && empty( $blog_id ) ) { @@ -250,7 +250,7 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { if ( ! empty( $blog_id ) ) { // Reclaiming, first check that this id is associated with the user. $all_blog_ids = pmpron_getBlogsForUser( $user_id ); - if ( in_array( $blog_id, $all_blog_ids, true ) ) { + if ( in_array( $blog_id, $all_blog_ids ) ) { // Activate the blog. update_blog_status( $blog_id, 'deleted', '0' ); do_action( 'activate_blog', $blog_id ); From 6e6ac00c196aa64a3358556585ee96cb08c7dd3d Mon Sep 17 00:00:00 2001 From: David Wanjuki Date: Mon, 20 Apr 2026 13:15:45 +0300 Subject: [PATCH 21/22] Call saveOrder() only if notes added --- pmpro-network.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmpro-network.php b/pmpro-network.php index 49ebecf..172c0b6 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -264,10 +264,11 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { $blog_id = pmpron_addSite( $sitename, $sitetitle, $user_id ); if ( is_wp_error( $blog_id ) ) { $order->notes .= sprintf( __( 'Site creation error: %s', 'pmpro-network' ), $blog_id->get_error_message() ) . "\n"; + $order->saveOrder(); } elseif ( empty( $blog_id ) ) { $order->notes .= __( 'Site creation failed: User not found.', 'pmpro-network' ) . "\n"; + $order->saveOrder(); } - $order->saveOrder(); } } add_action( 'pmpro_after_checkout', 'pmpron_update_site_after_checkout', 10, 2 ); From 40011389f202b3d81a8ba396aad99fe5d4219ff7 Mon Sep 17 00:00:00 2001 From: David Parker Date: Thu, 30 Apr 2026 13:05:42 -0400 Subject: [PATCH 22/22] Use core checkout_request_vars instead of custom order meta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PMPro core (since 2.12.3) already persists $_REQUEST to order meta via pmpro_save_checkout_data_to_order() before offsite redirects and repopulates it via pmpro_pull_checkout_data_from_order() before pmpro_after_checkout fires. So sitename/sitetitle/blog_id are already available in $_REQUEST for PBC, PayPal Express, Stripe Checkout (return + webhook), and PayFast — no custom order meta storage needed. Removes pmpron_pmpro_added_order() and the pmpron_sitename/ pmpron_sitetitle/pmpron_blog_id meta keys. pmpron_update_site_after_checkout() now reads sanitized values from $_REQUEST directly. Co-Authored-By: Claude Opus 4.7 (1M context) --- pmpro-network.php | 56 +++++++++-------------------------------------- 1 file changed, 10 insertions(+), 46 deletions(-) diff --git a/pmpro-network.php b/pmpro-network.php index 172c0b6..dd3e8a2 100644 --- a/pmpro-network.php +++ b/pmpro-network.php @@ -177,55 +177,17 @@ function pmpron_pmpro_checkout_boxes() } add_action('pmpro_checkout_boxes', 'pmpron_pmpro_checkout_boxes'); -/** - * Save site details to order meta when an order is added for a network site level. - * - * @since TBD - * - * @param MemberOrder $order The order object. - */ -function pmpron_pmpro_added_order( $order ) { - global $pmpro_network_non_site_levels; - - // If we don't have an order, bail. - if ( empty( $order ) || empty( $order->id ) ) { - return; - } - - // If the order level is not set or is in the non site levels array, bail. - if ( empty( $order->membership_id ) || ( ( is_array( $pmpro_network_non_site_levels ) && in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) ) ) { - return; - } - - // Get site name, site title, and blog id from request. - $sitename = ! empty( $_REQUEST['sitename'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitename'] ) ) : ''; - $sitetitle = ! empty( $_REQUEST['sitetitle'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitetitle'] ) ) : ''; - $blog_id = ! empty( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : 0; - - // Save site details to order meta for use later. - if ( ! empty( $sitename ) ) { - update_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', $sitename ); - } - if ( ! empty( $sitetitle ) ) { - update_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', $sitetitle ); - } - if ( ! empty( $blog_id ) ) { - update_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', $blog_id ); - } -} -add_action( 'pmpro_added_order', 'pmpron_pmpro_added_order' ); - /** * Update the user after checkout * * @since unknown - * @since TBD Fetching site details from order meta instead of request/session + * @since TBD Site details are pulled from $_REQUEST (which PMPro core repopulates from order meta on offsite/delayed checkout returns). * * @param int $user_id The ID of the user who completed checkout. * @param MemberOrder $order The order object. */ function pmpron_update_site_after_checkout( $user_id, $order ) { - global $current_site, $pmpro_network_non_site_levels; + global $pmpro_network_non_site_levels; // If we don't have an order, bail. if ( empty( $order ) || empty( $order->id ) ) { @@ -233,16 +195,18 @@ function pmpron_update_site_after_checkout( $user_id, $order ) { } // Membership level ID not set, or completed checkout is for a non-network site level, bail. - if ( empty( $order->membership_id ) || ( ( is_array( $pmpro_network_non_site_levels ) && in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) ) ) { + if ( empty( $order->membership_id ) || ( is_array( $pmpro_network_non_site_levels ) && in_array( $order->membership_id, $pmpro_network_non_site_levels ) ) ) { return; } - // Pull site details from order. - $sitename = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitename', true ); - $sitetitle = get_pmpro_membership_order_meta( $order->id, 'pmpron_sitetitle', true ); - $blog_id = get_pmpro_membership_order_meta( $order->id, 'pmpron_blog_id', true ); + // Pull site details from $_REQUEST. For offsite/delayed checkout flows, PMPro core + // repopulates $_REQUEST from order meta via pmpro_pull_checkout_data_from_order() + // before pmpro_after_checkout fires. + $sitename = ! empty( $_REQUEST['sitename'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitename'] ) ) : ''; + $sitetitle = ! empty( $_REQUEST['sitetitle'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['sitetitle'] ) ) : ''; + $blog_id = ! empty( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : 0; - // No network site details in the order, bail. + // No network site details in the request, bail. if ( empty( $sitename ) && empty( $blog_id ) ) { return; }