Plugin Directory

Changeset 3722434 for s2member


Ignore:
Timestamp:
10/01/2026 05:36:17 AM (5 weeks ago)
Author:
clavaque
Message:

Releasing s2Member v261001

Location:
s2member/trunk
Files:
2 deleted
22 edited

Legend:

Unmodified
Added
Removed
  • s2member/trunk/languages/s2member.pot

    r3714948 r3722434  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version:  260927\n"
     5"Project-Id-Version:  261001\n"
    66"Report-Msgid-Bugs-To: http://wordpress.org/tag/s2member\n"
    7 "POT-Creation-Date: 2026-09-27 03:32:02+00:00\n"
     7"POT-Creation-Date: 2026-10-01 05:20:09+00:00\n"
    88"MIME-Version: 1.0\n"
    99"Content-Type: text/plain; charset=UTF-8\n"
    … …  
    13101310#: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:97
    13111311#: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:326
    1312 #: src/includes/classes/paypal-utilities.inc.php:635
    1313 #: src/includes/classes/paypal-utilities.inc.php:643
     1312#: src/includes/classes/paypal-utilities.inc.php:639
     1313#: src/includes/classes/paypal-utilities.inc.php:647
    13141314msgctxt "s2member-front"
    13151315msgid "Error #%1$s. %2$s."
    … …  
    13181318#: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:100
    13191319#: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:329
    1320 #: src/includes/classes/paypal-utilities.inc.php:509
    1321 #: src/includes/classes/paypal-utilities.inc.php:637
    1322 #: src/includes/classes/paypal-utilities.inc.php:645
     1320#: src/includes/classes/paypal-utilities.inc.php:513
     1321#: src/includes/classes/paypal-utilities.inc.php:641
     1322#: src/includes/classes/paypal-utilities.inc.php:649
    13231323msgctxt "s2member-front"
    13241324msgid "Error. Please contact Support for assistance."
    … …  
    21852185#: src/includes/classes/sc-paypal-button-in.inc.php:258
    21862186#: src/includes/classes/sc-paypal-button-in.inc.php:290
    2187 #: src/includes/classes/sc-paypal-button-in.inc.php:503
    2188 #: src/includes/classes/sc-paypal-button-in.inc.php:718
     2187#: src/includes/classes/sc-paypal-button-in.inc.php:525
     2188#: src/includes/classes/sc-paypal-button-in.inc.php:761
    21892189#: src/includes/templates/buttons/paypal-ccaps-checkout-button.php:35
    21902190#: src/includes/templates/buttons/paypal-checkout-button.php:47
    … …  
    45424542msgstr ""
    45434543
    4544 #: src/includes/classes/menu-pages.inc.php:975
     4544#: src/includes/classes/menu-pages.inc.php:977
     4545#: src/includes/classes/menu-pages.inc.php:1098
     4546msgid "Thanks — your message was sent. You’ll receive a copy by email."
     4547msgstr ""
     4548
     4549#: src/includes/classes/menu-pages.inc.php:980
     4550msgid "Sorry, your message could not be sent. Please try again."
     4551msgstr ""
     4552
     4553#: src/includes/classes/menu-pages.inc.php:1019
     4554msgid "You do not have permission to send support requests from this page."
     4555msgstr ""
     4556
     4557#: src/includes/classes/menu-pages.inc.php:1030
     4558msgid "Please provide a valid email address."
     4559msgstr ""
     4560
     4561#: src/includes/classes/menu-pages.inc.php:1033
     4562msgid "Please describe how I can help."
     4563msgstr ""
     4564
     4565#: src/includes/classes/menu-pages.inc.php:1048
     4566msgid "s2Member help request"
     4567msgstr ""
     4568
     4569#: src/includes/classes/menu-pages.inc.php:1087
     4570msgid "Support mailbox is not configured correctly."
     4571msgstr ""
     4572
    45454573#: src/includes/classes/menu-pages.inc.php:1096
    4546 msgid "Thanks — your message was sent. You’ll receive a copy by email."
    4547 msgstr ""
    4548 
    4549 #: src/includes/classes/menu-pages.inc.php:978
    4550 msgid "Sorry, your message could not be sent. Please try again."
    4551 msgstr ""
    4552 
    4553 #: src/includes/classes/menu-pages.inc.php:1017
    4554 msgid "You do not have permission to send support requests from this page."
    4555 msgstr ""
    4556 
    4557 #: src/includes/classes/menu-pages.inc.php:1028
    4558 msgid "Please provide a valid email address."
    4559 msgstr ""
    4560 
    4561 #: src/includes/classes/menu-pages.inc.php:1031
    4562 msgid "Please describe how I can help."
    4563 msgstr ""
    4564 
    4565 #: src/includes/classes/menu-pages.inc.php:1046
    4566 msgid "s2Member help request"
    4567 msgstr ""
    4568 
    4569 #: src/includes/classes/menu-pages.inc.php:1085
    4570 msgid "Support mailbox is not configured correctly."
    4571 msgstr ""
    4572 
    4573 #: src/includes/classes/menu-pages.inc.php:1094
    45744574msgid "Email could not be sent (mail server issue). Please try again or contact us directly."
    45754575msgstr ""
    … …  
    45914591#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:211
    45924592#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:222
    4593 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:266
    4594 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:277
     4593#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:269
     4594#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:280
    45954595#: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:167
    45964596#: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:178
    … …  
    46164616
    46174617#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:199
    4618 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:254
     4618#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:257
    46194619msgctxt "s2member-front"
    46204620msgid "Thank you! You've been updated to:"
    … …  
    46224622
    46234623#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:200
    4624 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:255
     4624#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:258
    46254625#: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:156
    46264626msgctxt "s2member-front"
    … …  
    46294629
    46304630#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:210
    4631 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:265
     4631#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:268
    46324632msgctxt "s2member-front"
    46334633msgid "<strong>ERROR:</strong> Unable to modify Subscription.<br />Please contact Support for assistance.<br /><br />The existing User ID is associated with an Administrator. Stopping here. Otherwise, an Administrator could lose access. Please make sure that you are NOT logged in as an Administrator while testing."
    … …  
    46354635
    46364636#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:221
    4637 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:276
     4637#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:279
    46384638msgctxt "s2member-front"
    46394639msgid "<strong>ERROR:</strong> Unable to modify Subscription.<br />Please contact Support for assistance.<br /><br />Could not get the existing User ID from the DB."
    … …  
    46414641
    46424642#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:232
    4643 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:388
     4643#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:391
    46444644#: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:199
    46454645#: src/includes/classes/paypal-return-in-web-accept-sp.inc.php:141
    … …  
    46494649
    46504650#: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:233
    4651 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:389
     4651#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:392
    46524652#: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:200
    46534653#: src/includes/classes/paypal-return-in-web-accept-sp.inc.php:142
    … …  
    46564656msgstr ""
    46574657
    4658 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:349
    4659 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:372
     4658#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:352
     4659#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:375
    46604660msgctxt "s2member-front"
    46614661msgid "<strong>Thank you! Your account has been approved.<br />The next step is to Register a Username for immediate access.</strong>"
    46624662msgstr ""
    46634663
    4664 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:350
    4665 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:373
     4664#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:353
     4665#: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:376
    46664666msgctxt "s2member-front"
    46674667msgid "Please Register Now (Click Here)"
    … …  
    47174717#. again`, or something to that affect. Or, if you prefer, you could Filter
    47184718#. ``$response["__error"]`` with `ws_plugin__s2member_paypal_api_response`.
    4719 #: src/includes/classes/paypal-utilities.inc.php:506
     4719#: src/includes/classes/paypal-utilities.inc.php:510
    47204720msgctxt "s2member-front"
    47214721msgid "Error #%1$s. %2$s. %3$s."
    47224722msgstr ""
    47234723
    4724 #: src/includes/classes/paypal-utilities.inc.php:553
    4725 #: src/includes/classes/paypal-utilities.inc.php:559
     4724#: src/includes/classes/paypal-utilities.inc.php:557
     4725#: src/includes/classes/paypal-utilities.inc.php:563
    47264726msgctxt "s2member-front"
    47274727msgid "Error #%s. Transaction declined. Please use an alternate funding source."
    47284728msgstr ""
    47294729
    4730 #: src/includes/classes/paypal-utilities.inc.php:556
     4730#: src/includes/classes/paypal-utilities.inc.php:560
    47314731msgctxt "s2member-front"
    47324732msgid "Error #%s. Transaction declined. Express Checkout was NOT confirmed."
    47334733msgstr ""
    47344734
    4735 #: src/includes/classes/paypal-utilities.inc.php:690
     4735#: src/includes/classes/paypal-utilities.inc.php:694
    47364736msgctxt "s2member-front"
    47374737msgid " Please contact PayPal Merchant Technical Support (www.paypal.com/mts) and request `Recurring Billing` service, and also ask to have `Reference Transactions` enabled for Recurring Billing via Express Checkout."
  • s2member/trunk/readme.txt

    r3714948 r3722434  
    44Plugin URI: https://s2member.com/
    55Tags: membership, content restriction, paid subscriptions, members only, paid access
    6 Version: 260927
    7 Stable tag: 260927
    8 Tested up to: 7.2-alpha-63947
     6Version: 261001
     7Stable tag: 261001
     8Tested up to: 7.2-alpha-64027
    99Requires at least: 4.2
    1010Requires PHP: 5.6.2
    … …  
    177177== Upgrade Notice ==
    178178
    179 = v260927 =
     179= v261001 =
    180180
    181181(SECURITY RELEASE) UPGRADE IMMEDIATELY. v260215 included a CRITICAL VULNERABILITY fix, and you shouldn't wait any longer to update if you're behind.
    182182
    183183== Changelog ==
     184
     185= v261001 =
     186
     187- (Framework) **Improvement:** PayPal Checkout buttons now keep a recoverable record of each checkout, allowing s2Member to complete a subscription signup even if the customer closes the page or loses their connection before the final confirmation. PayPal's verified webhook can finish the signup using the original purchase details, while checkout retries reuse the same subscription instead of creating another. Thanks to Felix for reporting the issue. See [thread #13627](https://f.wpsharks.com/t/13627).
     188
     189- (Framework) **Fix:** When a PayPal Checkout Buy Now payment succeeded but the browser lost the final response, the customer could see a payment error despite having been charged. s2Member now recovers the completed checkout and continues to the registration instructions without capturing the payment again.
     190
     191- (Framework) **Fix:** Corrected JavaScript issues that could prevent PayPal Checkout buttons from appearing, including incorrectly encoded characters and problems loading the PayPal SDK.
     192
     193- (Framework) **Fix:** Corrected an edge case where PayPal Checkout returns could fall back to the legacy site-wide proxy handler. Checkout returns now consistently use their transaction-specific return flow, with returned values normalized before downstream processing.
     194
     195- (Pro) **Security:** Hardened Remote Operations API authentication by using timing-safe comparisons when validating API keys.
     196
     197- (Pro) **Fix:** Improved legacy Multisite Membership-Only registration after WordPress core updates. s2Member now restores its required patches after successful automatic/background upgrades, while avoiding unnecessary rewrites when files are already patched. Thanks to Tim for reporting the issue. See [thread #13641](https://f.wpsharks.com/t/13641).
     198
     199- (Framework) **Compatibility:** Updated admin JSON handling to use WordPress's bundled JSON compatibility script instead of maintaining a separate bundled copy.
     200
     201- (Framework) **Maintenance:** Added missing direct-access safeguards to Markdown, AWeber, and IP utility files that are only intended to load through WordPress/s2Member.
     202
     203- (Framework) **Maintenance:** Removed obsolete inactive code that previously prevented automatic Framework updates when Pro was installed.
     204
     205- (Framework) **Maintenance:** Aligned the plugin's GPL license declaration with the existing GPLv2-or-later declaration in the readme.
     206
     207- (Pro) **Maintenance:** Added the existing GPLv2-or-later license declaration to the installer-compatible Pro plugin header.
     208
     209- (Framework) **Maintenance:** Updated release packaging so the internal language-generation marker remains available in the source repository without being included in the distributed ZIP build.
    184210
    185211= v260927 =
  • s2member/trunk/s2member.php

    r3714948 r3722434  
    2121/* -- This section for WordPress parsing. ------------------------------------------------------------------------------
    2222
    23 Version: 260927
    24 Stable tag: 260927
     23Version: 261001
     24Stable tag: 261001
    2525
    2626SSL Compatible: yes
    … …  
    3737ClickBank Compatible: yes w/s2Member Pro
    3838
    39 Tested up to: 7.2-alpha-63947
     39Tested up to: 7.2-alpha-64027
    4040Requires at least: 4.2
    4141
    … …  
    4444
    4545Copyright: © 2009 WP Sharks
    46 License: GNU General Public License
     46License: GNU General Public License v2 or later.
    4747Contributors: WebSharks, JasWSInc, anguz, raamdev, bruce-caldwell, clavaque
    4848
    … …  
    7878 * @var string
    7979 */
    80 ${__FILE__}['tmp'] = '260927'; //version//
     80${__FILE__}['tmp'] = '261001'; //version//
    8181if(!defined('WS_PLUGIN__S2MEMBER_VERSION'))
    8282        define('WS_PLUGIN__S2MEMBER_VERSION', ${__FILE__}['tmp']);
  • s2member/trunk/src/includes/classes/installation.inc.php

    r3701026 r3722434  
    294294                }
    295295
    296                 /**
    297                  * Disallow automatic updates of s2Member w/ Pro is installed.
    298                  *
    299                  * @package s2Member\Installation
    300                  * @since 150717
    301                  *
    302                  * @attaches-to `auto_update_plugin` filter.
    303                  */
    304                 public static function auto_update_filter($update = NULL, $item = NULL)
    305                 {
    306                         if(is_object($item) && !empty($item->slug) && $item->slug === 's2member')
    307                                 if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
    308                                         $update = FALSE; // Disallow.
    309 
    310                         return $update; // Filter through.
    311                 }
    312296        }
    313297}
  • s2member/trunk/src/includes/classes/menu-pages.inc.php

    r3701026 r3722434  
    375375                                wp_enqueue_script('jquery-ui-core');
    376376                                wp_enqueue_script('jquery-sprintf', $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/includes/jquery/jquery.sprintf/jquery.sprintf.min.js', array('jquery'), c_ws_plugin__s2member_utilities::ver_checksum());
    377                                 wp_enqueue_script('jquery-json-ps', $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/includes/jquery/jquery.json-ps/jquery.json-ps.min.js', array('jquery'), c_ws_plugin__s2member_utilities::ver_checksum());
    378377                                wp_enqueue_script('jquery-ui-effects', $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/includes/jquery/jquery.ui-effects/jquery.ui-effects.min.js', array('jquery', 'jquery-ui-core'), c_ws_plugin__s2member_utilities::ver_checksum());
    379                                 wp_enqueue_script('ws-plugin--s2member-menu-pages', admin_url('admin.php?ws_plugin__s2member_menu_pages_js='.urlencode(mt_rand()), is_ssl() ? 'https' : 'http'), array('jquery', 'thickbox', 'media-upload', 'jquery-sprintf', 'jquery-json-ps', 'jquery-ui-core', 'jquery-ui-effects', 'password-strength-meter'), c_ws_plugin__s2member_utilities::ver_checksum());
     378                                //260928.0402 Use WordPress's registered JSON2 compatibility script instead of shipping a duplicate JSON parser.
     379                                wp_enqueue_script('ws-plugin--s2member-menu-pages', admin_url('admin.php?ws_plugin__s2member_menu_pages_js='.urlencode(mt_rand()), is_ssl() ? 'https' : 'http'), array('jquery', 'thickbox', 'media-upload', 'jquery-sprintf', 'json2', 'jquery-ui-core', 'jquery-ui-effects', 'password-strength-meter'), c_ws_plugin__s2member_utilities::ver_checksum());
    380380
    381381                                do_action('ws_plugin__s2member_during_add_admin_scripts', get_defined_vars());
    … …  
    551551                                if(!empty($error))
    552552                                        c_ws_plugin__s2member_admin_notices::display_admin_notice('Unknown error when attempting to archive log files. Please check directory permissions.', TRUE);
    553                                 else c_ws_plugin__s2member_admin_notices::display_admin_notice('All log files have been archived succesfully.');
     553                                else
     554                                        c_ws_plugin__s2member_admin_notices::display_admin_notice('All log files have been archived successfully.');
    554555                        }
    555556                }
    … …  
    578579                                if(!empty($error))
    579580                                        c_ws_plugin__s2member_admin_notices::display_admin_notice('Unknown error when attempting to delete log files. Please check directory permissions.', TRUE);
    580                                 else c_ws_plugin__s2member_admin_notices::display_admin_notice('All log files have been deleted succesfully.');
     581                                else
     582                                        c_ws_plugin__s2member_admin_notices::display_admin_notice('All log files have been deleted successfully.');
    581583                        }
    582584                }
  • s2member/trunk/src/includes/classes/paypal-checkout-in.inc.php

    r3714948 r3722434  
    7878                                exit();
    7979                        }
     80                        //260928.1645 Never write a reusable signed Gateway Checkout browser token to PayPal debug logs; the encrypted shortcode token remains available to the handler itself.
     81                        $log_token = $token;
     82                        unset($log_token['gateway_checkout_token']);
    8083                        if(!empty($token['exp']) && is_numeric($token['exp']) && time() > (int)$token['exp'])
    8184                        {
    … …  
    9295                                echo wp_json_encode(array('error' => 'token_checksum_mismatch'));
    9396                                exit();
     97                        }
     98
     99                        //260928.1520 Framework button shortcodes use the same durable coordinator as Pro-Forms, initialized before any provider-side create operation and required for later browser return/confirmation.
     100                        if(strpos((string)$token['invoice'], 's2mb-') === 0 && $op !== 'cancel')
     101                        {
     102                                $create_allowed = in_array($op, array('create_order', 'create_subscription', 'get_plan_id', 'redirect'), TRUE);
     103                                $prepared = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_button_gateway_checkout_prepare($token, $create_allowed);
     104                                if(empty($prepared['ok']))
     105                                {
     106                                        $error = !empty($prepared['error']) ? (string)$prepared['error'] : 'gateway_checkout_unavailable';
     107                                        if($is_redirect_mode)
     108                                                echo esc_html($error);
     109                                        else
     110                                                echo wp_json_encode(array('error' => $error));
     111                                        exit();
     112                                }
    94113                        }
    95114
    … …  
    101120                                                'env_setting' => $env_setting,
    102121                                                'event' => 'token_ip_mismatch',
    103                                                 'token' => $token,
     122                                                'token' => $log_token,
    104123                                                'ip'    => c_ws_plugin__s2member_utils_ip::current(),
    105124                                        ));
    … …  
    145164                                                        'event'  => 'redirect_order_create_response',
    146165                                                        'order'  => $order,
    147                                                         'token'  => $token,
     166                                                        'token'  => $log_token,
    148167                                                ));
    149168
    … …  
    158177                                                                }
    159178
     179                                                //260928.1605 A resumed Gateway Checkout returns its existing provider ID, not the original create response links; fetch the provider resource rather than create another chargeable order.
     180                                                if(!$approve_url && !empty($order['id']) && strpos((string)$token['invoice'], 's2mb-') === 0)
     181                                                {
     182                                                        $order_details = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_order_details((string)$order['id']);
     183                                                        if(empty($order_details['__error']) && !empty($order_details['links']) && is_array($order_details['links']))
     184                                                                foreach($order_details['links'] as $link)
     185                                                                        if(!empty($link['rel']) && !empty($link['href']) && in_array(strtolower((string)$link['rel']), array('approve', 'payer-action', 'approval_url'), TRUE))
     186                                                                                $approve_url = (string)$link['href'];
     187                                                }
     188
    160189                                                if(!$approve_url)
    161190                                                {
    … …  
    176205                                                        'event'        => 'redirect_subscription_create_response',
    177206                                                        'subscription' => $subscription,
    178                                                         'token'        => $token,
     207                                                        'token'        => $log_token,
    179208                                                ));
    180209
    … …  
    184213                                                                if(!empty($link['rel']) && !empty($link['href']) && strtolower((string)$link['rel']) === 'approve')
    185214                                                                        $approve_url = (string)$link['href'];
     215
     216                                                //260928.1605 Reuse the same persisted PayPal subscription on repeated redirect clicks. A details GET may supply the approval link without starting a second subscription.
     217                                                if(!$approve_url && !empty($subscription['id']) && strpos((string)$token['invoice'], 's2mb-') === 0)
     218                                                {
     219                                                        $subscription_details = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_subscription_details((string)$subscription['id']);
     220                                                        if(empty($subscription_details['__error']) && !empty($subscription_details['links']) && is_array($subscription_details['links']))
     221                                                                foreach($subscription_details['links'] as $link)
     222                                                                        if(!empty($link['rel']) && !empty($link['href']) && strtolower((string)$link['rel']) === 'approve')
     223                                                                                $approve_url = (string)$link['href'];
     224                                                }
    186225
    187226                                                if(!$approve_url)
    … …  
    220259                                                'payer'      => !empty($capture['payer']['email_address']) ? (string)$capture['payer']['email_address'] : '',
    221260                                                'capture'    => $capture,
    222                                                 'token'      => $token,
     261                                                'token'      => $log_token,
    223262                                        ));
    224263
    … …  
    232271                                        {
    233272                                                echo 'order_capture_failed';
     273                                                exit();
     274                                        }
     275
     276                                        //260928.1538 Framework button redirect purchases use the shared coordinator fulfillment instead of a second hand-written notify/return path.
     277                                        if(strpos((string)$token['invoice'], 's2mb-') === 0)
     278                                        {
     279                                                $fulfillment = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_order_fulfill($capture, $token);
     280                                                if(empty($fulfillment['ok']) || empty($fulfillment['rtn_url']) || empty($fulfillment['rtn_post']))
     281                                                {
     282                                                        echo esc_html(!empty($fulfillment['error']) ? (string)$fulfillment['error'] : 'order_fulfillment_failed');
     283                                                        exit();
     284                                                }
     285                                                echo '<!DOCTYPE html><html><head><meta charset="utf-8" /><meta name="robots" content="noindex,nofollow" /></head><body>';
     286                                                echo '<form id="s2m_ppco_rtn" method="post" accept-charset="UTF-8" action="'.esc_attr($fulfillment['rtn_url']).'">';
     287                                                foreach($fulfillment['rtn_post'] as $k => $v)
     288                                                        echo '<input type="hidden" name="'.esc_attr($k).'" value="'.esc_attr((string)$v).'" />';
     289                                                echo '</form><script type="text/javascript">document.getElementById("s2m_ppco_rtn").submit();</script></body></html>';
    234290                                                exit();
    235291                                        }
    … …  
    367423                                                'code'            => !empty($subscription_r['code']) ? (int)$subscription_r['code'] : 0,
    368424                                                'body'            => !empty($subscription_r['body']) ? (string)$subscription_r['body'] : '',
    369                                                 'token'           => $token,
     425                                                'token'           => $log_token,
    370426                                        ));
    371427
    … …  
    390446                                        {
    391447                                                echo 'subscription_custom_id_mismatch';
     448                                                exit();
     449                                        }
     450
     451                                        //260928.1538 A returned Framework button and an ACTIVATED webhook resolve the same provider subscription against the same saved purchase token.
     452                                        if(strpos((string)$token['invoice'], 's2mb-') === 0)
     453                                        {
     454                                                $fulfillment = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_button_subscription_fulfill($subscription, $token, 'return');
     455                                                if(!empty($fulfillment['pending_activation']))
     456                                                {
     457                                                        //260928.1703 PayPal can redirect before ACTIVE (or while the webhook holds the checkout lock). Recheck the same signed return, without creating another subscription or showing a false payment failure.
     458                                                        $wait_attempt = isset($_GET['s2member_paypal_checkout_wait']) ? max(0, (int)$_GET['s2member_paypal_checkout_wait']) : 0;
     459                                                        echo '<!DOCTYPE html><html><head><meta charset="utf-8" /><meta name="robots" content="noindex,nofollow" /><title>PayPal subscription confirmation</title></head><body>';
     460                                                        echo '<p>PayPal is confirming your subscription. Please do not start a second checkout.</p>';
     461                                                        if($wait_attempt < 12)
     462                                                        {
     463                                                                $poll_url = add_query_arg(array('subscription_id' => $subscription_id, 's2member_paypal_checkout_wait' => $wait_attempt + 1), $return_url);
     464                                                                echo '<script type="text/javascript">setTimeout(function(){window.location.replace('.wp_json_encode($poll_url, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT).');},2000);</script>';
     465                                                        }
     466                                                        else
     467                                                                echo '<p>Confirmation is taking longer than expected. If PayPal activates the subscription, s2Member will complete it through the webhook; check your registration email before trying again.</p>';
     468                                                        echo '</body></html>';
     469                                                        exit();
     470                                                }
     471                                                if(empty($fulfillment['ok']) || empty($fulfillment['rtn_url']) || empty($fulfillment['rtn_post']))
     472                                                {
     473                                                        echo esc_html(!empty($fulfillment['error']) ? (string)$fulfillment['error'] : 'subscription_fulfillment_failed');
     474                                                        exit();
     475                                                }
     476                                                echo '<!DOCTYPE html><html><head><meta charset="utf-8" /><meta name="robots" content="noindex,nofollow" /></head><body>';
     477                                                echo '<form id="s2m_ppco_rtn" method="post" accept-charset="UTF-8" action="'.esc_attr($fulfillment['rtn_url']).'">';
     478                                                foreach($fulfillment['rtn_post'] as $k => $v)
     479                                                        echo '<input type="hidden" name="'.esc_attr($k).'" value="'.esc_attr((string)$v).'" />';
     480                                                echo '</form><script type="text/javascript">document.getElementById("s2m_ppco_rtn").submit();</script></body></html>';
    392481                                                exit();
    393482                                        }
    … …  
    488577                                        'event'        => 'create_subscription_response',
    489578                                        'subscription' => $subscription,
    490                                         'token'        => $token,
     579                                        'token'        => $log_token,
    491580                                ));
    492581
    … …  
    546635                                                'event'   => 'get_plan_id_response',
    547636                                                'plan_id' => $plan_id,
    548                                                 'token'   => $token,
     637                                                'token'   => $log_token,
    549638                                        ));
    550639
    … …  
    595684                                                'subscription_id' => $subscription_id,
    596685                                                'subscription'    => $subscription_r,
    597                                                 'token'           => $token,
     686                                                'token'           => $log_token,
    598687                                        ));
    599688
    … …  
    681770                                        ));
    682771                                        echo wp_json_encode(array('error' => 'subscription_custom_id_mismatch'));
     772                                        exit();
     773                                }
     774
     775                                //260928.1538 Framework button and webhook share a single durable fulfillment path; do not create a second simulated IPN here.
     776                                if(strpos((string)$token['invoice'], 's2mb-') === 0)
     777                                {
     778                                        $fulfillment = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_button_subscription_fulfill($subscription, $token, 'browser');
     779                                        if(!empty($fulfillment['pending_activation']))
     780                                        {
     781                                                echo wp_json_encode(array('pending_activation' => TRUE, 'subscription_id' => $subscription_id, 'status' => (string)$fulfillment['status']));
     782                                                exit();
     783                                        }
     784                                        if(empty($fulfillment['ok']) || empty($fulfillment['rtn_url']) || empty($fulfillment['rtn_post']))
     785                                        {
     786                                                echo wp_json_encode(array('error' => !empty($fulfillment['error']) ? (string)$fulfillment['error'] : 'subscription_fulfillment_failed'));
     787                                                exit();
     788                                        }
     789                                        echo wp_json_encode(array('rtn_url' => $fulfillment['rtn_url'], 'rtn_post' => $fulfillment['rtn_post']));
    683790                                        exit();
    684791                                }
    … …  
    711818                                {
    712819                                        //260902.0200 Preserve existing non-coordinator PayPal Checkout button behavior until those flows migrate onto Gateway Checkout and gain the same activation polling.
    713                                         //260907.2142 TO-DO: Migrate maintained Framework PayPal Checkout button/redirect flows onto Gateway Checkout before claiming cross-surface PPCO dedupe/idempotency parity, preserving the Pro-Form guarantees for durable provider identity, stable idempotent retries, monotonic final-state recovery, and shared browser/webhook fulfillment dedupe.
     820                                        //260928.1645 Existing pre-migration browser tabs still carry the legacy PayPal Checkout token without Gateway Checkout identity. Preserve their original confirmation behavior until those in-flight tokens expire.
    714821                                        c_ws_plugin__s2member_utils_logs::log_entry('paypal-checkout', array(
    715822                                                'ppco'            => 'checkout',
    … …  
    848955                                                'env_setting' => $env_setting,
    849956                                                'event' => 'cancel_subscription_not_logged_in',
    850                                                 'token' => $token,
     957                                                'token' => $log_token,
    851958                                        ));
    852959
    … …  
    880987                                                'event'   => 'cancel_subscription_token_mismatch',
    881988                                                'user_id' => $user_id,
    882                                                 'token'   => $token,
     989                                                'token'   => $log_token,
    883990                                        ));
    884991
    … …  
    10211128                                                'event' => 'create_order_response',
    10221129                                                'order' => $order,
    1023                                                 'token' => $token,
     1130                                                'token' => $log_token,
    10241131                                        ));
    10251132
    … …  
    10311138                                                        'event' => 'order_create_failed',
    10321139                                                        'order' => $order,
    1033                                                         'token' => $token,
     1140                                                        'token' => $log_token,
    10341141                                                ));
    10351142
    … …  
    10471154                                //260907.1820 This recovery endpoint is intentionally coordinator-only: the signed checkout token authorizes a local state read, while PayPal polling/retries remain server/webhook responsibilities.
    10481155                                $gateway_checkout_id = !empty($token['gateway_checkout_id']) && c_ws_plugin__s2member_gateway_checkouts::valid_id((string)$token['gateway_checkout_id']) ? (string)$token['gateway_checkout_id'] : '';
    1049                                 $gateway_checkout = $gateway_checkout_id ? c_ws_plugin__s2member_gateway_checkouts::load_state($gateway_checkout_id) : FALSE;
     1156                                //260928.1703 Read fresh option state during capture-loss polling: a webhook may have fulfilled the checkout in another PHP worker moments earlier.
     1157                                $gateway_checkout = $gateway_checkout_id ? c_ws_plugin__s2member_gateway_checkouts::load_state_uncached($gateway_checkout_id) : FALSE;
    10501158                                if(!$gateway_checkout || (string)$gateway_checkout['gateway'] !== 'paypal_checkout' || (string)$gateway_checkout['operation'] !== 'payment')
    10511159                                {
    … …  
    10571165                                $fulfillment_result = is_array($private_context) && !empty($private_context['paypal_checkout']['fulfillment_result']) && is_array($private_context['paypal_checkout']['fulfillment_result']) ? $private_context['paypal_checkout']['fulfillment_result'] : array();
    10581166                                //260902.0635 Poll only local coordinator state while independent PayPal webhooks resolve delayed creates/captures; do not hammer the provider from the browser.
    1059                                 echo wp_json_encode(array(
     1167                                $fulfilled = ((string)$gateway_checkout['fulfillment_status'] === 'fulfilled' && !empty($fulfillment_result['rtn_url']) && !empty($fulfillment_result['rtn_post']));
     1168                                $response = array(
    10601169                                        'order_id'           => !empty($gateway_checkout['gateway_ids']['order_id']) ? (string)$gateway_checkout['gateway_ids']['order_id'] : '',
    10611170                                        'capture_id'         => !empty($gateway_checkout['gateway_ids']['capture_id']) ? (string)$gateway_checkout['gateway_ids']['capture_id'] : '',
    10621171                                        'status'             => !empty($gateway_checkout['gateway_status']) ? (string)$gateway_checkout['gateway_status'] : '',
    10631172                                        'fulfillment_status' => !empty($gateway_checkout['fulfillment_status']) ? (string)$gateway_checkout['fulfillment_status'] : '',
    1064                                         'fulfilled'          => ((string)$gateway_checkout['fulfillment_status'] === 'fulfilled' && !empty($fulfillment_result)),
    1065                                 ));
     1173                                        'fulfilled'          => $fulfilled,
     1174                                );
     1175                                //260928.1703 A lost capture response can be recovered with the already-signed return handoff; only the original encrypted checkout token can reach this endpoint.
     1176                                if($fulfilled)
     1177                                {
     1178                                        $response['rtn_url'] = $fulfillment_result['rtn_url'];
     1179                                        $response['rtn_post'] = $fulfillment_result['rtn_post'];
     1180                                }
     1181                                echo wp_json_encode($response);
    10661182                                exit();
    10671183                        }
    … …  
    11041220                                        'payer'      => !empty($capture['payer']['email_address']) ? (string)$capture['payer']['email_address'] : '',
    11051221                                        'capture'    => $capture,
    1106                                         'token'      => $token,
     1222                                        'token'      => $log_token,
    11071223                                ));
    11081224
    … …  
    11591275                                                        'order_id' => $order_id,
    11601276                                                        'capture'  => $capture,
    1161                                                         'token'    => $token,
     1277                                                        'token'    => $log_token,
    11621278                                                ));
    11631279
    … …  
    11751291                                                'event'    => 'amount_mismatch',
    11761292                                                'order_id' => $order_id,
    1177                                                 'token'    => $token,
     1293                                                'token'    => $log_token,
    11781294                                                'pu'       => array('amount' => $pu_amount, 'cc' => $pu_cc),
    11791295                                        ));
    … …  
    11881304                                                'event'    => 'currency_mismatch',
    11891305                                                'order_id' => $order_id,
    1190                                                 'token'    => $token,
     1306                                                'token'    => $log_token,
    11911307                                                'pu'       => array('amount' => $pu_amount, 'cc' => $pu_cc),
    11921308                                        ));
    … …  
    12071323                                                'event'    => 'invoice_mismatch',
    12081324                                                'order_id' => $order_id,
    1209                                                 'token'    => $token,
     1325                                                'token'    => $log_token,
    12101326                                                'invoice'  => $cap_invoice_id,
    12111327                                        ));
    … …  
    12271343                                                'event'    => 'custom_mismatch',
    12281344                                                'order_id' => $order_id,
    1229                                                 'token'    => $token,
     1345                                                'token'    => $log_token,
    12301346                                                'custom'   => array(
    12311347                                                        'token'   => !empty($token['custom']) ? $token['custom'] : '',
  • s2member/trunk/src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php

    r3671073 r3722434  
    4343                        extract($vars, EXTR_OVERWRITE | EXTR_REFS); // Extract all vars passed in from: ``c_ws_plugin__s2member_paypal_notify_in::paypal_notify()``.
    4444
     45                        //260927.2250 Strip PHP opening tags from verified Return data before any values can be substituted into configurable emails, URLs, or tracking-code templates.
     46                        $paypal = c_ws_plugin__s2member_utils_strings::strip_php_tags_deep($paypal);
     47
    4548                        if((!empty($paypal['txn_type']) && preg_match('/^(web_accept|subscr_signup|subscr_payment)$/i', $paypal['txn_type']))
    4649                           && (!empty($paypal['item_number']) && preg_match($GLOBALS['WS_PLUGIN__']['s2member']['c']['membership_item_number_w_level_regex'], $paypal['item_number']))
  • s2member/trunk/src/includes/classes/paypal-utilities.inc.php

    r3714948 r3722434  
    122122                                                                                $endpoint = ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"]) ? "www.sandbox.paypal.com" : "www.paypal.com";
    123123
     124                                                                                //260927.2250 Browser PayPal Returns must use the transaction-bound Checkout handoff above; never let the reusable server-to-server proxy credential authenticate them.
     125                                                                                if(!empty($_GET["s2member_paypal_return"]) && !empty($_REQUEST["s2member_paypal_proxy"]) && is_string($_REQUEST["s2member_paypal_proxy"]) && $_REQUEST["s2member_paypal_proxy"] === "paypal")
     126                                                                                        return false;
     127
    124128                                                                                //260909.0411 Normalize proxy verification input types and use the standard constant-time comparison helper.
    125                                                                                 if(!empty($_REQUEST["s2member_paypal_proxy"]) && is_string($_REQUEST["s2member_paypal_proxy"]) && !empty($_REQUEST["s2member_paypal_proxy_verification"]) && is_string($_REQUEST["s2member_paypal_proxy_verification"]) && is_string($proxy_verification_key = c_ws_plugin__s2member_paypal_utilities::paypal_proxy_key_gen()) && hash_equals($proxy_verification_key, $_REQUEST["s2member_paypal_proxy_verification"]))
     129                                                                                else if(!empty($_REQUEST["s2member_paypal_proxy"]) && is_string($_REQUEST["s2member_paypal_proxy"]) && !empty($_REQUEST["s2member_paypal_proxy_verification"]) && is_string($_REQUEST["s2member_paypal_proxy_verification"]) && is_string($proxy_verification_key = c_ws_plugin__s2member_paypal_utilities::paypal_proxy_key_gen()) && hash_equals($proxy_verification_key, $_REQUEST["s2member_paypal_proxy_verification"]))
    126130                                                                                        return apply_filters("ws_plugin__s2member_paypal_postvars", array_merge($postvars, array("proxy_verified" => $_REQUEST["s2member_paypal_proxy"])), get_defined_vars());
    127131
    … …  
    15301534                                                else if(strpos($invoice, 's2msp-') === 0)
    15311535                                                        $gateway_checkout_id = substr($invoice, strlen('s2msp-'));
     1536                                                else if(strpos($invoice, 's2mb-') === 0) //260928.1515 Standalone Framework buttons use their own invoice namespace, separate from Pro-Form account preparation.
     1537                                                        $gateway_checkout_id = substr($invoice, strlen('s2mb-'));
    15321538
    15331539                                                return c_ws_plugin__s2member_gateway_checkouts::valid_id($gateway_checkout_id) ? $gateway_checkout_id : '';
    15341540                                        }
     1541
     1542                                /**
     1543                                 * Starts or resumes a standalone Framework PayPal Checkout button using shared durable state.
     1544                                 *
     1545                                 * @since 260928.1520
     1546                                 *
     1547                                 * @param array $token Verified, signed standalone button purchase token.
     1548                                 * @param bool  $create_allowed True only before starting provider work.
     1549                                 * @return array Operation result containing ok and error.
     1550                                 */
     1551                                public static function paypal_checkout_button_gateway_checkout_prepare($token = array(), $create_allowed = FALSE)
     1552                                {
     1553                                        $invoice = !empty($token['invoice']) ? (string)$token['invoice'] : '';
     1554                                        if(strpos($invoice, 's2mb-') !== 0)
     1555                                                return array('ok' => TRUE, 'coordinator' => FALSE, 'error' => ''); // Existing in-flight button tokens and Pro-Forms use their established paths.
     1556
     1557                                        $id = self::paypal_checkout_gateway_checkout_id_from_invoice($invoice);
     1558                                        $browser_token = !empty($token['gateway_checkout_token']) ? (string)$token['gateway_checkout_token'] : '';
     1559                                        if(!$id || empty($token['gateway_checkout_id']) || !hash_equals($id, (string)$token['gateway_checkout_id'])
     1560                                        || !c_ws_plugin__s2member_gateway_checkouts::browser_token_verify($id, $browser_token))
     1561                                                return array('ok' => FALSE, 'coordinator' => TRUE, 'error' => 'gateway_checkout_identity_invalid');
     1562
     1563                                        $operation = (!empty($token['rr']) && strtoupper((string)$token['rr']) !== 'BN') ? 'subscription' : 'payment';
     1564                                        $purchase_terms = (array)$token;
     1565                                        unset($purchase_terms['exp'], $purchase_terms['gateway_checkout_token']); //260928.1520 Token renewal does not alter the underlying purchase contract.
     1566                                        $fingerprint = c_ws_plugin__s2member_gateway_checkouts::purchase_fingerprint($purchase_terms);
     1567
     1568                                        if($create_allowed)
     1569                                        {
     1570                                                //260928.1705 Do not rewrite a bound option on every retry: create_or_resume() may otherwise overwrite provider/fulfillment updates committed concurrently by a webhook.
     1571                                                $state = c_ws_plugin__s2member_gateway_checkouts::load_state_uncached($id);
     1572                                                if(!$state)
     1573                                                        $state = c_ws_plugin__s2member_gateway_checkouts::create_or_resume('paypal_checkout', $operation, $id, $browser_token, $fingerprint, get_current_user_id());
     1574                                                else if(!empty($state['user_id']) && (int)$state['user_id'] !== (int)get_current_user_id())
     1575                                                        return array('ok' => FALSE, 'coordinator' => TRUE, 'error' => 'gateway_checkout_user_mismatch');
     1576                                        }
     1577                                        else
     1578                                                $state = c_ws_plugin__s2member_gateway_checkouts::load_state_uncached($id);
     1579
     1580                                        //260928.1520 Reject a checkout returned under a replacement identity: the verified button token and PayPal invoice must keep pointing to the same durable record.
     1581                                        if(!$state || !hash_equals($id, (string)$state['id']) || (string)$state['gateway'] !== 'paypal_checkout'
     1582                                        || (string)$state['operation'] !== $operation || !hash_equals($fingerprint, (string)$state['purchase_fingerprint']))
     1583                                                return array('ok' => FALSE, 'coordinator' => TRUE, 'error' => 'gateway_checkout_mismatch');
     1584
     1585                                        $private = c_ws_plugin__s2member_gateway_checkouts::private_context_get($id);
     1586                                        if($private === FALSE)
     1587                                                return array('ok' => FALSE, 'coordinator' => TRUE, 'error' => 'gateway_checkout_private_context_invalid');
     1588
     1589                                        if(empty($private['paypal_checkout']['token']))
     1590                                        {
     1591                                                if(!$create_allowed)
     1592                                                        return array('ok' => FALSE, 'coordinator' => TRUE, 'error' => 'gateway_checkout_purchase_context_missing');
     1593                                                $private = (array)$private;
     1594                                                $private['paypal_checkout'] = !empty($private['paypal_checkout']) && is_array($private['paypal_checkout']) ? $private['paypal_checkout'] : array();
     1595                                                //260928.1520 The first provider operation durably stores the authenticated purchase token for webhook-only fulfillment. No password/card data is stored.
     1596                                                $private['paypal_checkout']['token'] = $token;
     1597                                                if(!c_ws_plugin__s2member_gateway_checkouts::private_context_set($id, $private))
     1598                                                        return array('ok' => FALSE, 'coordinator' => TRUE, 'error' => 'gateway_checkout_purchase_context_save_failed');
     1599                                        }
     1600                                        return array('ok' => TRUE, 'coordinator' => TRUE, 'error' => '', 'gateway_checkout_id' => $id);
     1601                                }
    15351602
    15361603                                /**
    … …  
    16551722                                                                $private_context['paypal_checkout'] = !empty($private_context['paypal_checkout']) && is_array($private_context['paypal_checkout']) ? $private_context['paypal_checkout'] : array();
    16561723                                                                //260902.0635 Save the validated token before contacting PayPal so a later capture webhook has enough trusted server-side context to finish an interrupted browser checkout.
    1657                                                                 $private_context['paypal_checkout']['token'] = $token;
     1724                                                                //260928.1615 An anchor/url checkout temporarily substitutes PayPal's internal approval-return URL for provider creation; keep the canonical, previously validated button token so a capture webhook returns the buyer to the original success page.
     1725                                                                if(strpos($invoice, 's2mb-') !== 0 || empty($private_context['paypal_checkout']['token']))
     1726                                                                        $private_context['paypal_checkout']['token'] = $token;
    16581727                                                                if(!c_ws_plugin__s2member_gateway_checkouts::private_context_set($gateway_checkout_id, $private_context))
    16591728                                                                        return array('__error' => 'gateway_checkout_private_context_failed');
    … …  
    21562225                                                        return array('ok' => FALSE, 'error' => 'gateway_checkout_private_context_failed');
    21572226
    2158                                                 $gateway_ids = !empty($gateway_checkout['gateway_ids']) && is_array($gateway_checkout['gateway_ids']) ? $gateway_checkout['gateway_ids'] : array();
    2159                                                 $gateway_ids['order_id'] = $order_id;
    2160                                                 $gateway_ids['capture_id'] = $pu_cap_id;
    2161                                                 if(!c_ws_plugin__s2member_gateway_checkouts::update($gateway_checkout_id, array('gateway_ids' => $gateway_ids, 'gateway_status' => 'CAPTURE_COMPLETED', 'fulfillment_status' => 'fulfilled')))
     2227                                                //260928.1705 Final fulfillment must patch the latest checkout version: a concurrent webhook/browser context write must not be lost or downgrade the terminal fulfilled state.
     2228                                                if(!c_ws_plugin__s2member_gateway_checkouts::patch($gateway_checkout_id, array('gateway_ids' => array('order_id' => $order_id, 'capture_id' => $pu_cap_id), 'gateway_status' => 'CAPTURE_COMPLETED', 'fulfillment_status' => 'fulfilled')))
    21622229                                                        return array('ok' => FALSE, 'error' => 'gateway_checkout_save_failed');
    21632230
    … …  
    22852352                                                $subscription_id = trim((string)$subscription_id);
    22862353                                                $status = strtoupper(trim((string)$status));
    2287                                                 $gateway_checkout_id = (strpos($invoice, 's2mpf-') === 0) ? substr($invoice, strlen('s2mpf-')) : '';
     2354                                                $gateway_checkout_id = self::paypal_checkout_gateway_checkout_id_from_invoice($invoice); //260928.1515 Recover both Pro-Forms and standalone Framework button subscriptions by their signed invoice identity.
    22882355
    22892356                                                if(!$subscription_id || !c_ws_plugin__s2member_gateway_checkouts::valid_id($gateway_checkout_id))
    … …  
    23342401                                                }
    23352402                                        }
     2403
     2404                                /**
     2405                                 * Completes an approved standalone Framework button subscription from the same
     2406                                 * authoritative PayPal resource whether invoked by browser or verified webhook.
     2407                                 *
     2408                                 * @since 260928.1530
     2409                                 */
     2410                                public static function paypal_checkout_button_subscription_fulfill($subscription = array(), $token = array(), $via = 'webhook')
     2411                                {
     2412                                        $invoice = !empty($token['invoice']) ? (string)$token['invoice'] : '';
     2413                                        $id = self::paypal_checkout_gateway_checkout_id_from_invoice($invoice);
     2414                                        if(!$id || strpos($invoice, 's2mb-') !== 0 || empty($token['gateway_checkout_id']) || !hash_equals($id, (string)$token['gateway_checkout_id']))
     2415                                                return array('ok' => FALSE, 'error' => 'gateway_checkout_identity_invalid');
     2416
     2417                                        $state = c_ws_plugin__s2member_gateway_checkouts::load_state($id);
     2418                                        if(!$state || (string)$state['gateway'] !== 'paypal_checkout' || (string)$state['operation'] !== 'subscription')
     2419                                                return array('ok' => FALSE, 'error' => 'gateway_checkout_invalid');
     2420
     2421                                        $subscription_id = !empty($subscription['id']) ? (string)$subscription['id'] : '';
     2422                                        $status = !empty($subscription['status']) ? strtoupper((string)$subscription['status']) : '';
     2423                                        $custom_id = !empty($subscription['custom_id']) ? (string)$subscription['custom_id'] : '';
     2424                                        if(!$subscription_id || !$custom_id || !hash_equals($invoice, $custom_id))
     2425                                                return array('ok' => FALSE, 'error' => 'subscription_purchase_identity_mismatch');
     2426
     2427                                        $expected_plan = self::paypal_checkout_plan_get_id($token);
     2428                                        if(!$expected_plan || empty($subscription['plan_id']) || !hash_equals((string)$expected_plan, (string)$subscription['plan_id']))
     2429                                                return array('ok' => FALSE, 'error' => 'subscription_plan_mismatch');
     2430
     2431                                        $is_single_cycle = isset($token['rr']) && (string)$token['rr'] === '0';
     2432                                        $last_payment_amount = isset($subscription['billing_info']['last_payment']['amount']['value']) ? (string)$subscription['billing_info']['last_payment']['amount']['value'] : '';
     2433                                        $last_payment_currency = !empty($subscription['billing_info']['last_payment']['amount']['currency_code']) ? strtoupper((string)$subscription['billing_info']['last_payment']['amount']['currency_code']) : '';
     2434                                        //260928.1703 An immediately EXPIRED single-cycle subscription is paid only when PayPal's reported last payment matches the signed price and currency, not merely when a payment field exists.
     2435                                        $last_paid = ($last_payment_amount !== '' && is_numeric($last_payment_amount) && isset($token['amount'])
     2436                                                && number_format((float)$last_payment_amount, 2, '.', '') === number_format((float)$token['amount'], 2, '.', '')
     2437                                                && !empty($token['cc']) && $last_payment_currency === strtoupper((string)$token['cc']));
     2438                                        if($status !== 'ACTIVE' && !($is_single_cycle && $status === 'EXPIRED' && $last_paid))
     2439                                                return in_array($status, array('APPROVAL_PENDING', 'APPROVED'), TRUE)
     2440                                                        ? array('ok' => FALSE, 'pending_activation' => TRUE, 'error' => 'pending_activation', 'status' => $status)
     2441                                                        : array('ok' => FALSE, 'error' => 'subscription_status_invalid', 'status' => $status);
     2442
     2443                                        //260928.1530 Bind the real subscription ID before fulfillment so a second event/browser request cannot attach a different provider subscription to this purchase.
     2444                                        $recovery = self::paypal_checkout_subscription_gateway_checkout_recover($invoice, $subscription_id, $status);
     2445                                        if(empty($recovery['handled']) || empty($recovery['ok']))
     2446                                        {
     2447                                                //260928.1608 An independent CREATED/ACTIVATED webhook can own the coordinator lock briefly; the browser should poll rather than report a permanent checkout failure.
     2448                                                //260928.1703 A redirect return also competes with CREATED/ACTIVATED webhook recovery; let it retry the signed return instead of displaying a spurious failure.
     2449                                                if(in_array($via, array('browser', 'return'), TRUE) && !empty($recovery['error']) && $recovery['error'] === 'gateway_checkout_busy')
     2450                                                        return array('ok' => FALSE, 'pending_activation' => TRUE, 'status' => $status, 'error' => 'gateway_checkout_busy');
     2451                                                return array('ok' => FALSE, 'error' => !empty($recovery['error']) ? $recovery['error'] : 'subscription_recovery_failed');
     2452                                        }
     2453
     2454                                        $private = c_ws_plugin__s2member_gateway_checkouts::private_context_get($id);
     2455                                        if(!is_array($private) || empty($private['paypal_checkout']['token']) || !is_array($private['paypal_checkout']['token']))
     2456                                                return array('ok' => FALSE, 'error' => 'gateway_checkout_purchase_context_missing');
     2457                                        $stored_token = $private['paypal_checkout']['token'];
     2458                                        if(empty($stored_token['invoice']) || !hash_equals($invoice, (string)$stored_token['invoice']) || empty($stored_token['item_number']))
     2459                                                return array('ok' => FALSE, 'error' => 'gateway_checkout_purchase_context_mismatch');
     2460
     2461                                        $state = c_ws_plugin__s2member_gateway_checkouts::load_state($id);
     2462                                        if($state && (string)$state['fulfillment_status'] === 'fulfilled' && !empty($private['paypal_checkout']['fulfillment_result']))
     2463                                                return array_merge(array('ok' => TRUE, 'processed' => FALSE, 'duplicate' => TRUE), $private['paypal_checkout']['fulfillment_result']);
     2464
     2465                                        $paypal = array(
     2466                                                'txn_type' => 'subscr_signup', 'payment_status' => 'Completed', 'subscr_gateway' => 'paypal',
     2467                                                'txn_id' => $subscription_id, 'subscr_id' => $subscription_id, 'subscr_baid' => $subscription_id, 'subscr_cid' => $subscription_id,
     2468                                                'mc_gross' => (string)$stored_token['amount'], 'mc_currency' => strtoupper((string)$stored_token['cc']),
     2469                                                'period1' => (!empty($stored_token['tp']) && !empty($stored_token['tt'])) ? ((string)$stored_token['tp'].' '.strtoupper((string)$stored_token['tt'])) : '0 D',
     2470                                                'mc_amount1' => (!empty($stored_token['tp']) && !empty($stored_token['tt'])) ? (string)$stored_token['ta'] : '0.00',
     2471                                                'period3' => ((string)$stored_token['rp'].' '.strtoupper((string)$stored_token['rt'])),
     2472                                                'mc_amount3' => (string)$stored_token['amount'],
     2473                                                'recurring' => ((isset($stored_token['rr']) && (string)$stored_token['rr'] === '1') ? '1' : '0'),
     2474                                                'invoice' => $invoice, 'custom' => (string)$stored_token['custom'],
     2475                                                'item_name' => (string)$stored_token['item_name'], 'item_number' => (string)$stored_token['item_number'],
     2476                                                'payer_email' => !empty($subscription['subscriber']['email_address']) ? (string)$subscription['subscriber']['email_address'] : '',
     2477                                                'first_name' => !empty($subscription['subscriber']['name']['given_name']) ? (string)$subscription['subscriber']['name']['given_name'] : '',
     2478                                                'last_name' => !empty($subscription['subscriber']['name']['surname']) ? (string)$subscription['subscriber']['name']['surname'] : '',
     2479                                                'option_name1' => (string)$stored_token['on0'], 'option_selection1' => (string)$stored_token['os0'],
     2480                                                'option_name2' => (string)$stored_token['on1'], 'option_selection2' => (string)$stored_token['os1'],
     2481                                        );
     2482
     2483                                        $notify_result = self::paypal_checkout_notify_once($paypal, 's2m_ppco_subscr_done_'.md5($subscription_id));
     2484                                        if(empty($notify_result['ok']))
     2485                                                return array('ok' => FALSE, 'error' => !empty($notify_result['error']) ? (string)$notify_result['error'] : 'notify_proxy_failed');
     2486
     2487                                        $return_url = add_query_arg('s2member_paypal_proxy', 'paypal', (string)$stored_token['return']);
     2488                                        $return_post = array_merge($paypal, array('s2member_paypal_proxy' => 'paypal', 's2member_paypal_proxy_use' => 'paypal_checkout'));
     2489                                        $handoff = self::paypal_checkout_return_handoff_create($return_post);
     2490                                        if(!$handoff)
     2491                                                return array('ok' => FALSE, 'error' => 'return_handoff_failed');
     2492                                        $return_post['s2member_paypal_checkout_handoff'] = $handoff;
     2493                                        $result = array('rtn_url' => $return_url, 'rtn_post' => $return_post, 'subscription_id' => $subscription_id);
     2494
     2495                                        $private['paypal_checkout']['fulfillment_result'] = $result;
     2496                                        if(!c_ws_plugin__s2member_gateway_checkouts::private_context_set($id, $private))
     2497                                                return array('ok' => FALSE, 'error' => 'gateway_checkout_private_context_save_failed');
     2498                                        if(!c_ws_plugin__s2member_gateway_checkouts::patch($id, array('gateway_ids' => array('subscription_id' => $subscription_id), 'gateway_status' => $status, 'fulfillment_status' => 'fulfilled')))
     2499                                                return array('ok' => FALSE, 'error' => 'gateway_checkout_save_failed');
     2500
     2501                                        //260928.1530 Preserve button upgrade semantics: only the request that processed Notify may cancel the old subscription, never a duplicate callback.
     2502                                        $old_id = !empty($stored_token['old__subscr_id']) ? (string)$stored_token['old__subscr_id'] : '';
     2503                                        if(!empty($notify_result['processed']) && $old_id && $old_id !== $subscription_id && apply_filters('s2member_pro_cancels_old_rp_before_new_rp', TRUE, array('old__subscr_id' => $old_id, 'subscr_id' => $subscription_id)))
     2504                                                c_ws_plugin__s2member_utilities::cancel_gateway_subscription(!empty($stored_token['old__subscr_gateway']) ? (string)$stored_token['old__subscr_gateway'] : '', $old_id,
     2505                                                        !empty($stored_token['old__subscr_baid']) ? (string)$stored_token['old__subscr_baid'] : '', !empty($stored_token['old__subscr_cid']) ? (string)$stored_token['old__subscr_cid'] : '',
     2506                                                        !empty($stored_token['old__ipn_signup_vars']) && is_array($stored_token['old__ipn_signup_vars']) ? $stored_token['old__ipn_signup_vars'] : array());
     2507
     2508                                        return array_merge(array('ok' => TRUE, 'processed' => !empty($notify_result['processed']), 'duplicate' => !empty($notify_result['duplicate'])), $result);
     2509                                }
    23362510
    23372511                                /**
  • s2member/trunk/src/includes/classes/paypal-webhook-in.inc.php

    r3687613 r3722434  
    390390                                        }
    391391
     392
     393                                        //260928.1538 Standalone Framework buttons are coordinator-backed; a verified activation now recovers all original purchase terms and completes membership even if the browser never calls confirm_subscription.
     394                                        if(strpos((string)$paypal['invoice'], 's2mb-') === 0)
     395                                        {
     396                                                $button_checkout_id = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_gateway_checkout_id_from_invoice((string)$paypal['invoice']);
     397                                                $button_private = $button_checkout_id ? c_ws_plugin__s2member_gateway_checkouts::private_context_get($button_checkout_id) : FALSE;
     398                                                $button_token = is_array($button_private) && !empty($button_private['paypal_checkout']['token']) && is_array($button_private['paypal_checkout']['token']) ? $button_private['paypal_checkout']['token'] : array();
     399                                                $subscription_details = $subscr_id ? c_ws_plugin__s2member_paypal_utilities::paypal_checkout_subscription_details($subscr_id) : array();
     400                                                $button_fulfillment = (!$button_token || !empty($subscription_details['__error']))
     401                                                        ? array('ok' => FALSE, 'error' => 'gateway_checkout_purchase_context_or_provider_missing')
     402                                                        : c_ws_plugin__s2member_paypal_utilities::paypal_checkout_button_subscription_fulfill($subscription_details, $button_token, 'webhook');
     403                                                if(empty($button_fulfillment['ok']))
     404                                                {
     405                                                        c_ws_plugin__s2member_utils_logs::log_entry('paypal-checkout', array('ppco' => 'webhook', 'event' => 'button_subscription_fulfillment_retry', 'subscription_id' => $subscr_id, 'invoice' => (string)$paypal['invoice'], 'error' => !empty($button_fulfillment['error']) ? (string)$button_fulfillment['error'] : 'unknown'));
     406                                                        c_ws_plugin__s2member_paypal_utilities::dedupe_lock_release($event_lock_option);
     407                                                        status_header(500); //260928.1538 Leave the activation event unconsumed so PayPal retries on temporary recovery/Notify failure.
     408                                                        exit();
     409                                                }
     410                                                c_ws_plugin__s2member_paypal_utilities::dedupe_done_mark($event_done_option);
     411                                                c_ws_plugin__s2member_paypal_utilities::dedupe_lock_release($event_lock_option);
     412                                                status_header(200);
     413                                                exit();
     414                                        }
    392415                                        $paypal['txn_type']       = 'subscr_signup'; //260401 Keep webhook activation as a fallback to the legacy signup handler only when checkout did not already handle this Subscription.
    393416                                        $paypal['payment_status'] = 'Completed';
  • s2member/trunk/src/includes/classes/sc-paypal-button-in.inc.php

    r3714948 r3722434  
    298298                                                                $paypal_invoice_input_value = /* s2Member's Unique Code~IP combo. */ uniqid () . "~" . c_ws_plugin__s2member_utils_ip::current();
    299299
     300                                                                //260928.1515 Give each rendered PayPal Checkout button a signed provisional identity; persist its Gateway Checkout only when the buyer starts, never on page render.
     301                                                                $ppco_gateway_checkout_identity = FALSE;
     302                                                                if(c_ws_plugin__s2member_paypal_utilities::paypal_checkout_is_enabled())
     303                                                                {
     304                                                                        $ppco_gateway_checkout_identity = c_ws_plugin__s2member_gateway_checkouts::browser_identity();
     305                                                                        $paypal_invoice_input_value = 's2mb-'.(string)$ppco_gateway_checkout_identity['id'];
     306                                                                }
     307
    300308                                                                $attr["sp_ids_exp"] = /* Combined "sp:ids:expiration hours". */ "sp:" . $attr["ids"] . ":" . $attr["exp"];
    301309
    … …  
    356364                                                                                        'checksum'    => md5($paypal_invoice_input_value.c_ws_plugin__s2member_utils_ip::current().$attr["sp_ids_exp"]),
    357365                                                                                );
     366
     367                                                                                //260928.1515 Bind the PayPal invoice and encrypted purchase terms to the same provisional Gateway Checkout identity, including in anchor/url mode.
     368                                                                                if($ppco_gateway_checkout_identity)
     369                                                                                {
     370                                                                                        $ppco_token['gateway_checkout_id'] = (string)$ppco_gateway_checkout_identity['id'];
     371                                                                                        $ppco_token['gateway_checkout_token'] = (string)$ppco_gateway_checkout_identity['token'];
     372                                                                                }
    358373
    359374                                                                                $ppco_token = urlencode(c_ws_plugin__s2member_utils_encryption::encrypt(serialize($ppco_token)));
    … …  
    422437                                                                                $code .= 'var t="'.esc_js($ppco_token).'";'."\n";
    423438                                                                                $code .= 'var u="'.esc_js($ppco_endpoint).'";'."\n";
     439                                                                                //260928.1739 Bind to the SDK script actually emitted above and encode its URL as JavaScript, not HTML entities.
    424440                                                                                $code .= 'var ns="'.esc_js($ppco_sdk_ns).'";'."\n";
    425                                                                                 $code .= 'var s="'.esc_js($ppco_sdk_src).'";'."\n";
     441                                                                                $code .= 'var sdkId="'.esc_js($ppco_sdk_id).'";'."\n";
     442                                                                                $code .= 'var s='.wp_json_encode($ppco_sdk_src, JSON_HEX_AMP | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT).';'."\n";
    426443                                                                                $code .= 'var cid="'.esc_js($paypal_invoice_input_value).'";'."\n";
    427444                                                                                //260819.0042 Keep standalone Checkout feedback consistent with Pro-Forms while distinguishing cancellation from errors.
    … …  
    429446                                                                                $code .= 'function postTo(url, data){var f=document.createElement("form");f.method="post";f.acceptCharset="UTF-8";f.action=url;for(var k in data){if(!data.hasOwnProperty(k))continue;var i=document.createElement("input");i.type="hidden";i.name=k;i.value=data[k];f.appendChild(i);}document.body.appendChild(f);f.submit();}'."\n"; //260817 Keep signed PayPal Checkout returns in UTF-8.
    430447                                                                                $code .= 'function enc(o){var a=[];for(var k in o){if(!o.hasOwnProperty(k))continue;a.push(encodeURIComponent(k)+"="+encodeURIComponent(o[k]));}return a.join("&");}'."\n";
    431                                                                                 $code .= 'function loadSdk(cb){var P=window[ns];if(P&&P.Buttons){cb(P);return;}var id=ns+"_sdk",tag=document.getElementById(id),done=false;function finish(){if(done)return;done=true;cb(window[ns]&&window[ns].Buttons?window[ns]:null);}function ok(){finish();}function fail(){finish();}if(tag){if(window[ns]&&window[ns].Buttons){finish();return;}if(tag.getAttribute("src")!==s){tag.setAttribute("src",s);}if(tag.readyState==="complete"||tag.readyState==="loaded"){setTimeout(finish,0);return;}tag.addEventListener("load",ok);tag.addEventListener("error",fail);setTimeout(finish,3500);return;}tag=document.createElement("script");tag.id=id;tag.setAttribute("data-namespace",ns);tag.src=s;tag.async=true;tag.onload=ok;tag.onerror=fail;(document.head||document.body||document.documentElement).appendChild(tag);setTimeout(finish,3500);}'."\n";
     448                                                                                $code .= 'function loadSdk(cb){var P=window[ns];if(P&&P.Buttons){cb(P);return;}var id=sdkId,tag=document.getElementById(id),done=false;function finish(){if(done)return;done=true;cb(window[ns]&&window[ns].Buttons?window[ns]:null);}function ok(){finish();}function fail(){finish();}if(tag){if(window[ns]&&window[ns].Buttons){finish();return;}if(tag.getAttribute("src")!==s){tag.setAttribute("src",s);}if(tag.readyState==="complete"||tag.readyState==="loaded"){setTimeout(finish,0);return;}tag.addEventListener("load",ok);tag.addEventListener("error",fail);setTimeout(finish,3500);return;}tag=document.createElement("script");tag.id=id;tag.setAttribute("data-namespace",ns);tag.src=s;tag.async=true;tag.onload=ok;tag.onerror=fail;(document.head||document.body||document.documentElement).appendChild(tag);setTimeout(finish,3500);}'."\n";
    432449                                                                                if($ppco_intent === 'subscription')
    433450                                                                                        {
    434                                                                                                 $code .= 'function getPlanId(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"get_plan_id",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.plan_id)return res.plan_id;throw(res&&res.error?res.error:"plan_get_failed");});}'."\n";
     451                                                                                                //260928.1739 WordPress rendered this inline JS with &#038;&#038; inside throw(res&&res.error), breaking the entire PayPal Button script. Use a ternary and || instead.
     452                                                                                                $code .= 'function getPlanId(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"get_plan_id",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.plan_id)return res.plan_id;throw(res ? (res.error || "plan_get_failed") : "plan_get_failed");});}'."\n";
    435453                                                                                                $code .= 'var planId=null;'."\n";
    436454                                                                                                $code .= 'function createSubscription(data,actions){if(planId)return actions.subscription.create({plan_id:planId,custom_id:cid,application_context:{shipping_preference:"NO_SHIPPING"}});return getPlanId().then(function(pid){planId=pid;return actions.subscription.create({plan_id:planId,custom_id:cid,application_context:{shipping_preference:"NO_SHIPPING"}});});}'."\n";
    437                                                                                                 $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"confirm_subscription",s2member_paypal_checkout_t:t,subscription_id:(data&&data.subscriptionID?data.subscriptionID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res&&res.error?res.error:"subscription_confirm_failed");}).catch(function(e){showErr("Subscription could not be completed. Please try again.");});}'."\n";
     455                                                                                                //260928.1739 WordPress rendered this inline JS with &#038;&#038; inside throw(res&&res.error), breaking the entire PayPal Button script. Use a ternary and || instead.
     456                                                                                                $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"confirm_subscription",s2member_paypal_checkout_t:t,subscription_id:(data&&data.subscriptionID?data.subscriptionID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res ? (res.error || "subscription_confirm_failed") : "subscription_confirm_failed");}).catch(function(e){showErr("Subscription could not be completed. Please try again.");});}'."\n";
    438457                                                                                                $code .= 'function onCancel(){showInfo("Subscription cancelled.");}'."\n";
    439458                                                                                                $code .= 'function onError(err){var m="PayPal error. Please try again.";try{if(err){if(typeof err==="string")m="PayPal error: "+err;else if(err.message)m="PayPal error: "+err.message;}}catch(x){}showErr(m);}'."\n";
    … …  
    442461                                                                                else
    443462                                                                                        {
    444                                                                                                 $code .= 'function createOrder(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"create_order",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.order_id)return res.order_id;throw(res&&res.error?res.error:"order_create_failed");});}'."\n";
    445                                                                                                 $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"capture_order",s2member_paypal_checkout_t:t,order_id:(data&&data.orderID?data.orderID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res&&res.error?res.error:"order_capture_failed");}).catch(function(e){showErr("Payment could not be completed. Please try again.");});}'."\n";
     463                                                                                                $code .= 'function request(op,vars){var body={s2member_paypal_checkout_op:op,s2member_paypal_checkout_t:t};for(var k in (vars||{})){if(vars.hasOwnProperty(k))body[k]=vars[k];}return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc(body)}).then(function(r){return r.json();});}'."\n";
     464                                                                                                $code .= 'function createOrder(){return request("create_order").then(function(res){if(res&&res.order_id)return res.order_id;throw(res ? (res.error || "order_create_failed") : "order_create_failed");});}'."\n";
     465                                                                                                //260928.1703 A completed PayPal capture can reach the verified webhook even when the browser loses its response. Poll only our durable state and submit its saved signed handoff; never issue a second capture on uncertainty.
     466                                                                                                $code .= 'function recoverOrder(n){return request("get_order_status").then(function(res){if(res&&res.fulfilled&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}if(n>=20)throw "order_confirmation_pending";return new Promise(function(resolve){setTimeout(resolve,1200);}).then(function(){return recoverOrder(n+1);});});}'."\n";
     467                                                                                                $code .= 'function onApprove(data){var oid=data&&data.orderID?data.orderID:"";return request("capture_order",{order_id:oid}).catch(function(){return {recoverable:true};}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}if(res&&res.recoverable)return recoverOrder(0).catch(function(){showInfo("Payment confirmation is pending. Please do not pay again; check your email or contact support.");});showErr("Payment could not be confirmed. Please contact support before trying again.");});}'."\n";
    446468                                                                                                $code .= 'function onCancel(){showInfo("Payment cancelled.");}'."\n";
    447469                                                                                                $code .= 'function onError(err){var m="PayPal error. Please try again.";try{if(err){if(typeof err==="string")m="PayPal error: "+err;else if(err.message)m="PayPal error: "+err.message;}}catch(x){}showErr(m);}'."\n";
    … …  
    511533                                                                $paypal_invoice_input_value = /* s2Member's Unique Code~IP combo. */ uniqid () . "~" . c_ws_plugin__s2member_utils_ip::current();
    512534
     535                                                                //260928.1515 Give each rendered PayPal Checkout button a signed provisional identity; persist its Gateway Checkout only when the buyer starts, never on page render.
     536                                                                $ppco_gateway_checkout_identity = FALSE;
     537                                                                if(c_ws_plugin__s2member_paypal_utilities::paypal_checkout_is_enabled())
     538                                                                {
     539                                                                        $ppco_gateway_checkout_identity = c_ws_plugin__s2member_gateway_checkouts::browser_identity();
     540                                                                        $paypal_invoice_input_value = 's2mb-'.(string)$ppco_gateway_checkout_identity['id'];
     541                                                                }
     542
    513543                                                                $attr["level_ccaps_eotper"] = ($attr["rr"] === "BN" && $attr["rt"] !== "L") ? $attr["level"] . ":" . $attr["ccaps"] . ":" . $attr["rp"] . " " . $attr["rt"] : $attr["level"] . ":" . $attr["ccaps"];
    514544                                                                $attr["level_ccaps_eotper"] = /* Clean any trailing separators from this string. */ rtrim ($attr["level_ccaps_eotper"], ":");
    … …  
    571601                                                                                        'checksum'    => md5($paypal_invoice_input_value.c_ws_plugin__s2member_utils_ip::current().$attr["level_ccaps_eotper"]),
    572602                                                                                );
     603
     604                                                                                //260928.1515 Bind the PayPal invoice and encrypted purchase terms to the same provisional Gateway Checkout identity, including in anchor/url mode.
     605                                                                                if($ppco_gateway_checkout_identity)
     606                                                                                {
     607                                                                                        $ppco_token['gateway_checkout_id'] = (string)$ppco_gateway_checkout_identity['id'];
     608                                                                                        $ppco_token['gateway_checkout_token'] = (string)$ppco_gateway_checkout_identity['token'];
     609                                                                                }
    573610
    574611                                                                                $ppco_token = urlencode(c_ws_plugin__s2member_utils_encryption::encrypt(serialize($ppco_token)));
    … …  
    637674                                                                                $code .= 'var t="'.esc_js($ppco_token).'";'."\n";
    638675                                                                                $code .= 'var u="'.esc_js($ppco_endpoint).'";'."\n";
     676                                                                                //260928.1739 Bind to the SDK script actually emitted above and encode its URL as JavaScript, not HTML entities.
    639677                                                                                $code .= 'var ns="'.esc_js($ppco_sdk_ns).'";'."\n";
    640                                                                                 $code .= 'var s="'.esc_js($ppco_sdk_src).'";'."\n";
     678                                                                                $code .= 'var sdkId="'.esc_js($ppco_sdk_id).'";'."\n";
     679                                                                                $code .= 'var s='.wp_json_encode($ppco_sdk_src, JSON_HEX_AMP | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT).';'."\n";
    641680                                                                                $code .= 'var cid="'.esc_js($paypal_invoice_input_value).'";'."\n";
    642681                                                                                //260819.0042 Keep standalone Checkout feedback consistent with Pro-Forms while distinguishing cancellation from errors.
    … …  
    644683                                                                                $code .= 'function postTo(url, data){var f=document.createElement("form");f.method="post";f.acceptCharset="UTF-8";f.action=url;for(var k in data){if(!data.hasOwnProperty(k))continue;var i=document.createElement("input");i.type="hidden";i.name=k;i.value=data[k];f.appendChild(i);}document.body.appendChild(f);f.submit();}'."\n"; //260817 Keep signed PayPal Checkout returns in UTF-8.
    645684                                                                                $code .= 'function enc(o){var a=[];for(var k in o){if(!o.hasOwnProperty(k))continue;a.push(encodeURIComponent(k)+"="+encodeURIComponent(o[k]));}return a.join("&");}'."\n";
    646                                                                                 $code .= 'function loadSdk(cb){var P=window[ns];if(P&&P.Buttons){cb(P);return;}var id=ns+"_sdk",tag=document.getElementById(id),done=false;function finish(){if(done)return;done=true;cb(window[ns]&&window[ns].Buttons?window[ns]:null);}function ok(){finish();}function fail(){finish();}if(tag){if(window[ns]&&window[ns].Buttons){finish();return;}if(tag.getAttribute("src")!==s){tag.setAttribute("src",s);}if(tag.readyState==="complete"||tag.readyState==="loaded"){setTimeout(finish,0);return;}tag.addEventListener("load",ok);tag.addEventListener("error",fail);setTimeout(finish,3500);return;}tag=document.createElement("script");tag.id=id;tag.setAttribute("data-namespace",ns);tag.src=s;tag.async=true;tag.onload=ok;tag.onerror=fail;(document.head||document.body||document.documentElement).appendChild(tag);setTimeout(finish,3500);}'."\n";
     685                                                                                $code .= 'function loadSdk(cb){var P=window[ns];if(P&&P.Buttons){cb(P);return;}var id=sdkId,tag=document.getElementById(id),done=false;function finish(){if(done)return;done=true;cb(window[ns]&&window[ns].Buttons?window[ns]:null);}function ok(){finish();}function fail(){finish();}if(tag){if(window[ns]&&window[ns].Buttons){finish();return;}if(tag.getAttribute("src")!==s){tag.setAttribute("src",s);}if(tag.readyState==="complete"||tag.readyState==="loaded"){setTimeout(finish,0);return;}tag.addEventListener("load",ok);tag.addEventListener("error",fail);setTimeout(finish,3500);return;}tag=document.createElement("script");tag.id=id;tag.setAttribute("data-namespace",ns);tag.src=s;tag.async=true;tag.onload=ok;tag.onerror=fail;(document.head||document.body||document.documentElement).appendChild(tag);setTimeout(finish,3500);}'."\n";
    647686                                                                                if($ppco_intent === 'subscription')
    648687                                                                                        {
    649                                                                                                 $code .= 'function getPlanId(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"get_plan_id",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.plan_id)return res.plan_id;throw(res&&res.error?res.error:"plan_get_failed");});}'."\n";
     688                                                                                                $code .= 'function getPlanId(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"get_plan_id",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.plan_id)return res.plan_id;throw(res ? (res.error || "plan_get_failed") : "plan_get_failed");});}'."\n";
    650689                                                                                                $code .= 'var planId=null;'."\n";
    651690                                                                                                $code .= 'function createSubscription(data,actions){if(planId)return actions.subscription.create({plan_id:planId,custom_id:cid,application_context:{shipping_preference:"NO_SHIPPING"}});return getPlanId().then(function(pid){planId=pid;return actions.subscription.create({plan_id:planId,custom_id:cid,application_context:{shipping_preference:"NO_SHIPPING"}});});}'."\n";
    652                                                                                                 $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"confirm_subscription",s2member_paypal_checkout_t:t,subscription_id:(data&&data.subscriptionID?data.subscriptionID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res&&res.error?res.error:"subscription_confirm_failed");}).catch(function(e){showErr("Subscription could not be completed. Please try again.");});}'."\n";
     691                                                                                                //260928.1739 WordPress rendered this inline JS with &#038;&#038; inside throw(res&&res.error), breaking the entire PayPal Button script. Use a ternary and || instead.
     692                                                                                                $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"confirm_subscription",s2member_paypal_checkout_t:t,subscription_id:(data&&data.subscriptionID?data.subscriptionID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res ? (res.error || "subscription_confirm_failed") : "subscription_confirm_failed");}).catch(function(e){showErr("Subscription could not be completed. Please try again.");});}'."\n";
    653693                                                                                                $code .= 'function onCancel(){showInfo("Subscription cancelled.");}'."\n";
    654694                                                                                                $code .= 'function onError(err){var m="PayPal error. Please try again.";try{if(err){if(typeof err==="string")m="PayPal error: "+err;else if(err.message)m="PayPal error: "+err.message;}}catch(x){}showErr(m);}'."\n";
    … …  
    657697                                                                                else
    658698                                                                                        {
    659                                                                                                 $code .= 'function createOrder(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"create_order",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.order_id)return res.order_id;throw(res&&res.error?res.error:"order_create_failed");});}'."\n";
    660                                                                                                 $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"capture_order",s2member_paypal_checkout_t:t,order_id:(data&&data.orderID?data.orderID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res&&res.error?res.error:"order_capture_failed");}).catch(function(e){showErr("Payment could not be completed. Please try again.");});}'."\n";
     699                                                                                                $code .= 'function request(op,vars){var body={s2member_paypal_checkout_op:op,s2member_paypal_checkout_t:t};for(var k in (vars||{})){if(vars.hasOwnProperty(k))body[k]=vars[k];}return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc(body)}).then(function(r){return r.json();});}'."\n";
     700                                                                                                $code .= 'function createOrder(){return request("create_order").then(function(res){if(res&&res.order_id)return res.order_id;throw(res ? (res.error || "order_create_failed") : "order_create_failed");});}'."\n";
     701                                                                                                //260928.1703 A completed PayPal capture can reach the verified webhook even when the browser loses its response. Poll only our durable state and submit its saved signed handoff; never issue a second capture on uncertainty.
     702                                                                                                $code .= 'function recoverOrder(n){return request("get_order_status").then(function(res){if(res&&res.fulfilled&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}if(n>=20)throw "order_confirmation_pending";return new Promise(function(resolve){setTimeout(resolve,1200);}).then(function(){return recoverOrder(n+1);});});}'."\n";
     703                                                                                                $code .= 'function onApprove(data){var oid=data&&data.orderID?data.orderID:"";return request("capture_order",{order_id:oid}).catch(function(){return {recoverable:true};}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}if(res&&res.recoverable)return recoverOrder(0).catch(function(){showInfo("Payment confirmation is pending. Please do not pay again; check your email or contact support.");});showErr("Payment could not be confirmed. Please contact support before trying again.");});}'."\n";
    661704                                                                                                $code .= 'function onCancel(){showInfo("Payment cancelled.");}'."\n";
    662705                                                                                                $code .= 'function onError(err){var m="PayPal error. Please try again.";try{if(err){if(typeof err==="string")m="PayPal error: "+err;else if(err.message)m="PayPal error: "+err.message;}}catch(x){}showErr(m);}'."\n";
    … …  
    725768
    726769                                                                $paypal_invoice_input_value = /* s2Member's Unique Code~IP combo. */ uniqid () . "~" . c_ws_plugin__s2member_utils_ip::current();
     770
     771                                                                //260928.1515 Give each rendered PayPal Checkout button a signed provisional identity; persist its Gateway Checkout only when the buyer starts, never on page render.
     772                                                                $ppco_gateway_checkout_identity = FALSE;
     773                                                                if(c_ws_plugin__s2member_paypal_utilities::paypal_checkout_is_enabled())
     774                                                                {
     775                                                                        $ppco_gateway_checkout_identity = c_ws_plugin__s2member_gateway_checkouts::browser_identity();
     776                                                                        $paypal_invoice_input_value = 's2mb-'.(string)$ppco_gateway_checkout_identity['id'];
     777                                                                }
    727778
    728779                                                                $attr["desc"] = (!$attr["desc"]) ? $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level" . $attr["level"] . "_label"] : $attr["desc"];
    … …  
    816867                                                                                );
    817868
     869                                                                                //260928.1515 Bind the PayPal invoice and encrypted purchase terms to the same provisional Gateway Checkout identity, including in anchor/url mode.
     870                                                                                if($ppco_gateway_checkout_identity)
     871                                                                                {
     872                                                                                        $ppco_token['gateway_checkout_id'] = (string)$ppco_gateway_checkout_identity['id'];
     873                                                                                        $ppco_token['gateway_checkout_token'] = (string)$ppco_gateway_checkout_identity['token'];
     874                                                                                }
     875
    818876                                                                                $ppco_token = urlencode(c_ws_plugin__s2member_utils_encryption::encrypt(serialize($ppco_token)));
    819877
    … …  
    880938                                                                                $code .= 'var t="'.esc_js($ppco_token).'";'."\n";
    881939                                                                                $code .= 'var u="'.esc_js($ppco_endpoint).'";'."\n";
     940                                                                                //260928.1739 Bind to the SDK script actually emitted above and encode its URL as JavaScript, not HTML entities.
    882941                                                                                $code .= 'var ns="'.esc_js($ppco_sdk_ns).'";'."\n";
    883                                                                                 $code .= 'var s="'.esc_js($ppco_sdk_src).'";'."\n";
     942                                                                                $code .= 'var sdkId="'.esc_js($ppco_sdk_id).'";'."\n";
     943                                                                                $code .= 'var s='.wp_json_encode($ppco_sdk_src, JSON_HEX_AMP | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT).';'."\n";
    884944                                                                                $code .= 'var cid="'.esc_js($paypal_invoice_input_value).'";'."\n";
    885945                                                                                //260819.0042 Keep standalone Checkout feedback consistent with Pro-Forms while distinguishing cancellation from errors.
    … …  
    887947                                                                                $code .= 'function postTo(url, data){var f=document.createElement("form");f.method="post";f.acceptCharset="UTF-8";f.action=url;for(var k in data){if(!data.hasOwnProperty(k))continue;var i=document.createElement("input");i.type="hidden";i.name=k;i.value=data[k];f.appendChild(i);}document.body.appendChild(f);f.submit();}'."\n"; //260817 Keep signed PayPal Checkout returns in UTF-8.
    888948                                                                                $code .= 'function enc(o){var a=[];for(var k in o){if(!o.hasOwnProperty(k))continue;a.push(encodeURIComponent(k)+"="+encodeURIComponent(o[k]));}return a.join("&");}'."\n";
    889                                                                                 $code .= 'function loadSdk(cb){var P=window[ns];if(P&&P.Buttons){cb(P);return;}var id=ns+"_sdk",tag=document.getElementById(id),done=false;function finish(){if(done)return;done=true;cb(window[ns]&&window[ns].Buttons?window[ns]:null);}function ok(){finish();}function fail(){finish();}if(tag){if(window[ns]&&window[ns].Buttons){finish();return;}if(tag.getAttribute("src")!==s){tag.setAttribute("src",s);}if(tag.readyState==="complete"||tag.readyState==="loaded"){setTimeout(finish,0);return;}tag.addEventListener("load",ok);tag.addEventListener("error",fail);setTimeout(finish,3500);return;}tag=document.createElement("script");tag.id=id;tag.setAttribute("data-namespace",ns);tag.src=s;tag.async=true;tag.onload=ok;tag.onerror=fail;(document.head||document.body||document.documentElement).appendChild(tag);setTimeout(finish,3500);}'."\n";
     949                                                                                $code .= 'function loadSdk(cb){var P=window[ns];if(P&&P.Buttons){cb(P);return;}var id=sdkId,tag=document.getElementById(id),done=false;function finish(){if(done)return;done=true;cb(window[ns]&&window[ns].Buttons?window[ns]:null);}function ok(){finish();}function fail(){finish();}if(tag){if(window[ns]&&window[ns].Buttons){finish();return;}if(tag.getAttribute("src")!==s){tag.setAttribute("src",s);}if(tag.readyState==="complete"||tag.readyState==="loaded"){setTimeout(finish,0);return;}tag.addEventListener("load",ok);tag.addEventListener("error",fail);setTimeout(finish,3500);return;}tag=document.createElement("script");tag.id=id;tag.setAttribute("data-namespace",ns);tag.src=s;tag.async=true;tag.onload=ok;tag.onerror=fail;(document.head||document.body||document.documentElement).appendChild(tag);setTimeout(finish,3500);}'."\n";
    890950                                                                                if($ppco_intent === 'subscription')
    891951                                                                                        {
    892                                                                                                 $code .= 'function getPlanId(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"get_plan_id",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.plan_id)return res.plan_id;throw(res&&res.error?res.error:"plan_get_failed");});}'."\n";
    893                                                                                                 $code .= 'var planId=null;'."\n";
    894                                                                                                 $code .= 'function createSubscription(data,actions){if(planId)return actions.subscription.create({plan_id:planId,custom_id:cid,application_context:{shipping_preference:"NO_SHIPPING"}});return getPlanId().then(function(pid){planId=pid;return actions.subscription.create({plan_id:planId,custom_id:cid,application_context:{shipping_preference:"NO_SHIPPING"}});});}'."\n";
    895                                                                                                 $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"confirm_subscription",s2member_paypal_checkout_t:t,subscription_id:(data&&data.subscriptionID?data.subscriptionID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res&&res.error?res.error:"subscription_confirm_failed");}).catch(function(e){showErr("Subscription could not be completed. Please try again.");});}'."\n";
     952                                                                                                //260928.1540 Move Framework subscription creation onto the same server-side provider/idempotency path Pro-Forms use. Only the persisted ID reaches the PayPal SDK for buyer approval.
     953                                                                                                $code .= 'function request(op,vars){var body={s2member_paypal_checkout_op:op,s2member_paypal_checkout_t:t};for(var k in (vars||{})){if(vars.hasOwnProperty(k))body[k]=vars[k];}return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc(body)}).then(function(r){return r.json();});}' . "\n";
     954                                                                                                $code .= 'function waitForSubscription(n){return request("get_subscription_id").then(function(res){if(res&&res.subscription_id)return res.subscription_id;if(n>=20)throw "subscription_create_unresolved";return new Promise(function(resolve){setTimeout(resolve,1000);}).then(function(){return waitForSubscription(n+1);});});}' . "\n";
     955                                                                                                $code .= 'function createSubscription(){return request("create_subscription").then(function(res){if(res&&res.subscription_id)return res.subscription_id;if(res&&res.recoverable)return waitForSubscription(0);throw(res ? (res.error || "subscription_create_failed") : "subscription_create_failed");});}' . "\n";
     956                                                                                                //260928.1540 PayPal can report APPROVED before subscription ACTIVATED; poll the same backend until entitlement is confirmed or the activation webhook fulfills off-session.
     957                                                                                                //260928.1739 WordPress rendered this inline JS with &#038;&#038; inside throw(res&&res.error), breaking the entire PayPal Button script. Use a ternary and || instead.
     958                                                                                                $code .= 'function onApprove(data){var sid=data&&data.subscriptionID?data.subscriptionID:"";function finish(n){return request("confirm_subscription",{subscription_id:sid}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}if(res&&res.pending_activation&&n<25){return new Promise(function(resolve){setTimeout(resolve,1200);}).then(function(){return finish(n+1);});}throw(res ? (res.error || "subscription_confirm_failed") : "subscription_confirm_failed");});}return finish(0).catch(function(){showErr("Subscription could not be completed. Please try again.");});}' . "\n";
    896959                                                                                                $code .= 'function onCancel(){showInfo("Subscription cancelled.");}'."\n";
    897960                                                                                                $code .= 'function onError(err){var m="PayPal error. Please try again.";try{if(err){if(typeof err==="string")m="PayPal error: "+err;else if(err.message)m="PayPal error: "+err.message;}}catch(x){}showErr(m);}'."\n";
    … …  
    900963                                                                                else
    901964                                                                                        {
    902                                                                                                 $code .= 'function createOrder(){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"create_order",s2member_paypal_checkout_t:t})}).then(function(r){return r.json();}).then(function(res){if(res&&res.order_id)return res.order_id;throw(res&&res.error?res.error:"order_create_failed");});}'."\n";
    903                                                                                                 $code .= 'function onApprove(data){return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc({s2member_paypal_checkout_op:"capture_order",s2member_paypal_checkout_t:t,order_id:(data&&data.orderID?data.orderID:"")})}).then(function(r){return r.json();}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}throw(res&&res.error?res.error:"order_capture_failed");}).catch(function(e){showErr("Payment could not be completed. Please try again.");});}'."\n";
     965                                                                                                $code .= 'function request(op,vars){var body={s2member_paypal_checkout_op:op,s2member_paypal_checkout_t:t};for(var k in (vars||{})){if(vars.hasOwnProperty(k))body[k]=vars[k];}return fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},body:enc(body)}).then(function(r){return r.json();});}'."\n";
     966                                                                                                $code .= 'function createOrder(){return request("create_order").then(function(res){if(res&&res.order_id)return res.order_id;throw(res ? (res.error || "order_create_failed") : "order_create_failed");});}'."\n";
     967                                                                                                //260928.1703 A completed PayPal capture can reach the verified webhook even when the browser loses its response. Poll only our durable state and submit its saved signed handoff; never issue a second capture on uncertainty.
     968                                                                                                $code .= 'function recoverOrder(n){return request("get_order_status").then(function(res){if(res&&res.fulfilled&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}if(n>=20)throw "order_confirmation_pending";return new Promise(function(resolve){setTimeout(resolve,1200);}).then(function(){return recoverOrder(n+1);});});}'."\n";
     969                                                                                                $code .= 'function onApprove(data){var oid=data&&data.orderID?data.orderID:"";return request("capture_order",{order_id:oid}).catch(function(){return {recoverable:true};}).then(function(res){if(res&&res.rtn_url&&res.rtn_post){postTo(res.rtn_url,res.rtn_post);return;}if(res&&res.recoverable)return recoverOrder(0).catch(function(){showInfo("Payment confirmation is pending. Please do not pay again; check your email or contact support.");});showErr("Payment could not be confirmed. Please contact support before trying again.");});}'."\n";
    904970                                                                                                $code .= 'function onCancel(){showInfo("Payment cancelled.");}'."\n";
    905971                                                                                                $code .= 'function onError(err){var m="PayPal error. Please try again.";try{if(err){if(typeof err==="string")m="PayPal error: "+err;else if(err.message)m="PayPal error: "+err.message;}}catch(x){}showErr(m);}'."\n";
  • s2member/trunk/src/includes/classes/utils-ip.inc.php

    r1542973 r3722434  
    11<?php
    22// @codingStandardsIgnoreFile
     3if(!defined('WPINC')) //260928.0402 This utility is only loaded through WordPress/s2Member; reject direct web requests.
     4        exit('Do not access this file directly.');
     5
    36/**
    47 * IP utilities.
  • s2member/trunk/src/includes/externals/aweber/aweber.php

    r1542973 r3722434  
    11<?php
    22// @codingStandardsIgnoreFile
     3if(!defined('WPINC')) //260928.0402 This API library is only loaded through WordPress/s2Member; reject direct web requests.
     4        exit('Do not access this file directly.');
     5
    36require_once('exceptions.php');
    47require_once('oauth_adapter.php');
  • s2member/trunk/src/includes/externals/aweber/aweber_api.php

    r1542973 r3722434  
    11<?php
    22// @codingStandardsIgnoreFile
     3
     4if(!defined('WPINC')) //260928.0402 This API entry point is only loaded through WordPress/s2Member; reject direct web requests.
     5        exit('Do not access this file directly.');
    36
    47if (class_exists('AWeberAPI')) {
  • s2member/trunk/src/includes/externals/aweber/oauth_application.php

    r3671073 r3722434  
    11<?php
    22// @codingStandardsIgnoreFile
     3if(!defined('WPINC')) //260928.0402 This OAuth library is only loaded through WordPress/s2Member; reject direct web requests.
     4        exit('Do not access this file directly.');
     5
    36if (!class_exists('CurlObject')) require_once('curl_object.php');
    47if (!class_exists('CurlResponse')) require_once('curl_response.php');
  • s2member/trunk/src/includes/externals/markdown/nc-markdown.inc.php

    r3671073 r3722434  
    11<?php
    22// @codingStandardsIgnoreFile
     3if(!defined('WPINC')) //260928.0402 This parser is only loaded through WordPress/s2Member; reject direct web requests.
     4        exit('Do not access this file directly.');
     5
    36/**
    47* PHP Markdown class.
  • s2member/trunk/src/includes/hooks.inc.php

    r3701026 r3722434  
    278278register_deactivation_hook($GLOBALS['WS_PLUGIN__']['s2member']['c']['plugin_basename'], 'c_ws_plugin__s2member_installation::deactivate');
    279279register_uninstall_hook($GLOBALS['WS_PLUGIN__']['s2member']['c']['plugin_basename'], 'c_ws_plugin__s2member_installation::uninstall');
    280 //221024 Now that Pro add-on doesn't have to match Framework's version to remain active, it's safer to auto-update Framework.
    281 // add_filter('auto_update_plugin', 'c_ws_plugin__s2member_installation::auto_update_filter', PHP_INT_MAX-10, 2); // Disallow s2Member update.
  • s2member/trunk/src/includes/menu-pages/menu-pages-s.js

    r3701026 r3722434  
    180180                                                                var $fields = $('input#ws-plugin--s2member-custom-reg-fields');
    181181                                                                var $configuration = $('div#ws-plugin--s2member-custom-reg-field-configuration');
    182                                                                 var fields = ($fields.val()) ? $.JSON.parse($fields.val()) : [];
     182                                                                //260928.0402 Use the standard JSON API provided natively or by WordPress's JSON2 compatibility dependency.
     183                                                                var fields = ($fields.val()) ? JSON.parse($fields.val()) : [];
    183184
    184185                                                                fields = /* Force fields to an array. */ (fields instanceof Array) ? fields : [];
    … …  
    382383                                                                var updateFields = /* Update hidden input value. */ function()
    383384                                                                        {
    384                                                                                 $fields.val(((fields.length > 0) ? $.JSON.stringify(fields) : ''));
     385                                                                                $fields.val(((fields.length > 0) ? JSON.stringify(fields) : ''));
    385386                                                                        };
    386387                                                                var fieldId2Var = /* Convert ids to variables. */ function(fieldId)
  • s2member/trunk/src/includes/menu-pages/menu-pages-s.min.js

    r3701026 r3722434  
    1 jQuery(document).ready(function($){var esc_attr=esc_html=function(e){return/[&\<\>"']/.test(e=String(e))&&(e=e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),e=e.replace(/"/g,"&quot;").replace(/'/g,"&#039;")),e};if(ws_plugin__s2member_generateSecurityKey=function(e){e||(e="#ws-plugin--s2member-sec-encryption-key");for(var t=function(e,t){return e=arguments.length<1?0:e,t=arguments.length<2?2147483647:t,Math.floor(Math.random()*(t-e+1))+e},s="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#%^&*()",l=0,r="";l<64;l++)r+=s.substr(t(0,s.length-1),1);return $(e).val(r),!1},location.href.match(/page\=ws-plugin--s2member/)&&$("input.ws-plugin--s2member-update-roles-button, input.ws-plugin--s2member-reset-roles-button").click(function(){var e=$(this);e.val("one moment please ...");var t='<?php echo (int)$GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; ?>',s=e.hasClass("ws-plugin--s2member-reset-roles-button")?"Reset":"Update";return $.post(ajaxurl,{action:"ws_plugin__s2member_update_roles_via_ajax",ws_plugin__s2member_update_roles_via_ajax:'<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (wp_create_nonce ("ws-plugin--s2member-update-roles-via-ajax")); ?>'},function(l){"1"===l?(alert("s2Member's Roles/Capabilities "+("reset"===s.toLowerCase()?"have been successfully reset":"updated successfully")+".\nYour installation of s2Member has Membership Levels 0-"+t+"."),e.val(s+" Roles/Capabilities")):"l"===l?(alert("Sorry, your request failed.\ns2Member's Roles/Capabilities are locked by Filter:\nws_plugin__s2member_lock_roles_caps"),e.val(s+" Roles/Capabilities")):(alert("Sorry, your request failed.\nAccess denied. Do you have the ability to `create_users`?"),e.val(s+" Roles/Capabilities"))}),!1}),location.href.match(/page\=ws-plugin--s2member-logs/)){$("input#ws-plugin--s2member-gateway-debug-logs-extensive-1").click(function(){var e=($(this),!!this.checked);e&&$("input#ws-plugin--s2member-gateway-debug-logs-1").attr("checked","checked")});var $toggles=$("a.ws-plugin--s2member-log-file-viewport-toggle");$toggles.click(function(){return $("textarea#ws-plugin--s2member-log-file-viewer").each(function(){var e=$(this);"expanded"!==e.attr("data-state")?(e.css({height:e.prop("scrollHeight")+50+"px","overflow-y":"auto"}),$toggles.html("&#8657; normalize viewport &#10073;"),e.attr("data-state","expanded")):(e.css({height:"auto","overflow-y":"scroll"}),$toggles.html("&#8659; expand viewport &#8659;"),e.attr("data-state","scrolling"))}),!1})}if(location.href.match(/page\=ws-plugin--s2member-mms-ops/)&&($("select#ws-plugin--s2member-mms-registration-file").change(function(){if("wp-signup"===$(this).val()){var e=$("select#ws-plugin--s2member-mms-registration-grants").val(),t=$("input#ws-plugin--s2member-mms-registration-blogs-level0").val();$("div#ws-plugin--s2member-mms-registration-support-package-details-wrapper").show(),$("div.ws-plugin--s2member-mms-registration-wp-login, table.ws-plugin--s2member-mms-registration-wp-login").hide(),$("div.ws-plugin--s2member-mms-registration-wp-signup, table.ws-plugin--s2member-mms-registration-wp-signup").show(),$("div.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0, table.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0")["all"===e?"show":"hide"](),$("input#ws-plugin--s2member-mms-registration-blogs-level0").val("all"===e?t>0?t:"1":"0")}else if("wp-login"===$(this).val()){var e=$("select#ws-plugin--s2member-mms-registration-grants").val(),t=$("input#ws-plugin--s2member-mms-registration-blogs-level0").val();$("div#ws-plugin--s2member-mms-registration-support-package-details-wrapper").hide(),$("div.ws-plugin--s2member-mms-registration-wp-login, table.ws-plugin--s2member-mms-registration-wp-login").show(),$("div.ws-plugin--s2member-mms-registration-wp-signup, table.ws-plugin--s2member-mms-registration-wp-signup").hide(),$("div.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0, table.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0").hide(),$("input#ws-plugin--s2member-mms-registration-blogs-level0").val("0")}}).trigger("change"),$("select#ws-plugin--s2member-mms-registration-grants").change(function(){$("select#ws-plugin--s2member-mms-registration-file").trigger("change")})),location.href.match(/page\=ws-plugin--s2member-gen-ops/)&&(ws_plugin__s2member_enableSecurityKey=function(){return confirm("Edit Security Encryption Key?\n\nChanging this key will invalidate existing data that was encrypted with the current effective key, which can include access links, registration/tracking cookies, pending transaction-return data, and values encrypted through s2Member's API.\n\nContinue only if you intend to change or recover the key. Keep the current key backed up; s2Member also retains a history of recent Security Encryption Keys configured in s2Member.")&&$("input#ws-plugin--s2member-sec-encryption-key").removeAttr("disabled"),!1},ws_plugin__s2member_securityKeyHistory=function(){return $("div#ws-plugin--s2member-sec-encryption-key-history").toggle(),!1},$("select#ws-plugin--s2member-new-user-emails-enabled").change(function(){var e=$("input#ws-plugin--s2member-pluggables-wp-new-user-notification"),t=$(this),s=$("div#ws-plugin--s2member-new-user-emails");"0"===e.val()||"0"===t.val()?("0"===e.val()?t.attr("disabled","disabled"):t.removeAttr("disabled"),$(":input",s).attr("disabled","disabled"),s.css("opacity","0.5")):(t.removeAttr("disabled"),$(":input",s).removeAttr("disabled"),s.css("opacity",""))}).trigger("change"),$("select#ws-plugin--s2member-login-reg-design-enabled").change(function(){var e=$(this),t=$("div#ws-plugin--s2member-login-reg-design");"0"===e.val()?($(":input",t).attr("disabled","disabled"),t.css("opacity","0.5"),t.hide()):($(":input",t).removeAttr("disabled"),t.css("opacity",""),t.show())}).trigger("change"),$("select#ws-plugin--s2member-custom-reg-password").change(function(){var e=$(this),t=$("div#ws-plugin--s2member-new-user-email-details");"1"===e.val()?t.css("opacity","0.5"):t.css("opacity","")}).trigger("change"),$("input#ws-plugin--s2member-custom-reg-fields").length&&$("div#ws-plugin--s2member-custom-reg-field-configuration").length&&!function(){var e,t,s,l,r,o,a=$("input#ws-plugin--s2member-custom-reg-fields"),i=$("div#ws-plugin--s2member-custom-reg-field-configuration"),n=a.val()?$.JSON.parse(a.val()):[];for(n=n instanceof Array?n:[],t={section:"no",sectitle:"",id:"",label:"",type:"text",deflt:"",options:"",expected:"",required:"yes",levels:"all",editable:"yes",classes:"",styles:"",attrs:""},e=0;e<n.length;e++)n[e]=$.extend(!0,{},t,n[e]);s='<div id="ws-plugin--s2member-custom-reg-field-configuration-tools"></div>',l='<table id="ws-plugin--s2member-custom-reg-field-configuration-table"></table>',i.html(s+l),r=$("div#ws-plugin--s2member-custom-reg-field-configuration-tools"),o=$("table#ws-plugin--s2member-custom-reg-field-configuration-table"),ws_plugin__s2member_customRegFieldSectionChange=function(e){var t=$(e).val(),s="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle";"yes"===t?$(s).css("display",""):$(s).css("display","none")},ws_plugin__s2member_customRegFieldTypeChange=function(e){var t=$(e).val(),s="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt",l="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-options",r="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected";t.match(/^(text|textarea)$/)?$(s).css("display",""):$(s).css("display","none"),t.match(/^(select|selects|checkboxes|radios)$/)?$(l).css("display",""):$(l).css("display","none"),t.match(/^(text|textarea)$/)?$(r).css("display",""):$(r).css("display","none")},ws_plugin__s2member_customRegFieldDelete=function(e){for(var t=new Array,s=0;s<n.length;s++)s!==e&&t.push(n[s]);n=t,m(),b()},ws_plugin__s2member_customRegFieldMoveUp=function(e){if("object"==typeof n[e]&&"object"==typeof n[e-1]){var t=n[e-1],s=n[e];n[e-1]=s,n[e]=t,m(),b()}},ws_plugin__s2member_customRegFieldMoveDown=function(e){if("object"==typeof n[e]&&"object"==typeof n[e+1]){var t=n[e+1],s=n[e];n[e+1]=s,n[e]=t,m(),b()}},ws_plugin__s2member_customRegFieldCreate=function(){var e=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),t={};$(":input[property]",e).each(function(){var e=$(this),s=e.attr("property"),l=$.trim(e.val());t[s]=l}),(t=c(t))&&(n.push(t),m(),g(),b(),d(),setTimeout(function(){"tr.ws-plugin--s2member-custom-reg-field-configuration-table-row-"+(n.length-1);alert('Field created successfully.\n* Remember to "Save All Changes".')},500))},ws_plugin__s2member_customRegFieldUpdate=function(e){var t=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),s={};$(":input[property]",t).each(function(){var e=$(this),t=e.attr("property"),l=$.trim(e.val());s[t]=l}),(s=c(s,e))&&(n[e]=s,m(),g(),b(),d(),setTimeout(function(){alert('Field updated successfully.\n* Remember to "Save All Changes".')},500))},ws_plugin__s2member_customRegFieldAdd=function(){g(!0)},ws_plugin__s2member_customRegFieldEdit=function(e){g(!1,e),d()},ws_plugin__s2member_customRegFieldCancel=function(){g(),d()};var c=function(e,s){var l,r,o="number"==typeof s&&"object"==typeof n[s],a=[];if("object"!=typeof e||"object"!=typeof(e=$.extend(!0,{},t,e)))return alert("Invalid field object. Please try again."),!1;if(e.sectitle="yes"===e.section?e.sectitle:"",e.deflt=e.type.match(/^(text|textarea)$/)?e.deflt:"",e.deflt=e.type.match(/^(text)$/)?e.deflt.replace(/[\r\n\t ]+/g," "):e.deflt,e.options=e.type.match(/^(select|selects|checkboxes|radios)$/)?e.options:"",e.expected=e.type.match(/^(text|textarea)$/)?e.expected:"",e.id?!u(e.id)||o&&e.id===n[s].id||a.push("Unique Field ID:\nThat Field ID already exists. Please try again."):a.push("Unique Field ID:\nThis is required. Please try again."),e.label||a.push("Field Label/Description:\nThis is required. Please try again."),e.type.match(/^(select|selects|checkboxes|radios)$/)&&!e.options)a.push("Option Configuration File:\nThis is required. Please try again.");else if(e.type.match(/^(select|selects|checkboxes|radios)$/)){for(r=0;r<(l=e.options.split(/[\r\n]+/)).length;r++)if(!(l[r]=$.trim(l[r])).match(/^([^\|]*)(\|)([^\|]*)(\|default)?$/)){a.push("Option Configuration File:\nInvalid configuration at line #"+(r+1)+".");break}e.options=$.trim(l.join("\n"))}return(e.levels=e.levels.replace(/ /g,""))?e.levels.match(/^(all|[0-9,]+)$/)||a.push("Applicable Levels:\nShould be comma-delimited Levels, or just type: all.\n(examples: 0,1,2,3,4 or type the word: all)"):a.push("Applicable Levels:\nThis is required. Please try again."),e.classes&&e.classes.match(/[^a-z 0-9 _ \-]/i)&&a.push("CSS Classes:\nContains invalid characters. Please try again.\n(only: alphanumerics, underscores, hyphens, spaces)"),e.styles&&e.styles.match(/["\=\>\<]/)&&a.push('CSS Styles:\nContains invalid characters. Please try again.\n(do NOT use these characters: = " < >)'),e.attrs&&e.attrs.match(/[\>\<]/)&&a.push("Other Attributes:\nContains invalid characters. Please try again.\n(do NOT use these characters: < >)"),a.length>0?(alert(a.join("\n\n")),!1):e},m=function(){a.val(n.length>0?$.JSON.stringify(n):"")},p=function(e){var t={text:"Text (single line)",textarea:"Textarea (multi-line)",select:"Select Menu (drop-down)",selects:"Select Menu (multi-option)",checkbox:"Checkbox (single)",pre_checkbox:"Checkbox (pre-checked)",checkboxes:"Checkboxes (multi-option)",radios:"Radio Buttons (multi-option)"};return"string"==typeof t[e]?t[e]:""},u=function(e){for(var t=0;t<n.length;t++)if(n[t].id===e)return!0},d=function(){scrollTo(0,$("div.ws-plugin--s2member-custom-reg-fields-section").offset().top-100)},g=function(e,s){var l=0,o="",a="",i="number"==typeof s&&"object"==typeof n[s],c=!(!e&&!i),m=i?$.extend(!0,{},t,n[s]):t;if(o+='<a href="#" onclick="ws_plugin__s2member_customRegFieldAdd(); return false;">Add New Field</a>',tb_remove(),$("div#ws-plugin--s2member-custom-reg-field-configuration-thickbox-tools-form").remove(),c){for(a+='<div id="ws-plugin--s2member-custom-reg-field-configuration-thickbox-tools-form">',a+='<table id="ws-plugin--s2member-custom-reg-field-configuration-tools-form">',a+="<tbody>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">Starts A New Section?</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">',a+='<td colspan="2">',a+='<select property="section" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section" onchange="ws_plugin__s2member_customRegFieldSectionChange(this);">',a+='<option value="no"'+("no"===m.section?' selected="selected"':"")+'">No (this Field flows normally)</option>',a+='<option value="yes"'+("yes"===m.section?' selected="selected"':"")+'">Yes (this Field begins a new section)</option>',a+="</select><br />",a+="<small>Optional. Allows Fields to be grouped into sections.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle ws-plugin--s2member-custom-reg-field-configuration-tools-form-section"'+("yes"===m.section?"":' style="display:none;"')+'><td colspan="2"><hr /></td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle ws-plugin--s2member-custom-reg-field-configuration-tools-form-section"'+("yes"===m.section?"":' style="display:none;"')+">",a+='<td colspan="2">',a+="Title for this new section? (optional)<br />",a+='<input type="text" property="sectitle" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle" value="'+esc_attr(m.sectitle)+'" /><br />',a+="<small>If empty, a simple divider will be used by default.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-type"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">Form Field Type: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">',a+='<td colspan="2">',a+='<select property="type" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type" onchange="ws_plugin__s2member_customRegFieldTypeChange(this);">',a+='<option value="text"'+("text"===m.type?' selected="selected"':"")+'">'+esc_html(p("text"))+"</option>",a+='<option value="textarea"'+("textarea"===m.type?' selected="selected"':"")+'">'+esc_html(p("textarea"))+"</option>",a+='<option value="select"'+("select"===m.type?' selected="selected"':"")+'">'+esc_html(p("select"))+"</option>",a+='<option value="selects"'+("selects"===m.type?' selected="selected"':"")+'">'+esc_html(p("selects"))+"</option>",a+='<option value="checkbox"'+("checkbox"===m.type?' selected="selected"':"")+'">'+esc_html(p("checkbox"))+"</option>",a+='<option value="pre_checkbox"'+("pre_checkbox"===m.type?' selected="selected"':"")+'">'+esc_html(p("pre_checkbox"))+"</option>",a+='<option value="checkboxes"'+("checkboxes"===m.type?' selected="selected"':"")+'">'+esc_html(p("checkboxes"))+"</option>",a+='<option value="radios"'+("radios"===m.type?' selected="selected"':"")+'">'+esc_html(p("radios"))+"</option>",a+="</select><br />",a+="<small>The options below may change, based on the Field Type you choose here.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-label"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">Field Label/Desc: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">',a+='<td colspan="2">',a+='<input type="text" property="label" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label" value="'+esc_attr(m.label)+'" /><br />',a+="<small>Examples: <code>Choose Country</code>, <code>Street Address</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-id"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">Unique Field ID: *</label></label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">',a+='<td colspan="2">',a+='<input type="text" property="id" maxlength="25" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id" value="'+esc_attr(m.id)+'" /><br />',a+="<small>Examples: <code>country_code</code>, <code>street_address</code></small><br />",a+='<small>e.g., <code>[s2Get user_field="country_code" /]</code></small>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-required"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">Field Required: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">',a+='<td colspan="2">',a+='<select property="required" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">',a+='<option value="yes"'+("yes"===m.required?' selected="selected"':"")+'">Yes (required)</option>',a+='<option value="no"'+("no"===m.required?' selected="selected"':"")+'">No (optional)</option>',a+="</select><br />",a+='<small>If <code>yes</code>, only Users/Members will be "required" to enter this field.</small><br />',a+="<small>* Administrators are exempt from this requirement.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt">Default Text Value: (optional)</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<textarea property="deflt" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt" rows="1">'+esc_html(m.deflt)+"</textarea><br />",a+="<small>Default value before user input is received.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+(m.type.match(/^(select|selects|checkboxes|radios)$/)?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+(m.type.match(/^(select|selects|checkboxes|radios)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options">Option Configuration File: * (one option per line)</label><br />',a+="<small>Use a pipe <code>|</code> delimited format: <code>option value|option label</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+(m.type.match(/^(select|selects|checkboxes|radios)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<textarea property="options" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options" rows="3">'+esc_html(m.options)+"</textarea><br />",a+="Here is a quick example:<br />",a+="<small>You can also specify a <em>default</em> option:</small><br />",a+="<code>US|United States|default</code><br />",a+="<code>CA|Canada</code><br />",a+="<code>VI|Virgin Islands (U.S.)</code>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">Expected Format: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<select property="expected" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">',a+='<option value=""'+(""===m.expected?' selected="selected"':"")+'">Anything Goes</option>',a+='<option disabled="disabled"></option>',a+='<optgroup label="Specific Input Types">',a+='<option value="numeric-wp-commas"'+("numeric-wp-commas"===m.expected?' selected="selected"':"")+'">Numeric (with or without decimals, commas allowed)</option>',a+='<option value="numeric"'+("numeric"===m.expected?' selected="selected"':"")+'">Numeric (with or without decimals, no commas)</option>',a+='<option value="integer"'+("integer"===m.expected?' selected="selected"':"")+'">Integer (whole number, without any decimals)</option>',a+='<option value="integer-gt-0"'+("integer-gt-0"===m.expected?' selected="selected"':"")+'">Integer > 0 (whole number, no decimals, greater than 0)</option>',a+='<option value="float"'+("float"===m.expected?' selected="selected"':"")+'">Float (floating point number, decimals required)</option>',a+='<option value="float-gt-0"'+("float-gt-0"===m.expected?' selected="selected"':"")+'">Float > 0 (floating point number, decimals required, greater than 0)</option>',a+='<option value="date"'+("date"===m.expected?' selected="selected"':"")+'">Date (required date format: dd/mm/yyyy)</option>',a+='<option value="email"'+("email"===m.expected?' selected="selected"':"")+'">Email (require valid email)</option>',a+='<option value="url"'+("url"===m.expected?' selected="selected"':"")+'">Full URL (starting with http or https)</option>',a+='<option value="domain"'+("domain"===m.expected?' selected="selected"':"")+'">Domain Name (domain name only, without http)</option>',a+='<option value="phone"'+("phone"===m.expected?' selected="selected"':"")+'">Phone # (10 digits w/possible hyphens,spaces,brackets)</option>',a+='<option value="uszip"'+("uszip"===m.expected?' selected="selected"':"")+'">US Zipcode (5-9 digits w/possible hyphen)</option>',a+='<option value="cazip"'+("cazip"===m.expected?' selected="selected"':"")+'">Canadian Zipcode (6 alpha-numerics w/possible space)</option>',a+='<option value="uczip"'+("uczip"===m.expected?' selected="selected"':"")+'">US/Canadian Zipcode (either a US or Canadian zipcode)</option>',a+='<option value="ukpostcode"'+("ukpostcode"===m.expected?' selected="selected"':"")+'">UK Postcode (valid UK postcode format)</option>',a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Any Character Combination">',l=1;l<=25;l++)a+='<option value="any-'+l+'"'+(m.expected==="any-"+l?' selected="selected"':"")+'">Any Character Combination ('+l+" character minimum)</option>",a+='<option value="any-'+l+'-e"'+(m.expected==="any-"+l+"-e"?' selected="selected"':"")+'">Any Character Combination (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics, Spaces &amp; Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-punctuation-'+l+'"'+(m.expected==="alphanumerics-spaces-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-spaces-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics &amp; Spaces Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-'+l+'"'+(m.expected==="alphanumerics-spaces-"+l?' selected="selected"':"")+'">Alphanumerics &amp; Spaces ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-'+l+'-e"'+(m.expected==="alphanumerics-spaces-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics &amp; Spaces (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics &amp; Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-punctuation-'+l+'"'+(m.expected==="alphanumerics-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-'+l+'"'+(m.expected==="alphanumerics-"+l?' selected="selected"':"")+'">Alphanumerics ('+l+" character minimum)</option>",a+='<option value="alphanumerics-'+l+'-e"'+(m.expected==="alphanumerics-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphabetics Only">',l=1;l<=25;l++)a+='<option value="alphabetics-'+l+'"'+(m.expected==="alphabetics-"+l?' selected="selected"':"")+'">Alphabetics ('+l+" character minimum)</option>",a+='<option value="alphabetics-'+l+'-e"'+(m.expected==="alphabetics-"+l+"-e"?' selected="selected"':"")+'">Alphabetics (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Numeric Digits Only">',l=1;l<=25;l++)a+='<option value="numerics-'+l+'"'+(m.expected==="numerics-"+l?' selected="selected"':"")+'">Numeric Digits ('+l+" digit minimum)</option>",a+='<option value="numerics-'+l+'-e"'+(m.expected==="numerics-"+l+"-e"?' selected="selected"':"")+'">Numeric Digits (exactly '+l+" digit"+(l>1?"s":"")+")</option>";a+="</optgroup>",a+="</select><br />",a+="<small>Only Users/Members will be required to meet this criteria.</small><br />",a+="<small>* Administrators are exempt from this.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">Applicable Membership Levels: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">',a+='<td colspan="2">',a+='<input type="text" property="levels" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels" value="'+esc_attr(m.levels)+'" /><br />',a+="<small>Please use comma-delimited Level #'s: <code>0,1,2,3,4</code> or type: <code>all</code>.</small><br />",a+="<small>This allows you to enable this field - only at specific Membership Levels.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">Allow Profile Edits: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">',a+='<td colspan="2">',a+='<select property="editable" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">',a+='<option value="yes"'+("yes"===m.editable?' selected="selected"':"")+'">Yes (editable)</option>',a+='<option value="no"'+("no"===m.editable?' selected="selected"':"")+'">No (uneditable after registration)</option>',a+='<option value="no-invisible"'+("no-invisible"===m.editable?' selected="selected"':"")+'">No (uneditable &amp; totally invisible after registration)</option>',a+='<option value="no-always-invisible"'+("no-always-invisible"===m.editable?' selected="selected"':"")+'">No (uneditable &amp; totally invisible, both during &amp; after registration)</option>',a+='<option value="yes-invisible"'+("yes-invisible"===m.editable?' selected="selected"':"")+'">Yes (editable after registration / invisible during registration)</option>',a+="</select><br />",a+="<small>If <code>No</code>, this field will be un-editable after registration.</small><br />",a+="<small>* Administrators are exempt from this.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">CSS Classes: (optional)</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">',a+='<td colspan="2">',a+='<input type="text" property="classes" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes" value="'+esc_attr(m.classes)+'" /><br />',a+="<small>Example: <code>my-style-1 my-style-2</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">CSS Styles: (optional)</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">',a+='<td colspan="2">',a+='<input type="text" property="styles" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles" value="'+esc_attr(m.styles)+'" /><br />',a+="<small>Example: <code>color:#000000; background:#FFFFFF;</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">Other Attributes: (optional)</label>',
     1jQuery(document).ready(function($){var esc_attr=esc_html=function(e){return/[&\<\>"']/.test(e=String(e))&&(e=e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),e=e.replace(/"/g,"&quot;").replace(/'/g,"&#039;")),e};if(ws_plugin__s2member_generateSecurityKey=function(e){e||(e="#ws-plugin--s2member-sec-encryption-key");for(var t=function(e,t){return e=arguments.length<1?0:e,t=arguments.length<2?2147483647:t,Math.floor(Math.random()*(t-e+1))+e},s="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#%^&*()",l=0,r="";l<64;l++)r+=s.substr(t(0,s.length-1),1);return $(e).val(r),!1},location.href.match(/page\=ws-plugin--s2member/)&&$("input.ws-plugin--s2member-update-roles-button, input.ws-plugin--s2member-reset-roles-button").click(function(){var e=$(this);e.val("one moment please ...");var t='<?php echo (int)$GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; ?>',s=e.hasClass("ws-plugin--s2member-reset-roles-button")?"Reset":"Update";return $.post(ajaxurl,{action:"ws_plugin__s2member_update_roles_via_ajax",ws_plugin__s2member_update_roles_via_ajax:'<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (wp_create_nonce ("ws-plugin--s2member-update-roles-via-ajax")); ?>'},function(l){"1"===l?(alert("s2Member's Roles/Capabilities "+("reset"===s.toLowerCase()?"have been successfully reset":"updated successfully")+".\nYour installation of s2Member has Membership Levels 0-"+t+"."),e.val(s+" Roles/Capabilities")):"l"===l?(alert("Sorry, your request failed.\ns2Member's Roles/Capabilities are locked by Filter:\nws_plugin__s2member_lock_roles_caps"),e.val(s+" Roles/Capabilities")):(alert("Sorry, your request failed.\nAccess denied. Do you have the ability to `create_users`?"),e.val(s+" Roles/Capabilities"))}),!1}),location.href.match(/page\=ws-plugin--s2member-logs/)){$("input#ws-plugin--s2member-gateway-debug-logs-extensive-1").click(function(){var e=($(this),!!this.checked);e&&$("input#ws-plugin--s2member-gateway-debug-logs-1").attr("checked","checked")});var $toggles=$("a.ws-plugin--s2member-log-file-viewport-toggle");$toggles.click(function(){return $("textarea#ws-plugin--s2member-log-file-viewer").each(function(){var e=$(this);"expanded"!==e.attr("data-state")?(e.css({height:e.prop("scrollHeight")+50+"px","overflow-y":"auto"}),$toggles.html("&#8657; normalize viewport &#10073;"),e.attr("data-state","expanded")):(e.css({height:"auto","overflow-y":"scroll"}),$toggles.html("&#8659; expand viewport &#8659;"),e.attr("data-state","scrolling"))}),!1})}if(location.href.match(/page\=ws-plugin--s2member-mms-ops/)&&($("select#ws-plugin--s2member-mms-registration-file").change(function(){if("wp-signup"===$(this).val()){var e=$("select#ws-plugin--s2member-mms-registration-grants").val(),t=$("input#ws-plugin--s2member-mms-registration-blogs-level0").val();$("div#ws-plugin--s2member-mms-registration-support-package-details-wrapper").show(),$("div.ws-plugin--s2member-mms-registration-wp-login, table.ws-plugin--s2member-mms-registration-wp-login").hide(),$("div.ws-plugin--s2member-mms-registration-wp-signup, table.ws-plugin--s2member-mms-registration-wp-signup").show(),$("div.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0, table.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0")["all"===e?"show":"hide"](),$("input#ws-plugin--s2member-mms-registration-blogs-level0").val("all"===e?t>0?t:"1":"0")}else if("wp-login"===$(this).val()){var e=$("select#ws-plugin--s2member-mms-registration-grants").val(),t=$("input#ws-plugin--s2member-mms-registration-blogs-level0").val();$("div#ws-plugin--s2member-mms-registration-support-package-details-wrapper").hide(),$("div.ws-plugin--s2member-mms-registration-wp-login, table.ws-plugin--s2member-mms-registration-wp-login").show(),$("div.ws-plugin--s2member-mms-registration-wp-signup, table.ws-plugin--s2member-mms-registration-wp-signup").hide(),$("div.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0, table.ws-plugin--s2member-mms-registration-wp-signup-blogs-level0").hide(),$("input#ws-plugin--s2member-mms-registration-blogs-level0").val("0")}}).trigger("change"),$("select#ws-plugin--s2member-mms-registration-grants").change(function(){$("select#ws-plugin--s2member-mms-registration-file").trigger("change")})),location.href.match(/page\=ws-plugin--s2member-gen-ops/)&&(ws_plugin__s2member_enableSecurityKey=function(){return confirm("Edit Security Encryption Key?\n\nChanging this key will invalidate existing data that was encrypted with the current effective key, which can include access links, registration/tracking cookies, pending transaction-return data, and values encrypted through s2Member's API.\n\nContinue only if you intend to change or recover the key. Keep the current key backed up; s2Member also retains a history of recent Security Encryption Keys configured in s2Member.")&&$("input#ws-plugin--s2member-sec-encryption-key").removeAttr("disabled"),!1},ws_plugin__s2member_securityKeyHistory=function(){return $("div#ws-plugin--s2member-sec-encryption-key-history").toggle(),!1},$("select#ws-plugin--s2member-new-user-emails-enabled").change(function(){var e=$("input#ws-plugin--s2member-pluggables-wp-new-user-notification"),t=$(this),s=$("div#ws-plugin--s2member-new-user-emails");"0"===e.val()||"0"===t.val()?("0"===e.val()?t.attr("disabled","disabled"):t.removeAttr("disabled"),$(":input",s).attr("disabled","disabled"),s.css("opacity","0.5")):(t.removeAttr("disabled"),$(":input",s).removeAttr("disabled"),s.css("opacity",""))}).trigger("change"),$("select#ws-plugin--s2member-login-reg-design-enabled").change(function(){var e=$(this),t=$("div#ws-plugin--s2member-login-reg-design");"0"===e.val()?($(":input",t).attr("disabled","disabled"),t.css("opacity","0.5"),t.hide()):($(":input",t).removeAttr("disabled"),t.css("opacity",""),t.show())}).trigger("change"),$("select#ws-plugin--s2member-custom-reg-password").change(function(){var e=$(this),t=$("div#ws-plugin--s2member-new-user-email-details");"1"===e.val()?t.css("opacity","0.5"):t.css("opacity","")}).trigger("change"),$("input#ws-plugin--s2member-custom-reg-fields").length&&$("div#ws-plugin--s2member-custom-reg-field-configuration").length&&!function(){var e,t,s,l,r,o,a=$("input#ws-plugin--s2member-custom-reg-fields"),i=$("div#ws-plugin--s2member-custom-reg-field-configuration"),n=a.val()?JSON.parse(a.val()):[];for(n=n instanceof Array?n:[],t={section:"no",sectitle:"",id:"",label:"",type:"text",deflt:"",options:"",expected:"",required:"yes",levels:"all",editable:"yes",classes:"",styles:"",attrs:""},e=0;e<n.length;e++)n[e]=$.extend(!0,{},t,n[e]);s='<div id="ws-plugin--s2member-custom-reg-field-configuration-tools"></div>',l='<table id="ws-plugin--s2member-custom-reg-field-configuration-table"></table>',i.html(s+l),r=$("div#ws-plugin--s2member-custom-reg-field-configuration-tools"),o=$("table#ws-plugin--s2member-custom-reg-field-configuration-table"),ws_plugin__s2member_customRegFieldSectionChange=function(e){var t=$(e).val(),s="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle";"yes"===t?$(s).css("display",""):$(s).css("display","none")},ws_plugin__s2member_customRegFieldTypeChange=function(e){var t=$(e).val(),s="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt",l="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-options",r="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected";t.match(/^(text|textarea)$/)?$(s).css("display",""):$(s).css("display","none"),t.match(/^(select|selects|checkboxes|radios)$/)?$(l).css("display",""):$(l).css("display","none"),t.match(/^(text|textarea)$/)?$(r).css("display",""):$(r).css("display","none")},ws_plugin__s2member_customRegFieldDelete=function(e){for(var t=new Array,s=0;s<n.length;s++)s!==e&&t.push(n[s]);n=t,m(),b()},ws_plugin__s2member_customRegFieldMoveUp=function(e){if("object"==typeof n[e]&&"object"==typeof n[e-1]){var t=n[e-1],s=n[e];n[e-1]=s,n[e]=t,m(),b()}},ws_plugin__s2member_customRegFieldMoveDown=function(e){if("object"==typeof n[e]&&"object"==typeof n[e+1]){var t=n[e+1],s=n[e];n[e+1]=s,n[e]=t,m(),b()}},ws_plugin__s2member_customRegFieldCreate=function(){var e=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),t={};$(":input[property]",e).each(function(){var e=$(this),s=e.attr("property"),l=$.trim(e.val());t[s]=l}),(t=c(t))&&(n.push(t),m(),g(),b(),d(),setTimeout(function(){"tr.ws-plugin--s2member-custom-reg-field-configuration-table-row-"+(n.length-1);alert('Field created successfully.\n* Remember to "Save All Changes".')},500))},ws_plugin__s2member_customRegFieldUpdate=function(e){var t=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),s={};$(":input[property]",t).each(function(){var e=$(this),t=e.attr("property"),l=$.trim(e.val());s[t]=l}),(s=c(s,e))&&(n[e]=s,m(),g(),b(),d(),setTimeout(function(){alert('Field updated successfully.\n* Remember to "Save All Changes".')},500))},ws_plugin__s2member_customRegFieldAdd=function(){g(!0)},ws_plugin__s2member_customRegFieldEdit=function(e){g(!1,e),d()},ws_plugin__s2member_customRegFieldCancel=function(){g(),d()};var c=function(e,s){var l,r,o="number"==typeof s&&"object"==typeof n[s],a=[];if("object"!=typeof e||"object"!=typeof(e=$.extend(!0,{},t,e)))return alert("Invalid field object. Please try again."),!1;if(e.sectitle="yes"===e.section?e.sectitle:"",e.deflt=e.type.match(/^(text|textarea)$/)?e.deflt:"",e.deflt=e.type.match(/^(text)$/)?e.deflt.replace(/[\r\n\t ]+/g," "):e.deflt,e.options=e.type.match(/^(select|selects|checkboxes|radios)$/)?e.options:"",e.expected=e.type.match(/^(text|textarea)$/)?e.expected:"",e.id?!u(e.id)||o&&e.id===n[s].id||a.push("Unique Field ID:\nThat Field ID already exists. Please try again."):a.push("Unique Field ID:\nThis is required. Please try again."),e.label||a.push("Field Label/Description:\nThis is required. Please try again."),e.type.match(/^(select|selects|checkboxes|radios)$/)&&!e.options)a.push("Option Configuration File:\nThis is required. Please try again.");else if(e.type.match(/^(select|selects|checkboxes|radios)$/)){for(r=0;r<(l=e.options.split(/[\r\n]+/)).length;r++)if(!(l[r]=$.trim(l[r])).match(/^([^\|]*)(\|)([^\|]*)(\|default)?$/)){a.push("Option Configuration File:\nInvalid configuration at line #"+(r+1)+".");break}e.options=$.trim(l.join("\n"))}return(e.levels=e.levels.replace(/ /g,""))?e.levels.match(/^(all|[0-9,]+)$/)||a.push("Applicable Levels:\nShould be comma-delimited Levels, or just type: all.\n(examples: 0,1,2,3,4 or type the word: all)"):a.push("Applicable Levels:\nThis is required. Please try again."),e.classes&&e.classes.match(/[^a-z 0-9 _ \-]/i)&&a.push("CSS Classes:\nContains invalid characters. Please try again.\n(only: alphanumerics, underscores, hyphens, spaces)"),e.styles&&e.styles.match(/["\=\>\<]/)&&a.push('CSS Styles:\nContains invalid characters. Please try again.\n(do NOT use these characters: = " < >)'),e.attrs&&e.attrs.match(/[\>\<]/)&&a.push("Other Attributes:\nContains invalid characters. Please try again.\n(do NOT use these characters: < >)"),a.length>0?(alert(a.join("\n\n")),!1):e},m=function(){a.val(n.length>0?JSON.stringify(n):"")},p=function(e){var t={text:"Text (single line)",textarea:"Textarea (multi-line)",select:"Select Menu (drop-down)",selects:"Select Menu (multi-option)",checkbox:"Checkbox (single)",pre_checkbox:"Checkbox (pre-checked)",checkboxes:"Checkboxes (multi-option)",radios:"Radio Buttons (multi-option)"};return"string"==typeof t[e]?t[e]:""},u=function(e){for(var t=0;t<n.length;t++)if(n[t].id===e)return!0},d=function(){scrollTo(0,$("div.ws-plugin--s2member-custom-reg-fields-section").offset().top-100)},g=function(e,s){var l=0,o="",a="",i="number"==typeof s&&"object"==typeof n[s],c=!(!e&&!i),m=i?$.extend(!0,{},t,n[s]):t;if(o+='<a href="#" onclick="ws_plugin__s2member_customRegFieldAdd(); return false;">Add New Field</a>',tb_remove(),$("div#ws-plugin--s2member-custom-reg-field-configuration-thickbox-tools-form").remove(),c){for(a+='<div id="ws-plugin--s2member-custom-reg-field-configuration-thickbox-tools-form">',a+='<table id="ws-plugin--s2member-custom-reg-field-configuration-tools-form">',a+="<tbody>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">Starts A New Section?</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">',a+='<td colspan="2">',a+='<select property="section" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section" onchange="ws_plugin__s2member_customRegFieldSectionChange(this);">',a+='<option value="no"'+("no"===m.section?' selected="selected"':"")+'">No (this Field flows normally)</option>',a+='<option value="yes"'+("yes"===m.section?' selected="selected"':"")+'">Yes (this Field begins a new section)</option>',a+="</select><br />",a+="<small>Optional. Allows Fields to be grouped into sections.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle ws-plugin--s2member-custom-reg-field-configuration-tools-form-section"'+("yes"===m.section?"":' style="display:none;"')+'><td colspan="2"><hr /></td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle ws-plugin--s2member-custom-reg-field-configuration-tools-form-section"'+("yes"===m.section?"":' style="display:none;"')+">",a+='<td colspan="2">',a+="Title for this new section? (optional)<br />",a+='<input type="text" property="sectitle" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle" value="'+esc_attr(m.sectitle)+'" /><br />',a+="<small>If empty, a simple divider will be used by default.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-type"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">Form Field Type: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">',a+='<td colspan="2">',a+='<select property="type" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type" onchange="ws_plugin__s2member_customRegFieldTypeChange(this);">',a+='<option value="text"'+("text"===m.type?' selected="selected"':"")+'">'+esc_html(p("text"))+"</option>",a+='<option value="textarea"'+("textarea"===m.type?' selected="selected"':"")+'">'+esc_html(p("textarea"))+"</option>",a+='<option value="select"'+("select"===m.type?' selected="selected"':"")+'">'+esc_html(p("select"))+"</option>",a+='<option value="selects"'+("selects"===m.type?' selected="selected"':"")+'">'+esc_html(p("selects"))+"</option>",a+='<option value="checkbox"'+("checkbox"===m.type?' selected="selected"':"")+'">'+esc_html(p("checkbox"))+"</option>",a+='<option value="pre_checkbox"'+("pre_checkbox"===m.type?' selected="selected"':"")+'">'+esc_html(p("pre_checkbox"))+"</option>",a+='<option value="checkboxes"'+("checkboxes"===m.type?' selected="selected"':"")+'">'+esc_html(p("checkboxes"))+"</option>",a+='<option value="radios"'+("radios"===m.type?' selected="selected"':"")+'">'+esc_html(p("radios"))+"</option>",a+="</select><br />",a+="<small>The options below may change, based on the Field Type you choose here.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-label"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">Field Label/Desc: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">',a+='<td colspan="2">',a+='<input type="text" property="label" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label" value="'+esc_attr(m.label)+'" /><br />',a+="<small>Examples: <code>Choose Country</code>, <code>Street Address</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-id"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">Unique Field ID: *</label></label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">',a+='<td colspan="2">',a+='<input type="text" property="id" maxlength="25" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id" value="'+esc_attr(m.id)+'" /><br />',a+="<small>Examples: <code>country_code</code>, <code>street_address</code></small><br />",a+='<small>e.g., <code>[s2Get user_field="country_code" /]</code></small>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-required"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">Field Required: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">',a+='<td colspan="2">',a+='<select property="required" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">',a+='<option value="yes"'+("yes"===m.required?' selected="selected"':"")+'">Yes (required)</option>',a+='<option value="no"'+("no"===m.required?' selected="selected"':"")+'">No (optional)</option>',a+="</select><br />",a+='<small>If <code>yes</code>, only Users/Members will be "required" to enter this field.</small><br />',a+="<small>* Administrators are exempt from this requirement.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt">Default Text Value: (optional)</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<textarea property="deflt" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt" rows="1">'+esc_html(m.deflt)+"</textarea><br />",a+="<small>Default value before user input is received.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+(m.type.match(/^(select|selects|checkboxes|radios)$/)?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+(m.type.match(/^(select|selects|checkboxes|radios)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options">Option Configuration File: * (one option per line)</label><br />',a+="<small>Use a pipe <code>|</code> delimited format: <code>option value|option label</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+(m.type.match(/^(select|selects|checkboxes|radios)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<textarea property="options" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options" rows="3">'+esc_html(m.options)+"</textarea><br />",a+="Here is a quick example:<br />",a+="<small>You can also specify a <em>default</em> option:</small><br />",a+="<code>US|United States|default</code><br />",a+="<code>CA|Canada</code><br />",a+="<code>VI|Virgin Islands (U.S.)</code>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">Expected Format: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+(m.type.match(/^(text|textarea)$/)?"":' style="display:none;"')+">",a+='<td colspan="2">',a+='<select property="expected" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">',a+='<option value=""'+(""===m.expected?' selected="selected"':"")+'">Anything Goes</option>',a+='<option disabled="disabled"></option>',a+='<optgroup label="Specific Input Types">',a+='<option value="numeric-wp-commas"'+("numeric-wp-commas"===m.expected?' selected="selected"':"")+'">Numeric (with or without decimals, commas allowed)</option>',a+='<option value="numeric"'+("numeric"===m.expected?' selected="selected"':"")+'">Numeric (with or without decimals, no commas)</option>',a+='<option value="integer"'+("integer"===m.expected?' selected="selected"':"")+'">Integer (whole number, without any decimals)</option>',a+='<option value="integer-gt-0"'+("integer-gt-0"===m.expected?' selected="selected"':"")+'">Integer > 0 (whole number, no decimals, greater than 0)</option>',a+='<option value="float"'+("float"===m.expected?' selected="selected"':"")+'">Float (floating point number, decimals required)</option>',a+='<option value="float-gt-0"'+("float-gt-0"===m.expected?' selected="selected"':"")+'">Float > 0 (floating point number, decimals required, greater than 0)</option>',a+='<option value="date"'+("date"===m.expected?' selected="selected"':"")+'">Date (required date format: dd/mm/yyyy)</option>',a+='<option value="email"'+("email"===m.expected?' selected="selected"':"")+'">Email (require valid email)</option>',a+='<option value="url"'+("url"===m.expected?' selected="selected"':"")+'">Full URL (starting with http or https)</option>',a+='<option value="domain"'+("domain"===m.expected?' selected="selected"':"")+'">Domain Name (domain name only, without http)</option>',a+='<option value="phone"'+("phone"===m.expected?' selected="selected"':"")+'">Phone # (10 digits w/possible hyphens,spaces,brackets)</option>',a+='<option value="uszip"'+("uszip"===m.expected?' selected="selected"':"")+'">US Zipcode (5-9 digits w/possible hyphen)</option>',a+='<option value="cazip"'+("cazip"===m.expected?' selected="selected"':"")+'">Canadian Zipcode (6 alpha-numerics w/possible space)</option>',a+='<option value="uczip"'+("uczip"===m.expected?' selected="selected"':"")+'">US/Canadian Zipcode (either a US or Canadian zipcode)</option>',a+='<option value="ukpostcode"'+("ukpostcode"===m.expected?' selected="selected"':"")+'">UK Postcode (valid UK postcode format)</option>',a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Any Character Combination">',l=1;l<=25;l++)a+='<option value="any-'+l+'"'+(m.expected==="any-"+l?' selected="selected"':"")+'">Any Character Combination ('+l+" character minimum)</option>",a+='<option value="any-'+l+'-e"'+(m.expected==="any-"+l+"-e"?' selected="selected"':"")+'">Any Character Combination (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics, Spaces &amp; Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-punctuation-'+l+'"'+(m.expected==="alphanumerics-spaces-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-spaces-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics &amp; Spaces Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-'+l+'"'+(m.expected==="alphanumerics-spaces-"+l?' selected="selected"':"")+'">Alphanumerics &amp; Spaces ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-'+l+'-e"'+(m.expected==="alphanumerics-spaces-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics &amp; Spaces (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics &amp; Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-punctuation-'+l+'"'+(m.expected==="alphanumerics-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-'+l+'"'+(m.expected==="alphanumerics-"+l?' selected="selected"':"")+'">Alphanumerics ('+l+" character minimum)</option>",a+='<option value="alphanumerics-'+l+'-e"'+(m.expected==="alphanumerics-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphabetics Only">',l=1;l<=25;l++)a+='<option value="alphabetics-'+l+'"'+(m.expected==="alphabetics-"+l?' selected="selected"':"")+'">Alphabetics ('+l+" character minimum)</option>",a+='<option value="alphabetics-'+l+'-e"'+(m.expected==="alphabetics-"+l+"-e"?' selected="selected"':"")+'">Alphabetics (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Numeric Digits Only">',l=1;l<=25;l++)a+='<option value="numerics-'+l+'"'+(m.expected==="numerics-"+l?' selected="selected"':"")+'">Numeric Digits ('+l+" digit minimum)</option>",a+='<option value="numerics-'+l+'-e"'+(m.expected==="numerics-"+l+"-e"?' selected="selected"':"")+'">Numeric Digits (exactly '+l+" digit"+(l>1?"s":"")+")</option>";a+="</optgroup>",a+="</select><br />",a+="<small>Only Users/Members will be required to meet this criteria.</small><br />",a+="<small>* Administrators are exempt from this.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">Applicable Membership Levels: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">',a+='<td colspan="2">',a+='<input type="text" property="levels" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels" value="'+esc_attr(m.levels)+'" /><br />',a+="<small>Please use comma-delimited Level #'s: <code>0,1,2,3,4</code> or type: <code>all</code>.</small><br />",a+="<small>This allows you to enable this field - only at specific Membership Levels.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">Allow Profile Edits: *</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">',a+='<td colspan="2">',a+='<select property="editable" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">',a+='<option value="yes"'+("yes"===m.editable?' selected="selected"':"")+'">Yes (editable)</option>',a+='<option value="no"'+("no"===m.editable?' selected="selected"':"")+'">No (uneditable after registration)</option>',a+='<option value="no-invisible"'+("no-invisible"===m.editable?' selected="selected"':"")+'">No (uneditable &amp; totally invisible after registration)</option>',a+='<option value="no-always-invisible"'+("no-always-invisible"===m.editable?' selected="selected"':"")+'">No (uneditable &amp; totally invisible, both during &amp; after registration)</option>',a+='<option value="yes-invisible"'+("yes-invisible"===m.editable?' selected="selected"':"")+'">Yes (editable after registration / invisible during registration)</option>',a+="</select><br />",a+="<small>If <code>No</code>, this field will be un-editable after registration.</small><br />",a+="<small>* Administrators are exempt from this.</small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">CSS Classes: (optional)</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">',a+='<td colspan="2">',a+='<input type="text" property="classes" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes" value="'+esc_attr(m.classes)+'" /><br />',a+="<small>Example: <code>my-style-1 my-style-2</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">CSS Styles: (optional)</label>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">',a+='<td colspan="2">',a+='<input type="text" property="styles" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles" value="'+esc_attr(m.styles)+'" /><br />',a+="<small>Example: <code>color:#000000; background:#FFFFFF;</code></small>",a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">',a+='<td colspan="2">',a+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">Other Attributes: (optional)</label>',
    22a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">',a+='<td colspan="2">',a+='<input type="text" property="attrs" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs" value="'+esc_attr(m.attrs)+'" /><br />',a+='<small>Example: <code>onkeyup="" onblur=""</code></small>',a+="</td>",a+="</tr>",a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-buttons"><td colspan="2">&nbsp;</td></tr>',a+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-buttons">',a+='<td align="left">',a+='<input type="button" value="Cancel" onclick="ws_plugin__s2member_customRegFieldCancel();" />',a+="</td>",a+='<td align="right">',a+='<input type="button" value="'+(i?"Update This Field":"Create Registration Field")+'" onclick="'+(i?"ws_plugin__s2member_customRegFieldUpdate("+s+");":"ws_plugin__s2member_customRegFieldCreate();")+'" />',a+="</td>",a+="</tr>",a+="</tbody>",a+="</table>",a+="<div>",$("body").append(a),tb_show(i?"Editing Registration/Profile Field":"New Custom Registration/Profile Field","#TB_inline?inlineId=ws-plugin--s2member-custom-reg-field-configuration-thickbox-tools-form"),$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form").show()}r.html(o)},b=function(){var e=(n.length,0),t="",s="o";if(t+="<tbody>",t+="<tr>",t+="<th>Order</th>",t+="<th>Field Type</th>",t+="<th>Unique ID</th>",t+="<th>Required</th>",t+="<th>Levels</th>",t+="<th>- Tools -</th>",t+="</tr>",n.length>0)for(e=0;e<n.length;e++)t+='<tr class="'+esc_attr(s="o"===s?"e":"o")+("yes"===n[e].section?" s":"")+" ws-plugin--s2member-custom-reg-field-configuration-table-row-"+e+'">',t+='<td nowrap="nowrap"><a class="ws-plugin--s2member-custom-reg-field-configuration-move-up" title="Move field up" href="#" onclick="ws_plugin__s2member_customRegFieldMoveUp('+e+'); return false;"></a><a class="ws-plugin--s2member-custom-reg-field-configuration-move-down" title="Move field down" href="#" onclick="ws_plugin__s2member_customRegFieldMoveDown('+e+'); return false;"></a></td>',t+='<td nowrap="nowrap">'+esc_html(p(n[e].type))+"</td>",t+='<td nowrap="nowrap">'+esc_html(n[e].id)+"</td>",t+='<td nowrap="nowrap">'+esc_html(n[e].required)+"</td>",t+='<td nowrap="nowrap">'+esc_html(n[e].levels)+"</td>",t+='<td nowrap="nowrap"><a class="ws-plugin--s2member-custom-reg-field-configuration-edit" title="Edit field" href="#" onclick="ws_plugin__s2member_customRegFieldEdit('+e+'); return false;"></a><a class="ws-plugin--s2member-custom-reg-field-configuration-delete" title="Delete field" href="#" onclick="ws_plugin__s2member_customRegFieldDelete('+e+'); return false;"></a></td>',t+="</tr>";else t+="<tr>",t+='<td colspan="6">No Custom Fields are configured.</td>',t+="</tr>";t+="</tbody>",o.html(t)};g(),b()}()),location.href.match(/page\=ws-plugin--s2member-res-ops/)&&($("input#ws-plugin--s2member-brute-force-restrictions-reset-button").click(function(){var e=$(this);return e.val("one moment please ..."),$.post(ajaxurl,{action:"ws_plugin__s2member_delete_reset_all_ip_restrictions_via_ajax",ws_plugin__s2member_delete_reset_all_ip_restrictions_via_ajax:'<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (wp_create_nonce ("ws-plugin--s2member-delete-reset-all-ip-restrictions-via-ajax")); ?>'},function(t){alert("s2Member's Brute Force Restriction Logs have all been reset."),e.val("Reset Brute Force Logs")}),!1}),$("input#ws-plugin--s2member-ip-restrictions-reset-button").click(function(){var e=$(this);return e.val("one moment please ..."),$.post(ajaxurl,{action:"ws_plugin__s2member_delete_reset_all_ip_restrictions_via_ajax",ws_plugin__s2member_delete_reset_all_ip_restrictions_via_ajax:'<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (wp_create_nonce ("ws-plugin--s2member-delete-reset-all-ip-restrictions-via-ajax")); ?>'},function(t){alert("s2Member's IP Restriction Logs have all been reset."),e.val("Reset IP Restriction Logs")}),!1}),$('div.ws-plugin--s2member-query-level-access-section input[type="checkbox"][name="ws_plugin__s2member_filter_wp_query[]"]').change(function(){var e=$(this).val();$('div.ws-plugin--s2member-query-level-access-section input[type="checkbox"][name="ws_plugin__s2member_filter_wp_query[]"]').each(function(){var t=$(this),s=t.val(),l='input[type="checkbox"]';"all"===s&&this.checked?t.nextAll(l).attr({checked:"checked",disabled:"disabled"}):"all"!==s||this.checked||(t.nextAll(l).removeAttr("disabled"),"all"===e?t.nextAll(l).removeAttr("checked"):null)})}).last().trigger("change")),location.href.match(/page\=ws-plugin--s2member-down-ops/)){var updateCloudFrontPrivateKey=function(){var e=$("input#ws-plugin--s2member-amazon-cf-files-private-key"),t=$("textarea#ws-plugin--s2member-amazon-cf-files-private-key-entry"),s=$.trim(e.val()),l=$.trim(t.val());(s&&!l||l.match(/[^\r\n\u25CF]/))&&(e.val(l),t.val(l.replace(/[^\r\n]/g,String.fromCharCode(9679))))};$("form#ws-plugin--s2member-options-form").submit(updateCloudFrontPrivateKey),$("textarea#ws-plugin--s2member-amazon-cf-files-private-key-entry").change(updateCloudFrontPrivateKey).trigger("change");var updateCloudFrontDistroCfgs=function(){var e=$("input#ws-plugin--s2member-amazon-cf-files-private-key"),t=$("input#ws-plugin--s2member-amazon-cf-files-private-key-id"),s=$("input#ws-plugin--s2member-amazon-cf-files-auto-configure-distros"),l=$("input#ws-plugin--s2member-amazon-cf-files-distros-auto-config-status"),r=$.trim(l.val()),o=$.trim(e.val()),a=$.trim(t.val()),i=$.trim(e.attr("data-s-prev-config-value")),n=$.trim(t.attr("data-s-prev-config-value"));"configured"===r&&(n&&a!==n||i&&o!==i)?(alert("s2Member will need to delete and re-configure your Amazon CloudFront distributions if you change this. When you're done editing, click (Save All Changes) below."),s.attr("checked","checked")):"configured"!==r&&a&&o&&(alert("s2Member will need to auto-configure your Amazon CloudFront distributions for you. When you're done editing, click (Save All Changes) below."),s.attr("checked","checked"))};$("input#ws-plugin--s2member-amazon-cf-files-private-key-id").change(updateCloudFrontDistroCfgs),$("textarea#ws-plugin--s2member-amazon-cf-files-private-key-entry").change(updateCloudFrontDistroCfgs),$("input#ws-plugin--s2member-amazon-cf-files-auto-configure-distros-w-cnames").change(function(){var e=($(this),!!this.checked),t=$("input#ws-plugin--s2member-amazon-cf-files-auto-configure-distros"),s=$("div#ws-plugin--s2member-amazon-cf-files-auto-configure-distro-cnames");e?s.show():s.hide(),e?t.attr("checked","checked"):null}).trigger("change")}$("select#ws-plugin--s2member-membership-eot-behavior").change(function(){var e="delete"===$(this).val(),t=$("tr.ws-plugin--s2member-eot-demotion-option-row"),s=$(":input",t),i=$("#ws-plugin--s2member-eots-remove-ccaps"),o=$('label[for="ws-plugin--s2member-eots-remove-ccaps"]').closest("tr").add(i.closest("tr"));s.add(i).prop("disabled",e),t.add(o).css("opacity",e?"0.5":"")}).trigger("change"),$("select#ws-plugin--s2member-auto-eot-system-runtime-mode").change(function(){var e="custom"!==$(this).val(),t=$("#ws-plugin--s2member-auto-eot-system-runtime-custom"),s=$('label[for="ws-plugin--s2member-auto-eot-system-runtime-custom"]').closest("tr").add(t.closest("tr"));t.prop("disabled",e),s.css("opacity",e?"0.5":"")}).trigger("change"),location.href.match(/page\=ws-plugin--s2member-paypal-ops/)&&$("select#ws-plugin--s2member-auto-eot-system-enabled").change(function(){var e=$(this),t=e.val(),s=$("p#ws-plugin--s2member-auto-eot-system-enabled-via-cron");2==t?s.show():s.hide()}),location.href.match(/page\=ws-plugin--s2member-paypal-buttons/)&&($("div.ws-menu-page select[id]").filter(function(){return this.id.match(/^ws-plugin--s2member-(level[1-9][0-9]*|modification)-term$/)}).change(function(){var e=this.id.replace(/^ws-plugin--s2member-(.+?)-term$/g,"$1"),t="BN"===$(this).val().split("-")[2].replace(/[^0-1BN]/g,"")?1:0;$("p#ws-plugin--s2member-"+e+"-trial-line").css("display",t?"none":""),$("span#ws-plugin--s2member-"+e+"-trial-then").css("display",t?"none":""),$("span#ws-plugin--s2member-"+e+"-20p-rule").css("display",t?"none":""),t?$("input#ws-plugin--s2member-"+e+"-trial-period").val(0):null,t?$("input#ws-plugin--s2member-"+e+"-trial-amount").val("0.00"):null}),$("div.ws-menu-page input[id]").filter(function(){return this.id.match(/^ws-plugin--s2member-(level[1-9][0-9]*|modification|ccap)-ccaps$/)}).keyup(function(){var e=this.value.replace(/^(-all|-al|-a|-)[;,]*/gi,""),t=this.value.match(/^(-all|-al|-a|-)[;,]*/i)?"-all,":"";e.match(/[^a-z_0-9,]/)&&(this.value=t+$.trim($.trim(e).replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase()))}),ws_plugin__s2member_paypalButtonGenerate=function(button){var shortCodeTemplate='[s2Member-PayPal-Button %%attrs%% image="default" output="button" /]',shortCodeTemplateAttrs="",labels={};eval("<?php echo c_ws_plugin__s2member_utils_strings::esc_dq($labels); ?>");var shortCode=$("input#ws-plugin--s2member-"+button+"-shortcode"),code=$("textarea#ws-plugin--s2member-"+button+"-button"),modLevel=$("select#ws-plugin--s2member-modification-level"),level="modification"===button?modLevel.val().split(":",2)[1]:button.replace(/^level/,""),label=labels["level"+level].replace(/"/g,""),desc=$.trim($("input#ws-plugin--s2member-"+button+"-desc").val().replace(/"/g,"")),trialAmount=$("input#ws-plugin--s2member-"+button+"-trial-amount").val().replace(/[^0-9\.]/g,""),trialPeriod=$("input#ws-plugin--s2member-"+button+"-trial-period").val().replace(/[^0-9]/g,""),trialTerm=$("select#ws-plugin--s2member-"+button+"-trial-term").val().replace(/[^A-Z]/g,""),regAmount=$("input#ws-plugin--s2member-"+button+"-amount").val().replace(/[^0-9\.]/g,""),regPeriod=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[0].replace(/[^0-9]/g,""),regTerm=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[1].replace(/[^A-Z]/g,""),regRecur=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[2].replace(/[^0-1BN]/g,""),regRecurTimes="",regRecurRetry="1",localeCode="",digital="0",noShipping="1",pageStyle=$.trim($("input#ws-plugin--s2member-"+button+"-page-style").val().replace(/"/g,"")),currencyCode=$("select#ws-plugin--s2member-"+button+"-currency").val().replace(/[^A-Z]/g,""),cCaps=$.trim($.trim($("input#ws-plugin--s2member-"+button+"-ccaps").val()).replace(/^(-all|-al|-a|-)[;,]*/gi,"").replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase());cCaps=$.trim($("input#ws-plugin--s2member-"+button+"-ccaps").val()).match(/^(-all|-al|-a|-)[;,]*/i)?(cCaps?"-all,":"-all")+cCaps.toLowerCase():cCaps.toLowerCase(),trialPeriod="BN"===regRecur?"0":trialPeriod,trialAmount=!trialAmount||isNaN(trialAmount)||trialAmount<.01||trialPeriod<=0?"0":trialAmount;var levelCcapsPer="BN"===regRecur&&"L"!==regTerm?level+":"+cCaps+":"+regPeriod+" "+regTerm:level+":"+cCaps;return levelCcapsPer=levelCcapsPer.replace(/\:+$/g,""),"0"!==trialAmount&&(isNaN(trialAmount)||trialAmount<0)?(alert("— Oops, a slight problem: —\n\nWhen provided, Trial Amount must be >= 0.00"),!1):"0"!==trialAmount&&trialAmount>1e4&&"USD"===currencyCode.toUpperCase()?(alert("— Oops, a slight problem: —\n\nMaximum Trial Amount is: 10000.00"),!1):"D"===trialTerm&&trialPeriod>90?(alert("— Oops, a slight problem: —\n\nMaximum Trial Days is: 90.\nIf you want to offer more than 90 days, please choose Weeks or Months from the drop-down."),!1):"W"===trialTerm&&trialPeriod>52?(alert("— Oops, a slight problem: —\n\nMaximum Trial Weeks is: 52.\nIf you want to offer more than 52 weeks, please choose Months from the drop-down."),!1):"M"===trialTerm&&trialPeriod>24?(alert("— Oops, a slight problem: —\n\nMaximum Trial Months is: 24.\nIf you want to offer more than 24 months, please choose Years from the drop-down."),!1):"Y"===trialTerm&&trialPeriod>5?(alert("— Oops, a slight problem: —\n\nMax Trial Period Years is: 5."),!1):!regAmount||isNaN(regAmount)||regAmount<.01?(alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01"),!1):regAmount>1e4&&"USD"===currencyCode.toUpperCase()?(alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00"),!1):desc?(code.html(code.val().replace(/ \<\!--(\<input type\="hidden" name\="(amount|src|srt|sra|a1|p1|t1|a3|p3|t3)" value\="(.*?)" \/\>)--\>/g," $1")),parseInt(trialPeriod)<=0?code.html(code.val().replace(/ (\<input type\="hidden" name\="(a1|p1|t1)" value\="(.*?)" \/\>)/g," <!--$1-->")):null,"BN"===regRecur?code.html(code.val().replace(/ (\<input type\="hidden" name\="cmd" value\=")(.*?)(" \/\>)/g," $1_xclick$3")):null,"BN"===regRecur?code.html(code.val().replace(/ (\<input type\="hidden" name\="(src|srt|sra|a1|p1|t1|a3|p3|t3)" value\="(.*?)" \/\>)/g," <!--$1-->")):null,"BN"!==regRecur?code.html(code.val().replace(/ (\<input type\="hidden" name\="cmd" value\=")(.*?)(" \/\>)/g," $1_xclick-subscriptions$3")):null,"BN"!==regRecur?code.html(code.val().replace(/ (\<input type\="hidden" name\="amount" value\="(.*?)" \/\>)/g," <!--$1-->")):null,shortCodeTemplateAttrs+="modification"===button?'modify="1" ':"",shortCodeTemplateAttrs+='level="'+esc_attr(level)+'" ccaps="'+esc_attr(cCaps)+'" desc="'+esc_attr(desc)+'" ps="'+esc_attr(pageStyle)+'" lc="'+esc_attr(localeCode)+'" cc="'+esc_attr(currencyCode)+'" dg="'+esc_attr(digital)+'" ns="'+esc_attr(noShipping)+'" custom="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>"',shortCodeTemplateAttrs+=' ta="'+esc_attr(trialAmount)+'" tp="'+esc_attr(trialPeriod)+'" tt="'+esc_attr(trialTerm)+'" ra="'+esc_attr(regAmount)+'" rp="'+esc_attr(regPeriod)+'" rt="'+esc_attr(regTerm)+'" rr="'+esc_attr(regRecur)+'" rrt="'+esc_attr(regRecurTimes)+'" rra="'+esc_attr(regRecurRetry)+'"',shortCode.val(shortCodeTemplate.replace(/%%attrs%%/,shortCodeTemplateAttrs)),code.html(code.val().replace(/ name\="lc" value\="(.*?)"/,' name="lc" value="'+esc_attr(localeCode)+'"')),code.html(code.val().replace(/ name\="no_shipping" value\="(.*?)"/,' name="no_shipping" value="'+esc_attr(noShipping)+'"')),code.html(code.val().replace(/ name\="item_name" value\="(.*?)"/,' name="item_name" value="'+esc_attr(desc)+'"')),code.html(code.val().replace(/ name\="item_number" value\="(.*?)"/,' name="item_number" value="'+esc_attr(levelCcapsPer)+'"')),code.html(code.val().replace(/ name\="page_style" value\="(.*?)"/,' name="page_style" value="'+esc_attr(pageStyle)+'"')),code.html(code.val().replace(/ name\="currency_code" value\="(.*?)"/,' name="currency_code" value="'+esc_attr(currencyCode)+'"')),code.html(code.val().replace(/ name\="custom" value\="(.*?)"/,' name="custom" value="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>"')),code.html(code.val().replace(/ name\="modify" value\="(.*?)"/,' name="modify" value="'+("modification"===button?"1":"0")+'"')),code.html(code.val().replace(/ name\="amount" value\="(.*?)"/,' name="amount" value="'+esc_attr(regAmount)+'"')),code.html(code.val().replace(/ name\="src" value\="(.*?)"/,' name="src" value="'+esc_attr(regRecur)+'"')),code.html(code.val().replace(/ name\="srt" value\="(.*?)"/,' name="srt" value="'+esc_attr(regRecurTimes)+'"')),code.html(code.val().replace(/ name\="sra" value\="(.*?)"/,' name="sra" value="'+esc_attr(regRecurRetry)+'"')),code.html(code.val().replace(/ name\="a1" value\="(.*?)"/,' name="a1" value="'+esc_attr(trialAmount)+'"')),code.html(code.val().replace(/ name\="p1" value\="(.*?)"/,' name="p1" value="'+esc_attr(trialPeriod)+'"')),code.html(code.val().replace(/ name\="t1" value\="(.*?)"/,' name="t1" value="'+esc_attr(trialTerm)+'"')),code.html(code.val().replace(/ name\="a3" value\="(.*?)"/,' name="a3" value="'+esc_attr(regAmount)+'"')),code.html(code.val().replace(/ name\="p3" value\="(.*?)"/,' name="p3" value="'+esc_attr(regPeriod)+'"')),code.html(code.val().replace(/ name\="t3" value\="(.*?)"/,' name="t3" value="'+esc_attr(regTerm)+'"')),$("div#ws-plugin--s2member-"+button+"-button-prev").html(code.val().replace(/\<form/,'<form target="_blank"').replace(/\<\?php echo S2MEMBER_VALUE_FOR_PP_INV\(\); \?\>/g,Math.round((new Date).getTime())+"~<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr (c_ws_plugin__s2member_utils_ip::current())); ?>").replace(/\<\?php echo S2MEMBER_CURRENT_USER_VALUE_FOR_PP_(ON0|OS0|ON1|OS1); \?\>/g,"")),"modification"===button?alert("Your Modification Button has been generated.\nPlease copy/paste the Shortcode into your Login Welcome Page, or wherever you feel it would be most appropriate.\n\n* Remember, Modification Buttons should be displayed to existing Users/Members, and they should be logged-in, BEFORE clicking this Button."):alert("Your Button has been generated.\nPlease copy/paste the Shortcode Format into your Membership Options Page."),shortCode.each(function(){this.focus(),this.select()}),!1):(alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button."),!1)},ws_plugin__s2member_paypalCcapButtonGenerate=function(){var e='[s2Member-PayPal-Button %%attrs%% image="default" output="button" /]',t="",s=$("input#ws-plugin--s2member-ccap-shortcode"),l=$("textarea#ws-plugin--s2member-ccap-button"),r=$.trim($("input#ws-plugin--s2member-ccap-desc").val().replace(/"/g,"")),o=$("input#ws-plugin--s2member-ccap-amount").val().replace(/[^0-9\.]/g,""),a=$("select#ws-plugin--s2member-ccap-term").val().split("-")[0].replace(/[^0-9]/g,""),i=$("select#ws-plugin--s2member-ccap-term").val().split("-")[1].replace(/[^A-Z]/g,""),n=$("select#ws-plugin--s2member-ccap-term").val().split("-")[2].replace(/[^0-1BN]/g,""),c="",m="0",p="1",u=$.trim($("input#ws-plugin--s2member-ccap-page-style").val().replace(/"/g,"")),d=$("select#ws-plugin--s2member-ccap-currency").val().replace(/[^A-Z]/g,""),g=$.trim($.trim($("input#ws-plugin--s2member-ccap-ccaps").val()).replace(/^(-all|-al|-a|-)[;,]*/gi,"").replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase());g=$.trim($("input#ws-plugin--s2member-ccap-ccaps").val()).match(/^(-all|-al|-a|-)[;,]*/i)?(g?"-all,":"-all")+g.toLowerCase():g.toLowerCase();var b="BN"===n&&"L"!==i?"*:"+g+":"+a+" "+i:"*:"+g;return b=b.replace(/\:+$/g,""),g&&"-all"!==g?!o||isNaN(o)||o<.01?(alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01"),!1):o>1e4&&"USD"===d.toUpperCase()?(alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00"),!1):r?(t+='level="*" ccaps="'+esc_attr(g)+'" desc="'+esc_attr(r)+'" ps="'+esc_attr(u)+'" lc="'+esc_attr(c)+'" cc="'+esc_attr(d)+'" dg="'+esc_attr(m)+'" ns="'+esc_attr(p)+'"',t+=' custom="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>" ra="'+esc_attr(o)+'" rp="'+esc_attr(a)+'" rt="'+esc_attr(i)+'" rr="'+esc_attr(n)+'"',s.val(e.replace(/%%attrs%%/,t)),l.html(l.val().replace(/ name\="lc" value\="(.*?)"/,' name="lc" value="'+esc_attr(c)+'"')),l.html(l.val().replace(/ name\="no_shipping" value\="(.*?)"/,' name="no_shipping" value="'+esc_attr(p)+'"')),l.html(l.val().replace(/ name\="item_name" value\="(.*?)"/,' name="item_name" value="'+esc_attr(r)+'"')),l.html(l.val().replace(/ name\="item_number" value\="(.*?)"/,' name="item_number" value="'+esc_attr(b)+'"')),l.html(l.val().replace(/ name\="page_style" value\="(.*?)"/,' name="page_style" value="'+esc_attr(u)+'"')),l.html(l.val().replace(/ name\="currency_code" value\="(.*?)"/,' name="currency_code" value="'+esc_attr(d)+'"')),l.html(l.val().replace(/ name\="custom" value\="(.*?)"/,' name="custom" value="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>"')),l.html(l.val().replace(/ name\="amount" value\="(.*?)"/,' name="amount" value="'+esc_attr(o)+'"')),$("div#ws-plugin--s2member-ccap-button-prev").html(l.val().replace(/\<form/,'<form target="_blank"').replace(/\<\?php echo S2MEMBER_VALUE_FOR_PP_INV\(\); \?\>/g,Math.round((new Date).getTime())+"~<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr (c_ws_plugin__s2member_utils_ip::current())); ?>").replace(/\<\?php echo S2MEMBER_CURRENT_USER_VALUE_FOR_PP_(ON0|OS0|ON1|OS1); \?\>/g,"")),alert("Your Button has been generated.\nPlease copy/paste the Shortcode into your Login Welcome Page, or wherever you feel it would be most appropriate.\n\n* Remember, Independent Custom Capability Buttons should ONLY be displayed to existing Users/Members, and they MUST be logged-in, BEFORE clicking this Button."),s.each(function(){this.focus(),this.select()}),!1):(alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button."),!1):(alert("— Oops, a slight problem: —\n\nPlease provide at least one Custom Capability."),!1)},ws_plugin__s2member_paypalSpButtonGenerate=function(){var e='[s2Member-PayPal-Button %%attrs%% image="default" output="button" /]',t="",s=$("input#ws-plugin--s2member-sp-shortcode"),l=$("textarea#ws-plugin--s2member-sp-button"),r=$("select#ws-plugin--s2member-sp-leading-id").val().replace(/[^0-9]/g,""),o=$("select#ws-plugin--s2member-sp-additional-ids").val()||[],a=$("select#ws-plugin--s2member-sp-hours").val().replace(/[^0-9]/g,""),i=$("input#ws-plugin--s2member-sp-amount").val().replace(/[^0-9\.]/g,""),n=$.trim($("input#ws-plugin--s2member-sp-desc").val().replace(/"/g,"")),c="",m="0",p="1",u=$.trim($("input#ws-plugin--s2member-sp-page-style").val().replace(/"/g,"")),d=$("select#ws-plugin--s2member-sp-currency").val().replace(/[^A-Z]/g,"");if(!r)return alert("— Oops, a slight problem: —\n\nPlease select a Leading Post/Page.\n\n*Tip* If there are no Posts/Pages in the menu, it's because you've not configured s2Member for Specific Post/Page Access yet. See: s2Member → Restriction Options → Specific Post/Page Access."),!1;if(!i||isNaN(i)||i<.01)return alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01"),!1;if(i>1e4&&"USD"===d.toUpperCase())return alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00"),!1;if(!n)return alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button."),!1;for(var g=0,b=r;g<o.length;g++)o[g]&&o[g]!==r&&(b+=","+o[g]);var f="sp:"+b+":"+a;return t+='sp="1" ids="'+esc_attr(b)+'" exp="'+esc_attr(a)+'" desc="'+esc_attr(n)+'" ps="'+esc_attr(u)+'" lc="'+esc_attr(c)+'" cc="'+esc_attr(d)+'" dg="'+esc_attr(m)+'" ns="'+esc_attr(p)+'"',t+=' custom="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>" ra="'+esc_attr(i)+'"',s.val(e.replace(/%%attrs%%/,t)),l.html(l.val().replace(/ name\="lc" value\="(.*?)"/,' name="lc" value="'+esc_attr(c)+'"')),l.html(l.val().replace(/ name\="no_shipping" value\="(.*?)"/,' name="no_shipping" value="'+esc_attr(p)+'"')),l.html(l.val().replace(/ name\="item_name" value\="(.*?)"/,' name="item_name" value="'+esc_attr(n)+'"')),l.html(l.val().replace(/ name\="item_number" value\="(.*?)"/,' name="item_number" value="'+esc_attr(f)+'"')),l.html(l.val().replace(/ name\="page_style" value\="(.*?)"/,' name="page_style" value="'+esc_attr(u)+'"')),l.html(l.val().replace(/ name\="currency_code" value\="(.*?)"/,' name="currency_code" value="'+esc_attr(d)+'"')),l.html(l.val().replace(/ name\="custom" value\="(.*?)"/,' name="custom" value="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>"')),l.html(l.val().replace(/ name\="amount" value\="(.*?)"/,' name="amount" value="'+esc_attr(i)+'"')),$("div#ws-plugin--s2member-sp-button-prev").html(l.val().replace(/\<form/,'<form target="_blank"').replace(/\<\?php echo S2MEMBER_VALUE_FOR_PP_INV\(\); \?\>/g,Math.round((new Date).getTime())+"~<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr (c_ws_plugin__s2member_utils_ip::current())); ?>").replace(/\<\?php echo S2MEMBER_CURRENT_USER_VALUE_FOR_PP_(ON0|OS0|ON1|OS1); \?\>/g,"")),alert("Your Button has been generated.\nPlease copy/paste the Shortcode into your WordPress Editor."),s.each(function(){this.focus(),this.select()}),!1},ws_plugin__s2member_paypalRegLinkGenerate=function(){var e=$("select#ws-plugin--s2member-reg-link-level").val().replace(/[^0-9]/g,""),t=$.trim($("input#ws-plugin--s2member-reg-link-subscr-id").val()),s=$.trim($("input#ws-plugin--s2member-reg-link-custom").val()),l=$.trim($.trim($("input#ws-plugin--s2member-reg-link-ccaps").val()).replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase()),r=$.trim($("input#ws-plugin--s2member-reg-link-fixed-term").val().replace(/[^A-Z 0-9]/gi,"").toUpperCase()),o=$("p#ws-plugin--s2member-reg-link"),a=$("img#ws-plugin--s2member-reg-link-loading"),i=r&&!r.match(/L$/)?e+":"+l+":"+r:e+":"+l;return i=i.replace(/\:+$/g,""),t?s&&0===s.indexOf('<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq ($_SERVER["HTTP_HOST"]); ?>')?r&&!r.match(/^[1-9]+ (D|W|M|Y|L)$/)?(alert("— Oops, a slight problem: —\n\nThe Fixed Term Length is not formatted properly."),!1):(o.hide(),a.show(),$.post(ajaxurl,{action:"ws_plugin__s2member_reg_access_link_via_ajax",ws_plugin__s2member_reg_access_link_via_ajax:'<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (wp_create_nonce ("ws-plugin--s2member-reg-access-link-via-ajax")); ?>',s2member_reg_access_link_subscr_gateway:"paypal",s2member_reg_access_link_subscr_id:t,s2member_reg_access_link_custom:s,s2member_reg_access_link_item_number:i},function(e){o.show().html('<a href="'+esc_attr(e)+'" target="_blank" rel="external">'+esc_html(e)+"</a>"),a.hide()}),!1):(alert("— Oops, a slight problem: —\n\nThe Custom Value MUST start with your domain name."),!1):(alert("— Oops, a slight problem: —\n\nPaid Subscr. ID is a required value."),!1)},ws_plugin__s2member_paypalSpLinkGenerate=function(){var e=$("select#ws-plugin--s2member-sp-link-leading-id").val().replace(/[^0-9]/g,""),t=$("select#ws-plugin--s2member-sp-link-additional-ids").val()||[],s=$("select#ws-plugin--s2member-sp-link-hours").val().replace(/[^0-9]/g,""),l=$("p#ws-plugin--s2member-sp-link"),r=$("img#ws-plugin--s2member-sp-link-loading");if(!e)return alert("— Oops, a slight problem: —\n\nPlease select a Leading Post/Page.\n\n*Tip* If there are no Posts/Pages in the menu, it's because you've not configured s2Member for Specific Post/Page Access yet. See: s2Member → Restriction Options → Specific Post/Page Access."),!1;for(var o=0,a=e;o<t.length;o++)t[o]&&t[o]!==e&&(a+=","+t[o]);return l.hide(),r.show(),$.post(ajaxurl,{action:"ws_plugin__s2member_sp_access_link_via_ajax",ws_plugin__s2member_sp_access_link_via_ajax:'<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (wp_create_nonce ("ws-plugin--s2member-sp-access-link-via-ajax")); ?>',s2member_sp_access_link_ids:a,s2member_sp_access_link_hours:s},function(e){l.show().html('<a href="'+esc_attr(e)+'" target="_blank" rel="external">'+esc_html(e)+"</a>"),r.hide()}),!1}),location.href.match(/page\=ws-plugin--s2member-els-ops/)&&($("select#ws-plugin--s2member-custom-reg-opt-in").change(function(){var e=$(this),t=e.val(),s=$("tr.ws-plugin--s2member-custom-reg-opt-in-label-row"),l=$("img.ws-plugin--s2member-custom-reg-opt-in-label-prev-img");t<=0?(s.css("display","none"),l.attr("src",l.attr("src").replace(/\/checked\.png$/,"/unchecked.png"))):1==t?(s.css("display",""),l.attr("src",l.attr("src").replace(/\/unchecked\.png$/,"/checked.png"))):2==t&&(s.css("display",""),l.attr("src",l.attr("src").replace(/\/checked\.png$/,"/unchecked.png")))}),$('div.ws-plugin--s2member-opt-out-section input[type="checkbox"][name="ws_plugin__s2member_custom_reg_auto_opt_outs[]"]').change(function(){var e=$(this).val(),t=[];$('div.ws-plugin--s2member-opt-out-section input[type="checkbox"][name="ws_plugin__s2member_custom_reg_auto_opt_outs[]"]').each(function(){var t=$(this),s=t.val(),l='input[type="checkbox"]';"removal-deletion"===s&&this.checked?t.nextAll(l).slice(0,2).attr({checked:"checked",disabled:"disabled"}):"removal-deletion"!==s||this.checked?"modification"===s&&this.checked?t.nextAll(l).slice(0,3).attr({checked:"checked",disabled:"disabled"}):"modification"!==s||this.checked||("modification"===e?t.nextAll(l).slice(0,3).removeAttr("checked"):null,t.nextAll(l).slice(0,3).removeAttr("disabled")):(t.nextAll(l).slice(0,2).removeAttr("disabled"),"removal-deletion"===e?t.nextAll(l).slice(0,2).removeAttr("checked"):null)}).each(function(e){this.checked?t.push(e):null}),$("select#ws-plugin--s2member-custom-reg-auto-opt-out-transitions").removeAttr("disabled"),$.inArray(3,t)===-1&&$.inArray(4,t)===-1&&$.inArray(5,t)===-1&&$.inArray(6,t)===-1&&$("select#ws-plugin--s2member-custom-reg-auto-opt-out-transitions").attr("disabled","disabled")}).last().trigger("change"))});
  • s2member/trunk/src/includes/s2member.js.php

    r3714948 r3722434  
    44 * Generated frontend static JavaScript data map for s2Member Framework.
    55 *
    6  * Generated: 2026-09-27 03:32 UTC (260927.0332)
     6 * Generated: 2026-10-01 05:20 UTC (261001.0520)
    77 *
    88 * Generated from paired data-boundary markers in the canonical frontend JavaScript source files.
  • s2member/trunk/src/vendor/autoload.php

    r3714948 r3722434  
    55require_once __DIR__ . '/composer/autoload_real.php';
    66
    7 return ComposerAutoloaderInit06b4082f675ef419e0a31554db6464cf::getLoader();
     7return ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4::getLoader();
  • s2member/trunk/src/vendor/composer/autoload_real.php

    r3714948 r3722434  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit06b4082f675ef419e0a31554db6464cf
     5class ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4
    66{
    77    private static $loader;
    … …  
    2323        }
    2424
    25         spl_autoload_register(array('ComposerAutoloaderInit06b4082f675ef419e0a31554db6464cf', 'loadClassLoader'), true, true);
     25        spl_autoload_register(array('ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4', 'loadClassLoader'), true, true);
    2626        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
    27         spl_autoload_unregister(array('ComposerAutoloaderInit06b4082f675ef419e0a31554db6464cf', 'loadClassLoader'));
     27        spl_autoload_unregister(array('ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4', 'loadClassLoader'));
    2828
    2929        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
    … …  
    3131            require_once __DIR__ . '/autoload_static.php';
    3232
    33             call_user_func(\Composer\Autoload\ComposerStaticInit06b4082f675ef419e0a31554db6464cf::getInitializer($loader));
     33            call_user_func(\Composer\Autoload\ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4::getInitializer($loader));
    3434        } else {
    3535            $map = require __DIR__ . '/autoload_namespaces.php';
    … …  
    5252
    5353        if ($useStaticLoader) {
    54             $includeFiles = Composer\Autoload\ComposerStaticInit06b4082f675ef419e0a31554db6464cf::$files;
     54            $includeFiles = Composer\Autoload\ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4::$files;
    5555        } else {
    5656            $includeFiles = require __DIR__ . '/autoload_files.php';
    5757        }
    5858        foreach ($includeFiles as $fileIdentifier => $file) {
    59             composerRequire06b4082f675ef419e0a31554db6464cf($fileIdentifier, $file);
     59            composerRequire721ad85981bc43510cccc6c81e3ccbc4($fileIdentifier, $file);
    6060        }
    6161
    … …  
    6464}
    6565
    66 function composerRequire06b4082f675ef419e0a31554db6464cf($fileIdentifier, $file)
     66function composerRequire721ad85981bc43510cccc6c81e3ccbc4($fileIdentifier, $file)
    6767{
    6868    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
  • s2member/trunk/src/vendor/composer/autoload_static.php

    r3714948 r3722434  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit06b4082f675ef419e0a31554db6464cf
     7class ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4
    88{
    99    public static $files = array (
    … …  
    3131    {
    3232        return \Closure::bind(function () use ($loader) {
    33             $loader->classMap = ComposerStaticInit06b4082f675ef419e0a31554db6464cf::$classMap;
     33            $loader->classMap = ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4::$classMap;
    3434
    3535        }, null, ClassLoader::class);
Note: See TracChangeset for help on using the changeset viewer.