- Timestamp:
- 10/01/2026 05:36:17 AM (5 weeks ago)
- Location:
- s2member/trunk
- Files:
-
- 2 deleted
- 22 edited
-
languages/.generate (deleted)
-
languages/s2member.pot (modified) (14 diffs)
-
readme.txt (modified) (2 diffs)
-
s2member.php (modified) (4 diffs)
-
src/includes/classes/installation.inc.php (modified) (1 diff)
-
src/includes/classes/menu-pages.inc.php (modified) (3 diffs)
-
src/includes/classes/paypal-checkout-in.inc.php (modified) (28 diffs)
-
src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php (modified) (1 diff)
-
src/includes/classes/paypal-utilities.inc.php (modified) (6 diffs)
-
src/includes/classes/paypal-webhook-in.inc.php (modified) (1 diff)
-
src/includes/classes/sc-paypal-button-in.inc.php (modified) (15 diffs)
-
src/includes/classes/utils-ip.inc.php (modified) (1 diff)
-
src/includes/externals/aweber/aweber.php (modified) (1 diff)
-
src/includes/externals/aweber/aweber_api.php (modified) (1 diff)
-
src/includes/externals/aweber/oauth_application.php (modified) (1 diff)
-
src/includes/externals/markdown/nc-markdown.inc.php (modified) (1 diff)
-
src/includes/hooks.inc.php (modified) (1 diff)
-
src/includes/jquery/jquery.json-ps (deleted)
-
src/includes/menu-pages/menu-pages-s.js (modified) (2 diffs)
-
src/includes/menu-pages/menu-pages-s.min.js (modified) (1 diff)
-
src/includes/s2member.js.php (modified) (1 diff)
-
src/vendor/autoload.php (modified) (1 diff)
-
src/vendor/composer/autoload_real.php (modified) (5 diffs)
-
src/vendor/composer/autoload_static.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
s2member/trunk/languages/s2member.pot
r3714948 r3722434 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: 26 0927\n"5 "Project-Id-Version: 261001\n" 6 6 "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" 8 8 "MIME-Version: 1.0\n" 9 9 "Content-Type: text/plain; charset=UTF-8\n" … … 1310 1310 #: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:97 1311 1311 #: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:326 1312 #: src/includes/classes/paypal-utilities.inc.php:63 51313 #: src/includes/classes/paypal-utilities.inc.php:64 31312 #: src/includes/classes/paypal-utilities.inc.php:639 1313 #: src/includes/classes/paypal-utilities.inc.php:647 1314 1314 msgctxt "s2member-front" 1315 1315 msgid "Error #%1$s. %2$s." … … 1318 1318 #: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:100 1319 1319 #: pro/src/includes/classes/gateways/authnet/authnet-utilities.inc.php:329 1320 #: src/includes/classes/paypal-utilities.inc.php:5 091321 #: src/includes/classes/paypal-utilities.inc.php:6 371322 #: src/includes/classes/paypal-utilities.inc.php:64 51320 #: 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 1323 1323 msgctxt "s2member-front" 1324 1324 msgid "Error. Please contact Support for assistance." … … 2185 2185 #: src/includes/classes/sc-paypal-button-in.inc.php:258 2186 2186 #: src/includes/classes/sc-paypal-button-in.inc.php:290 2187 #: src/includes/classes/sc-paypal-button-in.inc.php:5 032188 #: src/includes/classes/sc-paypal-button-in.inc.php:7 182187 #: src/includes/classes/sc-paypal-button-in.inc.php:525 2188 #: src/includes/classes/sc-paypal-button-in.inc.php:761 2189 2189 #: src/includes/templates/buttons/paypal-ccaps-checkout-button.php:35 2190 2190 #: src/includes/templates/buttons/paypal-checkout-button.php:47 … … 4542 4542 msgstr "" 4543 4543 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 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:980 4550 msgid "Sorry, your message could not be sent. Please try again." 4551 msgstr "" 4552 4553 #: src/includes/classes/menu-pages.inc.php:1019 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:1030 4558 msgid "Please provide a valid email address." 4559 msgstr "" 4560 4561 #: src/includes/classes/menu-pages.inc.php:1033 4562 msgid "Please describe how I can help." 4563 msgstr "" 4564 4565 #: src/includes/classes/menu-pages.inc.php:1048 4566 msgid "s2Member help request" 4567 msgstr "" 4568 4569 #: src/includes/classes/menu-pages.inc.php:1087 4570 msgid "Support mailbox is not configured correctly." 4571 msgstr "" 4572 4545 4573 #: 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:9784550 msgid "Sorry, your message could not be sent. Please try again."4551 msgstr ""4552 4553 #: src/includes/classes/menu-pages.inc.php:10174554 msgid "You do not have permission to send support requests from this page."4555 msgstr ""4556 4557 #: src/includes/classes/menu-pages.inc.php:10284558 msgid "Please provide a valid email address."4559 msgstr ""4560 4561 #: src/includes/classes/menu-pages.inc.php:10314562 msgid "Please describe how I can help."4563 msgstr ""4564 4565 #: src/includes/classes/menu-pages.inc.php:10464566 msgid "s2Member help request"4567 msgstr ""4568 4569 #: src/includes/classes/menu-pages.inc.php:10854570 msgid "Support mailbox is not configured correctly."4571 msgstr ""4572 4573 #: src/includes/classes/menu-pages.inc.php:10944574 4574 msgid "Email could not be sent (mail server issue). Please try again or contact us directly." 4575 4575 msgstr "" … … 4591 4591 #: src/includes/classes/paypal-return-in-subscr-modify-w-level.inc.php:211 4592 4592 #: 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:26 64594 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:2 774593 #: 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 4595 4595 #: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:167 4596 4596 #: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:178 … … 4616 4616 4617 4617 #: 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:25 44618 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:257 4619 4619 msgctxt "s2member-front" 4620 4620 msgid "Thank you! You've been updated to:" … … 4622 4622 4623 4623 #: 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:25 54624 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:258 4625 4625 #: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:156 4626 4626 msgctxt "s2member-front" … … 4629 4629 4630 4630 #: 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:26 54631 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:268 4632 4632 msgctxt "s2member-front" 4633 4633 msgid "<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." … … 4635 4635 4636 4636 #: 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:27 64637 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:279 4638 4638 msgctxt "s2member-front" 4639 4639 msgid "<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." … … 4641 4641 4642 4642 #: 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:3 884643 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:391 4644 4644 #: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:199 4645 4645 #: src/includes/classes/paypal-return-in-web-accept-sp.inc.php:141 … … 4649 4649 4650 4650 #: 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:3 894651 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:392 4652 4652 #: src/includes/classes/paypal-return-in-wa-ccaps-wo-level.inc.php:200 4653 4653 #: src/includes/classes/paypal-return-in-web-accept-sp.inc.php:142 … … 4656 4656 msgstr "" 4657 4657 4658 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:3 494659 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:37 24658 #: 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 4660 4660 msgctxt "s2member-front" 4661 4661 msgid "<strong>Thank you! Your account has been approved.<br />The next step is to Register a Username for immediate access.</strong>" 4662 4662 msgstr "" 4663 4663 4664 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:35 04665 #: src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php:37 34664 #: 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 4666 4666 msgctxt "s2member-front" 4667 4667 msgid "Please Register Now (Click Here)" … … 4717 4717 #. again`, or something to that affect. Or, if you prefer, you could Filter 4718 4718 #. ``$response["__error"]`` with `ws_plugin__s2member_paypal_api_response`. 4719 #: src/includes/classes/paypal-utilities.inc.php:5 064719 #: src/includes/classes/paypal-utilities.inc.php:510 4720 4720 msgctxt "s2member-front" 4721 4721 msgid "Error #%1$s. %2$s. %3$s." 4722 4722 msgstr "" 4723 4723 4724 #: src/includes/classes/paypal-utilities.inc.php:55 34725 #: src/includes/classes/paypal-utilities.inc.php:5 594724 #: src/includes/classes/paypal-utilities.inc.php:557 4725 #: src/includes/classes/paypal-utilities.inc.php:563 4726 4726 msgctxt "s2member-front" 4727 4727 msgid "Error #%s. Transaction declined. Please use an alternate funding source." 4728 4728 msgstr "" 4729 4729 4730 #: src/includes/classes/paypal-utilities.inc.php:5 564730 #: src/includes/classes/paypal-utilities.inc.php:560 4731 4731 msgctxt "s2member-front" 4732 4732 msgid "Error #%s. Transaction declined. Express Checkout was NOT confirmed." 4733 4733 msgstr "" 4734 4734 4735 #: src/includes/classes/paypal-utilities.inc.php:69 04735 #: src/includes/classes/paypal-utilities.inc.php:694 4736 4736 msgctxt "s2member-front" 4737 4737 msgid " 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 4 4 Plugin URI: https://s2member.com/ 5 5 Tags: membership, content restriction, paid subscriptions, members only, paid access 6 Version: 26 09277 Stable tag: 26 09278 Tested up to: 7.2-alpha-6 39476 Version: 261001 7 Stable tag: 261001 8 Tested up to: 7.2-alpha-64027 9 9 Requires at least: 4.2 10 10 Requires PHP: 5.6.2 … … 177 177 == Upgrade Notice == 178 178 179 = v26 0927=179 = v261001 = 180 180 181 181 (SECURITY RELEASE) UPGRADE IMMEDIATELY. v260215 included a CRITICAL VULNERABILITY fix, and you shouldn't wait any longer to update if you're behind. 182 182 183 183 == 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. 184 210 185 211 = v260927 = -
s2member/trunk/s2member.php
r3714948 r3722434 21 21 /* -- This section for WordPress parsing. ------------------------------------------------------------------------------ 22 22 23 Version: 26 092724 Stable tag: 26 092723 Version: 261001 24 Stable tag: 261001 25 25 26 26 SSL Compatible: yes … … 37 37 ClickBank Compatible: yes w/s2Member Pro 38 38 39 Tested up to: 7.2-alpha-6 394739 Tested up to: 7.2-alpha-64027 40 40 Requires at least: 4.2 41 41 … … 44 44 45 45 Copyright: © 2009 WP Sharks 46 License: GNU General Public License 46 License: GNU General Public License v2 or later. 47 47 Contributors: WebSharks, JasWSInc, anguz, raamdev, bruce-caldwell, clavaque 48 48 … … 78 78 * @var string 79 79 */ 80 ${__FILE__}['tmp'] = '26 0927'; //version//80 ${__FILE__}['tmp'] = '261001'; //version// 81 81 if(!defined('WS_PLUGIN__S2MEMBER_VERSION')) 82 82 define('WS_PLUGIN__S2MEMBER_VERSION', ${__FILE__}['tmp']); -
s2member/trunk/src/includes/classes/installation.inc.php
r3701026 r3722434 294 294 } 295 295 296 /**297 * Disallow automatic updates of s2Member w/ Pro is installed.298 *299 * @package s2Member\Installation300 * @since 150717301 *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 }312 296 } 313 297 } -
s2member/trunk/src/includes/classes/menu-pages.inc.php
r3701026 r3722434 375 375 wp_enqueue_script('jquery-ui-core'); 376 376 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());378 377 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()); 380 380 381 381 do_action('ws_plugin__s2member_during_add_admin_scripts', get_defined_vars()); … … 551 551 if(!empty($error)) 552 552 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.'); 554 555 } 555 556 } … … 578 579 if(!empty($error)) 579 580 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.'); 581 583 } 582 584 } -
s2member/trunk/src/includes/classes/paypal-checkout-in.inc.php
r3714948 r3722434 78 78 exit(); 79 79 } 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']); 80 83 if(!empty($token['exp']) && is_numeric($token['exp']) && time() > (int)$token['exp']) 81 84 { … … 92 95 echo wp_json_encode(array('error' => 'token_checksum_mismatch')); 93 96 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 } 94 113 } 95 114 … … 101 120 'env_setting' => $env_setting, 102 121 'event' => 'token_ip_mismatch', 103 'token' => $ token,122 'token' => $log_token, 104 123 'ip' => c_ws_plugin__s2member_utils_ip::current(), 105 124 )); … … 145 164 'event' => 'redirect_order_create_response', 146 165 'order' => $order, 147 'token' => $ token,166 'token' => $log_token, 148 167 )); 149 168 … … 158 177 } 159 178 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 160 189 if(!$approve_url) 161 190 { … … 176 205 'event' => 'redirect_subscription_create_response', 177 206 'subscription' => $subscription, 178 'token' => $ token,207 'token' => $log_token, 179 208 )); 180 209 … … 184 213 if(!empty($link['rel']) && !empty($link['href']) && strtolower((string)$link['rel']) === 'approve') 185 214 $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 } 186 225 187 226 if(!$approve_url) … … 220 259 'payer' => !empty($capture['payer']['email_address']) ? (string)$capture['payer']['email_address'] : '', 221 260 'capture' => $capture, 222 'token' => $ token,261 'token' => $log_token, 223 262 )); 224 263 … … 232 271 { 233 272 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>'; 234 290 exit(); 235 291 } … … 367 423 'code' => !empty($subscription_r['code']) ? (int)$subscription_r['code'] : 0, 368 424 'body' => !empty($subscription_r['body']) ? (string)$subscription_r['body'] : '', 369 'token' => $ token,425 'token' => $log_token, 370 426 )); 371 427 … … 390 446 { 391 447 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>'; 392 481 exit(); 393 482 } … … 488 577 'event' => 'create_subscription_response', 489 578 'subscription' => $subscription, 490 'token' => $ token,579 'token' => $log_token, 491 580 )); 492 581 … … 546 635 'event' => 'get_plan_id_response', 547 636 'plan_id' => $plan_id, 548 'token' => $ token,637 'token' => $log_token, 549 638 )); 550 639 … … 595 684 'subscription_id' => $subscription_id, 596 685 'subscription' => $subscription_r, 597 'token' => $ token,686 'token' => $log_token, 598 687 )); 599 688 … … 681 770 )); 682 771 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'])); 683 790 exit(); 684 791 } … … 711 818 { 712 819 //260902.0200 Preserve existing non-coordinator PayPal Checkout button behavior until those flows migrate onto Gateway Checkout and gain the same activation polling. 713 //2609 07.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. 714 821 c_ws_plugin__s2member_utils_logs::log_entry('paypal-checkout', array( 715 822 'ppco' => 'checkout', … … 848 955 'env_setting' => $env_setting, 849 956 'event' => 'cancel_subscription_not_logged_in', 850 'token' => $ token,957 'token' => $log_token, 851 958 )); 852 959 … … 880 987 'event' => 'cancel_subscription_token_mismatch', 881 988 'user_id' => $user_id, 882 'token' => $ token,989 'token' => $log_token, 883 990 )); 884 991 … … 1021 1128 'event' => 'create_order_response', 1022 1129 'order' => $order, 1023 'token' => $ token,1130 'token' => $log_token, 1024 1131 )); 1025 1132 … … 1031 1138 'event' => 'order_create_failed', 1032 1139 'order' => $order, 1033 'token' => $ token,1140 'token' => $log_token, 1034 1141 )); 1035 1142 … … 1047 1154 //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. 1048 1155 $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; 1050 1158 if(!$gateway_checkout || (string)$gateway_checkout['gateway'] !== 'paypal_checkout' || (string)$gateway_checkout['operation'] !== 'payment') 1051 1159 { … … 1057 1165 $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(); 1058 1166 //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( 1060 1169 'order_id' => !empty($gateway_checkout['gateway_ids']['order_id']) ? (string)$gateway_checkout['gateway_ids']['order_id'] : '', 1061 1170 'capture_id' => !empty($gateway_checkout['gateway_ids']['capture_id']) ? (string)$gateway_checkout['gateway_ids']['capture_id'] : '', 1062 1171 'status' => !empty($gateway_checkout['gateway_status']) ? (string)$gateway_checkout['gateway_status'] : '', 1063 1172 '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); 1066 1182 exit(); 1067 1183 } … … 1104 1220 'payer' => !empty($capture['payer']['email_address']) ? (string)$capture['payer']['email_address'] : '', 1105 1221 'capture' => $capture, 1106 'token' => $ token,1222 'token' => $log_token, 1107 1223 )); 1108 1224 … … 1159 1275 'order_id' => $order_id, 1160 1276 'capture' => $capture, 1161 'token' => $ token,1277 'token' => $log_token, 1162 1278 )); 1163 1279 … … 1175 1291 'event' => 'amount_mismatch', 1176 1292 'order_id' => $order_id, 1177 'token' => $ token,1293 'token' => $log_token, 1178 1294 'pu' => array('amount' => $pu_amount, 'cc' => $pu_cc), 1179 1295 )); … … 1188 1304 'event' => 'currency_mismatch', 1189 1305 'order_id' => $order_id, 1190 'token' => $ token,1306 'token' => $log_token, 1191 1307 'pu' => array('amount' => $pu_amount, 'cc' => $pu_cc), 1192 1308 )); … … 1207 1323 'event' => 'invoice_mismatch', 1208 1324 'order_id' => $order_id, 1209 'token' => $ token,1325 'token' => $log_token, 1210 1326 'invoice' => $cap_invoice_id, 1211 1327 )); … … 1227 1343 'event' => 'custom_mismatch', 1228 1344 'order_id' => $order_id, 1229 'token' => $ token,1345 'token' => $log_token, 1230 1346 'custom' => array( 1231 1347 'token' => !empty($token['custom']) ? $token['custom'] : '', -
s2member/trunk/src/includes/classes/paypal-return-in-subscr-or-wa-w-level.inc.php
r3671073 r3722434 43 43 extract($vars, EXTR_OVERWRITE | EXTR_REFS); // Extract all vars passed in from: ``c_ws_plugin__s2member_paypal_notify_in::paypal_notify()``. 44 44 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 45 48 if((!empty($paypal['txn_type']) && preg_match('/^(web_accept|subscr_signup|subscr_payment)$/i', $paypal['txn_type'])) 46 49 && (!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 122 122 $endpoint = ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"]) ? "www.sandbox.paypal.com" : "www.paypal.com"; 123 123 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 124 128 //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"])) 126 130 return apply_filters("ws_plugin__s2member_paypal_postvars", array_merge($postvars, array("proxy_verified" => $_REQUEST["s2member_paypal_proxy"])), get_defined_vars()); 127 131 … … 1530 1534 else if(strpos($invoice, 's2msp-') === 0) 1531 1535 $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-')); 1532 1538 1533 1539 return c_ws_plugin__s2member_gateway_checkouts::valid_id($gateway_checkout_id) ? $gateway_checkout_id : ''; 1534 1540 } 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 } 1535 1602 1536 1603 /** … … 1655 1722 $private_context['paypal_checkout'] = !empty($private_context['paypal_checkout']) && is_array($private_context['paypal_checkout']) ? $private_context['paypal_checkout'] : array(); 1656 1723 //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; 1658 1727 if(!c_ws_plugin__s2member_gateway_checkouts::private_context_set($gateway_checkout_id, $private_context)) 1659 1728 return array('__error' => 'gateway_checkout_private_context_failed'); … … 2156 2225 return array('ok' => FALSE, 'error' => 'gateway_checkout_private_context_failed'); 2157 2226 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'))) 2162 2229 return array('ok' => FALSE, 'error' => 'gateway_checkout_save_failed'); 2163 2230 … … 2285 2352 $subscription_id = trim((string)$subscription_id); 2286 2353 $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. 2288 2355 2289 2356 if(!$subscription_id || !c_ws_plugin__s2member_gateway_checkouts::valid_id($gateway_checkout_id)) … … 2334 2401 } 2335 2402 } 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 } 2336 2510 2337 2511 /** -
s2member/trunk/src/includes/classes/paypal-webhook-in.inc.php
r3687613 r3722434 390 390 } 391 391 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 } 392 415 $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. 393 416 $paypal['payment_status'] = 'Completed'; -
s2member/trunk/src/includes/classes/sc-paypal-button-in.inc.php
r3714948 r3722434 298 298 $paypal_invoice_input_value = /* s2Member's Unique Code~IP combo. */ uniqid () . "~" . c_ws_plugin__s2member_utils_ip::current(); 299 299 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 300 308 $attr["sp_ids_exp"] = /* Combined "sp:ids:expiration hours". */ "sp:" . $attr["ids"] . ":" . $attr["exp"]; 301 309 … … 356 364 'checksum' => md5($paypal_invoice_input_value.c_ws_plugin__s2member_utils_ip::current().$attr["sp_ids_exp"]), 357 365 ); 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 } 358 373 359 374 $ppco_token = urlencode(c_ws_plugin__s2member_utils_encryption::encrypt(serialize($ppco_token))); … … 422 437 $code .= 'var t="'.esc_js($ppco_token).'";'."\n"; 423 438 $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. 424 440 $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"; 426 443 $code .= 'var cid="'.esc_js($paypal_invoice_input_value).'";'."\n"; 427 444 //260819.0042 Keep standalone Checkout feedback consistent with Pro-Forms while distinguishing cancellation from errors. … … 429 446 $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. 430 447 $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"; 432 449 if($ppco_intent === 'subscription') 433 450 { 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 && 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"; 435 453 $code .= 'var planId=null;'."\n"; 436 454 $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 && 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"; 438 457 $code .= 'function onCancel(){showInfo("Subscription cancelled.");}'."\n"; 439 458 $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"; … … 442 461 else 443 462 { 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"; 446 468 $code .= 'function onCancel(){showInfo("Payment cancelled.");}'."\n"; 447 469 $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"; … … 511 533 $paypal_invoice_input_value = /* s2Member's Unique Code~IP combo. */ uniqid () . "~" . c_ws_plugin__s2member_utils_ip::current(); 512 534 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 513 543 $attr["level_ccaps_eotper"] = ($attr["rr"] === "BN" && $attr["rt"] !== "L") ? $attr["level"] . ":" . $attr["ccaps"] . ":" . $attr["rp"] . " " . $attr["rt"] : $attr["level"] . ":" . $attr["ccaps"]; 514 544 $attr["level_ccaps_eotper"] = /* Clean any trailing separators from this string. */ rtrim ($attr["level_ccaps_eotper"], ":"); … … 571 601 'checksum' => md5($paypal_invoice_input_value.c_ws_plugin__s2member_utils_ip::current().$attr["level_ccaps_eotper"]), 572 602 ); 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 } 573 610 574 611 $ppco_token = urlencode(c_ws_plugin__s2member_utils_encryption::encrypt(serialize($ppco_token))); … … 637 674 $code .= 'var t="'.esc_js($ppco_token).'";'."\n"; 638 675 $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. 639 677 $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"; 641 680 $code .= 'var cid="'.esc_js($paypal_invoice_input_value).'";'."\n"; 642 681 //260819.0042 Keep standalone Checkout feedback consistent with Pro-Forms while distinguishing cancellation from errors. … … 644 683 $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. 645 684 $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"; 647 686 if($ppco_intent === 'subscription') 648 687 { 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"; 650 689 $code .= 'var planId=null;'."\n"; 651 690 $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 && 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"; 653 693 $code .= 'function onCancel(){showInfo("Subscription cancelled.");}'."\n"; 654 694 $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"; … … 657 697 else 658 698 { 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"; 661 704 $code .= 'function onCancel(){showInfo("Payment cancelled.");}'."\n"; 662 705 $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"; … … 725 768 726 769 $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 } 727 778 728 779 $attr["desc"] = (!$attr["desc"]) ? $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level" . $attr["level"] . "_label"] : $attr["desc"]; … … 816 867 ); 817 868 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 818 876 $ppco_token = urlencode(c_ws_plugin__s2member_utils_encryption::encrypt(serialize($ppco_token))); 819 877 … … 880 938 $code .= 'var t="'.esc_js($ppco_token).'";'."\n"; 881 939 $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. 882 941 $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"; 884 944 $code .= 'var cid="'.esc_js($paypal_invoice_input_value).'";'."\n"; 885 945 //260819.0042 Keep standalone Checkout feedback consistent with Pro-Forms while distinguishing cancellation from errors. … … 887 947 $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. 888 948 $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"; 890 950 if($ppco_intent === 'subscription') 891 951 { 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 && 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"; 896 959 $code .= 'function onCancel(){showInfo("Subscription cancelled.");}'."\n"; 897 960 $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"; … … 900 963 else 901 964 { 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"; 904 970 $code .= 'function onCancel(){showInfo("Payment cancelled.");}'."\n"; 905 971 $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 1 1 <?php 2 2 // @codingStandardsIgnoreFile 3 if(!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 3 6 /** 4 7 * IP utilities. -
s2member/trunk/src/includes/externals/aweber/aweber.php
r1542973 r3722434 1 1 <?php 2 2 // @codingStandardsIgnoreFile 3 if(!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 3 6 require_once('exceptions.php'); 4 7 require_once('oauth_adapter.php'); -
s2member/trunk/src/includes/externals/aweber/aweber_api.php
r1542973 r3722434 1 1 <?php 2 2 // @codingStandardsIgnoreFile 3 4 if(!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.'); 3 6 4 7 if (class_exists('AWeberAPI')) { -
s2member/trunk/src/includes/externals/aweber/oauth_application.php
r3671073 r3722434 1 1 <?php 2 2 // @codingStandardsIgnoreFile 3 if(!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 3 6 if (!class_exists('CurlObject')) require_once('curl_object.php'); 4 7 if (!class_exists('CurlResponse')) require_once('curl_response.php'); -
s2member/trunk/src/includes/externals/markdown/nc-markdown.inc.php
r3671073 r3722434 1 1 <?php 2 2 // @codingStandardsIgnoreFile 3 if(!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 3 6 /** 4 7 * PHP Markdown class. -
s2member/trunk/src/includes/hooks.inc.php
r3701026 r3722434 278 278 register_deactivation_hook($GLOBALS['WS_PLUGIN__']['s2member']['c']['plugin_basename'], 'c_ws_plugin__s2member_installation::deactivate'); 279 279 register_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 180 180 var $fields = $('input#ws-plugin--s2member-custom-reg-fields'); 181 181 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()) : []; 183 184 184 185 fields = /* Force fields to an array. */ (fields instanceof Array) ? fields : []; … … 382 383 var updateFields = /* Update hidden input value. */ function() 383 384 { 384 $fields.val(((fields.length > 0) ? $.JSON.stringify(fields) : ''));385 $fields.val(((fields.length > 0) ? JSON.stringify(fields) : '')); 385 386 }; 386 387 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,"&").replace(/</g,"<").replace(/>/g,">"),e=e.replace(/"/g,""").replace(/'/g,"'")),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("⇑ normalize viewport ❙"),e.attr("data-state","expanded")):(e.css({height:"auto","overflow-y":"scroll"}),$toggles.html("⇓ expand viewport ⇓"),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"> </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"> </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"> </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"> </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"> </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"> </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"> </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 & Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-punctuation-'+l+'"'+(m.expected==="alphanumerics-spaces-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics, Spaces & Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-spaces-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics, Spaces & Punctuation (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics & Spaces Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-'+l+'"'+(m.expected==="alphanumerics-spaces-"+l?' selected="selected"':"")+'">Alphanumerics & Spaces ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-'+l+'-e"'+(m.expected==="alphanumerics-spaces-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics & Spaces (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics & Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-punctuation-'+l+'"'+(m.expected==="alphanumerics-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics & Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics & 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"> </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"> </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 & totally invisible after registration)</option>',a+='<option value="no-always-invisible"'+("no-always-invisible"===m.editable?' selected="selected"':"")+'">No (uneditable & totally invisible, both during & 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"> </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"> </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"> </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>',1 jQuery(document).ready(function($){var esc_attr=esc_html=function(e){return/[&\<\>"']/.test(e=String(e))&&(e=e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),e=e.replace(/"/g,""").replace(/'/g,"'")),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("⇑ normalize viewport ❙"),e.attr("data-state","expanded")):(e.css({height:"auto","overflow-y":"scroll"}),$toggles.html("⇓ expand viewport ⇓"),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"> </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"> </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"> </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"> </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"> </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"> </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"> </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 & Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-punctuation-'+l+'"'+(m.expected==="alphanumerics-spaces-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics, Spaces & Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-spaces-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics, Spaces & Punctuation (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics & Spaces Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-spaces-'+l+'"'+(m.expected==="alphanumerics-spaces-"+l?' selected="selected"':"")+'">Alphanumerics & Spaces ('+l+" character minimum)</option>",a+='<option value="alphanumerics-spaces-'+l+'-e"'+(m.expected==="alphanumerics-spaces-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics & Spaces (exactly '+l+" character"+(l>1?"s":"")+")</option>";for(a+="</optgroup>",a+='<option disabled="disabled"></option>',a+='<optgroup label="Alphanumerics & Punctuation Only">',l=1;l<=25;l++)a+='<option value="alphanumerics-punctuation-'+l+'"'+(m.expected==="alphanumerics-punctuation-"+l?' selected="selected"':"")+'">Alphanumerics & Punctuation ('+l+" character minimum)</option>",a+='<option value="alphanumerics-punctuation-'+l+'-e"'+(m.expected==="alphanumerics-punctuation-"+l+"-e"?' selected="selected"':"")+'">Alphanumerics & 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"> </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"> </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 & totally invisible after registration)</option>',a+='<option value="no-always-invisible"'+("no-always-invisible"===m.editable?' selected="selected"':"")+'">No (uneditable & totally invisible, both during & 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"> </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"> </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"> </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>', 2 2 a+="</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"> </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 4 4 * Generated frontend static JavaScript data map for s2Member Framework. 5 5 * 6 * Generated: 2026- 09-27 03:32 UTC (260927.0332)6 * Generated: 2026-10-01 05:20 UTC (261001.0520) 7 7 * 8 8 * Generated from paired data-boundary markers in the canonical frontend JavaScript source files. -
s2member/trunk/src/vendor/autoload.php
r3714948 r3722434 5 5 require_once __DIR__ . '/composer/autoload_real.php'; 6 6 7 return ComposerAutoloaderInit 06b4082f675ef419e0a31554db6464cf::getLoader();7 return ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4::getLoader(); -
s2member/trunk/src/vendor/composer/autoload_real.php
r3714948 r3722434 3 3 // autoload_real.php @generated by Composer 4 4 5 class ComposerAutoloaderInit 06b4082f675ef419e0a31554db6464cf5 class ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4 6 6 { 7 7 private static $loader; … … 23 23 } 24 24 25 spl_autoload_register(array('ComposerAutoloaderInit 06b4082f675ef419e0a31554db6464cf', 'loadClassLoader'), true, true);25 spl_autoload_register(array('ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4', 'loadClassLoader'), true, true); 26 26 self::$loader = $loader = new \Composer\Autoload\ClassLoader(); 27 spl_autoload_unregister(array('ComposerAutoloaderInit 06b4082f675ef419e0a31554db6464cf', 'loadClassLoader'));27 spl_autoload_unregister(array('ComposerAutoloaderInit721ad85981bc43510cccc6c81e3ccbc4', 'loadClassLoader')); 28 28 29 29 $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); … … 31 31 require_once __DIR__ . '/autoload_static.php'; 32 32 33 call_user_func(\Composer\Autoload\ComposerStaticInit 06b4082f675ef419e0a31554db6464cf::getInitializer($loader));33 call_user_func(\Composer\Autoload\ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4::getInitializer($loader)); 34 34 } else { 35 35 $map = require __DIR__ . '/autoload_namespaces.php'; … … 52 52 53 53 if ($useStaticLoader) { 54 $includeFiles = Composer\Autoload\ComposerStaticInit 06b4082f675ef419e0a31554db6464cf::$files;54 $includeFiles = Composer\Autoload\ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4::$files; 55 55 } else { 56 56 $includeFiles = require __DIR__ . '/autoload_files.php'; 57 57 } 58 58 foreach ($includeFiles as $fileIdentifier => $file) { 59 composerRequire 06b4082f675ef419e0a31554db6464cf($fileIdentifier, $file);59 composerRequire721ad85981bc43510cccc6c81e3ccbc4($fileIdentifier, $file); 60 60 } 61 61 … … 64 64 } 65 65 66 function composerRequire 06b4082f675ef419e0a31554db6464cf($fileIdentifier, $file)66 function composerRequire721ad85981bc43510cccc6c81e3ccbc4($fileIdentifier, $file) 67 67 { 68 68 if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { -
s2member/trunk/src/vendor/composer/autoload_static.php
r3714948 r3722434 5 5 namespace Composer\Autoload; 6 6 7 class ComposerStaticInit 06b4082f675ef419e0a31554db6464cf7 class ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4 8 8 { 9 9 public static $files = array ( … … 31 31 { 32 32 return \Closure::bind(function () use ($loader) { 33 $loader->classMap = ComposerStaticInit 06b4082f675ef419e0a31554db6464cf::$classMap;33 $loader->classMap = ComposerStaticInit721ad85981bc43510cccc6c81e3ccbc4::$classMap; 34 34 35 35 }, null, ClassLoader::class);
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)