s2Member Framework (Member Roles, Capabilities, Membership, PayPal Members) - Version 150225

Version Description

= v150225 =

(Maintenance Release) Upgrade immediately.

Download this release

Release Info

Developer JasWSInc
Plugin Icon 128x128 s2Member Framework (Member Roles, Capabilities, Membership, PayPal Members)
Version 150225
Comparing to
See all releases

Code changes from version 150203 to 150225

Files changed (88) hide show
  1. changelog.md +127 -114
  2. checksum.txt +1 -1
  3. images/trans-bg.png +0 -0
  4. includes/classes/access-cap-times.inc.php +3 -3
  5. includes/classes/admin-notices.inc.php +2 -2
  6. includes/classes/auto-eots.inc.php +1 -1
  7. includes/classes/aweber.inc.php +2 -2
  8. includes/classes/constants.inc.php +37 -37
  9. includes/classes/files-in.inc.php +7 -7
  10. includes/classes/files.inc.php +5 -5
  11. includes/classes/getresponse.inc.php +1 -1
  12. includes/classes/installation.inc.php +5 -5
  13. includes/classes/list-servers.inc.php +2 -2
  14. includes/classes/login-checks.inc.php +4 -4
  15. includes/classes/login-customizations.inc.php +1 -1
  16. includes/classes/login-redirects.inc.php +2 -2
  17. includes/classes/mailchimp-o.inc.php +1 -1
  18. includes/classes/mailchimp.inc.php +1 -1
  19. includes/classes/menu-pages-rs.inc.php +1 -1
  20. includes/classes/menu-pages-tb.inc.php +1 -1
  21. includes/classes/menu-pages.inc.php +11 -35
  22. includes/classes/meta-box-security.inc.php +4 -4
  23. includes/classes/meta-boxes.inc.php +1 -1
  24. includes/classes/mo-page-in.inc.php +1 -1
  25. includes/classes/mo-page.inc.php +1 -1
  26. includes/classes/op-notices.inc.php +6 -6
  27. includes/classes/paypal-notify-in-subscr-modify-w-level.inc.php +3 -3
  28. includes/classes/paypal-notify-in-subscr-or-rp-eots-w-level.inc.php +3 -3
  29. includes/classes/paypal-notify-in-subscr-or-rp-payment-w-level.inc.php +1 -1
  30. includes/classes/paypal-notify-in-subscr-or-wa-w-level.inc.php +3 -3
  31. includes/classes/paypal-notify-in-wa-ccaps-wo-level.inc.php +3 -3
  32. includes/classes/paypal-notify-in.inc.php +1 -1
  33. includes/classes/paypal-return-in-no-tx-data.inc.php +2 -2
  34. includes/classes/paypal-return-in-proxy-ty-email.inc.php +2 -2
  35. includes/classes/paypal-return-in-proxy-x-preview.inc.php +1 -1
  36. includes/classes/paypal-return-in.inc.php +3 -3
  37. includes/classes/paypal-utilities.inc.php +3 -3
  38. includes/classes/profile-in.inc.php +1 -1
  39. includes/classes/querys.inc.php +4 -5
  40. includes/classes/readmes.inc.php +4 -7
  41. includes/classes/register-access.inc.php +1 -1
  42. includes/classes/registrations.inc.php +4 -4
  43. includes/classes/sc-files-in.inc.php +3 -3
  44. includes/classes/sc-if-conds-in.inc.php +1 -1
  45. includes/classes/sc-paypal-button-e.inc.php +1 -1
  46. includes/classes/sc-profile-in.inc.php +1 -1
  47. includes/classes/user-new-in.inc.php +2 -2
  48. includes/classes/user-securities.inc.php +16 -4
  49. includes/classes/users-list-in.inc.php +6 -6
  50. includes/classes/utilities.inc.php +6 -6
  51. includes/classes/utils-conds.inc.php +1 -1
  52. includes/classes/utils-dirs.inc.php +1 -1
  53. includes/classes/utils-encryption.inc.php +9 -9
  54. includes/classes/utils-forms.inc.php +1 -1
  55. includes/classes/utils-gets.inc.php +2 -2
  56. includes/classes/utils-logs.inc.php +17 -16
  57. includes/classes/utils-time.inc.php +11 -11
  58. includes/classes/utils-users.inc.php +1 -1
  59. includes/externals/mailchimp/Mailchimp-o.php +1 -1
  60. includes/externals/markdown/nc-markdown.inc.php +2 -2
  61. includes/functions/api-functions.inc.php +43 -43
  62. includes/hooks.inc.php +2 -0
  63. includes/menu-pages/api-ops.inc.php +87 -89
  64. includes/menu-pages/code-samples/api-mop-vars-o.x-php +2 -2
  65. includes/menu-pages/code-samples/api-mop-vars-ro.x-php +1 -1
  66. includes/menu-pages/code-samples/api-mop-vars.x-php +1 -1
  67. includes/menu-pages/code-samples/current-user-subscr-id.x-php +1 -1
  68. includes/menu-pages/code-samples/current-user-subscr-or-wp-id.x-php +1 -1
  69. includes/menu-pages/code-samples/s2-conditional-supplements-2.x-php +1 -1
  70. includes/menu-pages/code-samples/s2-conditional-supplements-3.x-php +2 -2
  71. includes/menu-pages/code-samples/s2drip-example1.x-php +6 -6
  72. includes/menu-pages/code-samples/s2member-paid-registration-time.x-php +1 -1
  73. includes/menu-pages/code-samples/sc-current-user-is-specific-content-else-farm.x-php +1 -1
  74. includes/menu-pages/code-samples/sc-current-user-is-specific-content-else.x-php +1 -1
  75. includes/menu-pages/code-samples/sc-s2-conditional-supplements-1-farm.x-php +1 -1
  76. includes/menu-pages/code-samples/sc-s2-conditional-supplements-1.x-php +2 -2
  77. includes/menu-pages/code-samples/sc-s2-conditional-supplements-3-farm.x-php +1 -1
  78. includes/menu-pages/code-samples/sc-s2-conditional-supplements-3.x-php +1 -1
  79. includes/menu-pages/down-ops.inc.php +42 -42
  80. includes/menu-pages/els-ops.inc.php +19 -21
  81. includes/menu-pages/gen-ops.inc.php +1419 -1439
  82. includes/menu-pages/info.inc.php +0 -81
  83. includes/menu-pages/logs.inc.php +25 -25
  84. includes/menu-pages/menu-pages-s-min.js +1 -1
  85. includes/menu-pages/menu-pages-s.js +4 -4
  86. includes/menu-pages/menu-pages.css +69 -43
  87. includes/menu-pages/mms-ops.inc.php +10 -12
  88. includes/menu-pages/paypal-buttons.inc.php +33 -33
changelog.md CHANGED
@@ -1,38 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  = v150203 =
2
 
3
- - (s2Member Pro) **Gift/Redemption Codes:** This release adds a powerful new shortcode: `[s2Member-Gift-Codes /]`. This makes it easy to generate and sell access to gift codes (i.e. gift certificates) and/or to a list of redemption codes. For instance, where a single team leader might like to purchase multiple accounts they can distribute to others on a team, or in a group. Video demo here: https://www.youtube.com/watch?v=T3N_vygowbM&feature=youtu.be ~ See also: [this GitHub issue](https://github.com/websharks/s2member/issues/386) for additional technical details.
4
- - (s2Member Pro) **User-Specific Coupon Codes:** This release of s2Member makes it possible to configure Pro Form Coupon Codes that are connected (i.e. only valid) when entered by specific Users/Members who are logged into the site. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/403) for additional technical details.
5
  - (s2Member Pro) **Coupon Code Max Uses:** This release of s2Member Pro adds the ability to set a maximum number of times that a Coupon Code can be used. This makes it easy to create Coupon Codes that are designed to be used only one time, for instance; or for X number of times. After a Coupon Code is used X number of times, it will expire automatically. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
6
  - (s2Member Pro) **Coupon Code Usage Tracking:** This release of s2Member Pro adds the ability to track the number of times that each of your Coupon Codes have been used. It is also possible to alter the number of uses, and/or set a maximum number of uses. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
7
  - (s2Member Pro) **Coupon Code Active/Expires Dates:** This release of s2Member Pro makes it possible to establish both a start and end time for each of your Pro Coupon Codes. In previous versions of s2Member, it was only possible to set an expiration date. You can now create Coupon Codes that will become active at some point in the future automatically. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
8
  - (s2Member Pro) **Coupon Code UI Enhancements:** This release of s2Member Pro comes with an updated UI that makes it easier to manage your Pro Coupon Codes. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
9
- - (s2Member Pro) **Store Coupon Codes for Each User:** s2Member Pro now stores a list of all coupon codes that a customer has used on your site. See: `Dashboard ⥱ Users ⥱ Choose User [Edit]`. Scrolling down to the set of s2-related fields will reveal a new list of coupon codes. This list will be filled for new customers only; i.e. s2Member does not have this data for past purchases; only for new customers that you acquire after updating to the latest release. See also [this GitHub issue](https://github.com/websharks/s2member/issues/462) if you'd additional details.
10
  - (s2Member/s2Member Pro) **EOT Custom Value:** In this release of s2Member, the `get_user_option('s2member_custom')` value is preserved after an EOT has taken place, making it possible for site owners to continue to read this value (along with any custom pipe-delimited values they have injected there), even after an EOT has taken place. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/449).
11
  - (s2Member/s2Member Pro) **JW Player Broken Links:** This release corrects some broken links referenced by the inline documentation for s2Member in the WordPress Dashboard. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/448) if you'd like further details.
12
- - (s2Member/s2Member Pro) **Security:** This release of s2Member checks for the existence of the WordPress PHP Constant: `WPINC` instead of looking for the less reliable `$_SERVER['SCRIPT_FILENAME']`. Some site owners reported this was causing trouble in a localhost environment during testing, or when running s2Member on some hosts that are missing the `SCRIPT_FILENAME` environment variable; e.g. some Windows servers. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/454) if you'd like additional details.
13
  - (s2Member Pro) **Advanced Import/Export Compat:** This release of s2Member Pro includes compatibility and a bug fix when running on WordPress v4.1+. Three PHP notices during importation, along with some quirky behavior associated with the `role` CSV column have been corrected. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/455) for technical details.
14
  - (s2Member Pro) **`[s2Member-List /]` Bug Fix:** This release resolves an issue with pagination in the `[s2Member-List /]` shortcode after recent improvements to the search functionality. See [this GitHub issue](https://github.com/websharks/s2member/issues/155#issuecomment-69403120) if you'd like additional details.
15
- - (s2Member Pro) **`[s2Member-List /]` Enhancement:** This release improves search functionality in the `[s2Member-List /]` shortcode, making it so that all searches default to `*[query]*`; i.e. are automatically wrapped by wildcards. If a user enters a wildcard explicitly (or a double quote), this default behavior is overridden and the search query is taken as given in such a scenario. This makes the search functionality easier for end-users to work with, since it no longer requires an exact match. Default behavior is now a fuzzy match. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/394) if you'd like further details.
16
  - (s2Member/s2Member Pro) **AWS v4 Authentication:** This release of s2Member adds AWS v4 Authentication support for Amazon Web Service Regions that only accept the AWS v4 authentication scheme. If you had trouble in the recent past when attempting to integrate s2Member with S3 Buckets (or with CloudFront) in regions outside the USA, this release should resolve those issues for you. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/440) if you'd like additional technical details.
17
- - (s2Member Pro) **Bug Fix:** Pro Form Checkout Options not working in all cases whenever they are used together with Free Registration Forms. Resolved in this release.
18
 
19
  = v150102 =
20
 
21
  - (s2Member/s2Member Pro) **Custom Field Mapping:** This release of s2Member adds an internal mapping from s2Member's Custom Field values for each user, to the `get_user_option()` function in the WordPress core. This makes it possible to retrieve user custom field values like always via `get_user_field()` or now through the native `get_user_option()` function also. The benefit of this is that s2Member's custom fields are now more compatible with other themes/plugins for WordPress.
22
- - (s2Member Pro) **[s2Member-List /] Shortcode:** It is now possible to search through custom fields created with s2Member using the `search_columns=""` attribute; e.g. `search_columns="user_login,user_email,s2member_custom_field_MYFIELDID"`; where `MYFIELDID` can be replaced with a field ID that you generate with s2Member via `Dashboard ⥱ s2Member ⥱ General Options ⥱ Registration/Profile Fields`. See also: [this KB article](http://www.s2member.com/kb/s2member-list-shortcode/) for further details. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/155) for details regarding this improvement.
23
  - (s2Member/s2Member Pro) **MailChimp Bug Fix** This release fixes a bug first introduced in the previous release, which was causing Interest Groups configured w/ s2Member to not be added properly. Resolved in this release. Props to @ethanpil Thanks!
24
  - (s2Member Pro) **ccBill Buttons** This release updates all ccBill button graphics. The MasterCard logo has been removed, and a new set of buttons was created to improve upon the set provided in previous versions of s2Member Pro. See: [this GitHub issue](https://github.com/websharks/s2member/issues/392) if you'd like further details.
25
- - (s2Member Pro) **Authorize.Net** The `AUD` currency code is now supported by Authorize.Net, and thus, s2Member Pro has been updated to support the `AUD` currency code for Pro Forms integrated with Authorize.Net. See [this GitHub issue](https://github.com/websharks/s2member/issues/383) if you'd like further details.
26
  - (s2Member Pro) **Subscr. CID for Stripe** This release corrects a bug which made it impossible to update the Subscr. CID value (for Stripe) using the user edit form in the Dashboard. For further details, please see [this GitHub issue](https://github.com/websharks/s2member/issues/380).
27
  - (s2Member/s2Member Pro) **Bug fix** s2Member's membership access times log was failing to collect all required access times under certain scenarios where multiple CCAPS were being added or removed in succession within the same process, but across multiple function calls. This resulted in unexpected behaviors (in rare cases) when attempting to use the `[s2Drip /]` shortcode. Fixed in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/406) for technical details.
28
  - (s2Member/s2Member Pro) **Compatibility** This release includes a fix for s2Member's Multisite Network patches applied to the `wp-admin/user-new.php` file whenever you configure s2Member on a Multisite Network. This change makes s2Member compatible with the coming release of WordPress v4.1 and v4.2-beta as it exists now. See: [this GitHub issue](https://github.com/websharks/s2member/issues/410) if you'd like additional details.
29
  - (s2Member Pro) **Bug Fix:** A feature that was previously introduced in v140816, which made it possible for site owners to set a failed payment threshold (in s2Member's Authorize.Net integration), was suffering from an off-by-one issue during total failed payment calculations. Fixed in this release. See also [this GitHub issue](https://github.com/websharks/s2member/issues/416) if you'd like further details.
30
- - (s2Member Pro) **Feature Enhancement:** Whenever a failed payment threshold is reached (in s2Member's Authorize.Net integration), not only will s2Member terminate on-site access, but now the underlying ARB (Automated Recurring Profile) is cancelled at the same exact time. This way future billing attempts on the Authorize.Net side will not be possible; i.e. it ensures that a failed payment threshold will always terminate both on-site access and the ARB itself together at the same time, as opposed to allowing the ARB termination to occur automatically via Authorize.Net, _whenever_. See also [this GitHub issue](https://github.com/websharks/s2member/issues/416) if you'd like further details.
31
  - (s2Member Pro) **ClickBank Disclaimer:** This release of s2Member adds a default Auto-Return Header Template (customizable from `s2Member ⥱ ClickBank Options` in the Dashboard) which includes a disclaimer that ClickBank requires of most merchants before final approval.
32
 
33
  _This default template should help to reduce the time it takes new merchants to receive final approval from ClickBank when first starting out in the ClickBank network. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/412) if you'd like further details._
34
- - (s2Member Pro) **Bug Fix:** PayPal Pro Forms for Specific Post/Page Access, and configured with `accept="paypal"` (i.e. to accept PayPal only) were not hiding the entire Billing Method section as intended. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/399) if you'd like further details.
35
- - (s2Member Pro) **Bug Fix:** PayPal Pro Forms using Express Checkout for Billing Agreements under a non-native currency (i.e. under a different currency than their own PayPal account) were failing under some scenarios (notably with the `BRL` currency code). Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/414) if you'd like technical details.
36
  - (s2Member Pro) **Stripe API:** s2Member's Stripe integration has been updated to use the new `statement_descriptor` field in favor of the now deprecated `statement_description`. See [this GitHub issue](https://github.com/websharks/s2member/issues/422) for technical details.
37
  - (s2Member Pro) **Stripe Bug Fix:** In the case of a global tax rate having been applied to the total cost, there were certain scenarios where s2Member Pro would kick back an error message, "Invalid Parameters to Stripe". Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/425) if you'd like technical details.
38
  - (s2Member/s2Member Pro) **WP Core Compat.:** This version of s2Member forces the `wptexturize` filter off in WordPress, due to a bug that was introduced in recent versions of the WordPress core; which results in broken shortcodes in some scenarios. Until the underlying bug is fixed in the WP core, the `wptexturize` filter must be disabled to prevent corruption of any WordPress shortcode that may contain `<` or `>` symbols.
@@ -43,11 +56,11 @@
43
  - (s2Member Pro) **Coupon Codes:** All transaction-related email templates now support three additional Replacement Codes: `%%full_coupon_code%%`, `%%coupon_code%%`, and `%%coupon_affiliate_id%%`. These have been documented in your Dashboard in places where transaction-related email templates are configured. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/384) if you'd like additional details.
44
  - (s2Member Pro) **Stripe Tax Info:** s2Member now attaches metadata to Stripe Charges and to Stripe Subscriptions which includes a JSON-encoded object containing two or more properties when tax applies.
45
 
46
- These metadata properties can be found in your Stripe Dashboard with the metadata key: `tax_info`; which contains the following JSON-encoded properties: `tax` (total tax that was or will be charged on the regular amount), `tax_per` (tax percentage rate that was applied based on your configuration of s2Member Pro); along with `trial_tax` and `trial_tax_per` in the case of a Stripe Subscription that includes an initial/trial period that requires payment; i.e. the tax applied (if any) to an initial/trial payment on a Subscription.
47
 
48
  We hope this additional information being recorded by s2Member and stored securely within your Stripe account will make it easier for you to maintain accurate bookkeeping records moving forward. This additional metadata is generated for new customers only. It will not be backfilled for any past transactions.
49
 
50
- - (s2Member Pro) **Stripe Tax Info:** s2Member now passes the tax location; i.e. `address_state`, `address_zip`, and `address_country` to each Stripe Card object associated with a Stripe Customer.
51
 
52
  We hope this additional information being recorded by s2Member and stored securely within your Stripe account will make it easier for you to maintain accurate bookkeeping records moving forward. This additional cardholder data is collected and stored for new customers only; it will not be backfilled for any past transactions.
53
 
@@ -55,28 +68,28 @@
55
 
56
  - (s2Member Pro) **Stripe Coupon Code:** s2Member now attaches metadata w/ a coupon code used by your customer (if applicable) to each Stripe Charge and/or Stripe Subscription object.
57
 
58
- This metadata property can be found in your Stripe Dashboard with the metadata key: `coupon`; which contains the following JSON-encoded property: `code` i.e. the full coupon code used by your customer. This additional metadata is generated for new customers only. It will not be backfilled for any past transactions. Filled only for transactions that use a coupon code.
59
  - (s2Member Pro) **Stripe Invoice:** This release corrects a bug in s2Member's Stripe integration whereby `subscr-signup-as-subscr-payment` was not always being forced into the core gateway processor; resulting in a miscalculation of the `last_payment_time` under certain scenarios. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/396) if you'd like additional details.
60
 
61
  = v141007 =
62
 
63
  - (s2Member Pro) **ClickBank IPN v6:** This release enables a new integration option for site owners integrated with ClickBank. You may now choose to integrate with v6 of ClickBank's IPN service, since all previous versions are slowly being phased out by ClickBank. Please see: `Dashboard ⥱ s2Member ⥱ ClickBank Options ⥱ IPN Integration` for v6 config. options. See also [this GitHub issue](https://github.com/websharks/s2member/issues/256) if you'd like further details regarding this topic. See also: [this article @ ClickBank](https://support.clickbank.com/entries/22803622-instant-notification-service).
64
- - (s2Member/s2Member Pro) **AWeber API Integration:** This release of s2Member adds a new option for site owners using AWeber. It is now possible to integrate with the new [s2Member App](http://www.s2member.com/r/aweber-api-key) for AWeber; i.e. via the AWeber API instead of via email-based communication. For further details, please see: `Dashboard ⥱ s2Member ⥱ API / List Servers ⥱ AWeber Integration`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/303) if you'd like additional details.
65
  - (s2Member/s2Member Pro) **Bug Fix:** The EOT Behavior option for `refunds,partial_refunds,reversals` was not being accepted by s2Member. Fixed in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/345) if you'd like further details.
66
- - (s2Member/s2Member Pro) **MailChimp API Wrapper:** This release of s2Member comes with an updated API wrapper class for MailChimp integration. No change in functionality, just a smoother, slightly faster, and more bug-free interaction with the MailChimp API. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/303) if you'd like further details regarding this improvement. See also: [the official MailChimp API class](https://bitbucket.org/mailchimp/mailchimp-api-php); i.e. what s2Member has been updated to in this release.
67
- - (s2Member/s2Member Pro) **URI Restrictions caSe-insensitive (Security Fix)** This release of s2Member changes the way URI Restrictions work. All URI Restrictions are now caSe-insensitive (i.e. `/some-path/` is now the same as `/some-Path/`), allowing s2Member to automatically pick up different variations used in attempts to exploit the behavior of certain slugs within the WordPress core. You can also change this new default behavior, if you prefer. Please see: `Dashboard ⥱ s2Member ⥱ Restriction Options ⥱ URI Restrictions`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/354) for the details about why this was changed in the most recent copy of s2Member.
68
  - (s2Member/s2Member) **AWeber Role-Based Emails:** In this release we're adding a note in the s2Member UI regarding role-based email addresses being rejected by AWeber. AWeber does not allow role-based emails like: `admin@` or `webmaster@` to be subscribed. It is suggested that you enable s2Member's config. option: "Force Personal Emails" if you intend to integrate with AWeber. Please see: `Dashboard ⥱ s2Member ⥱ General Options ⥱ Registration/Profile Fields`; where you can tell s2Member for force personal email addresses when someone registers on-site. This will prevent a potential subscriber from entering something like `admin@example.com` as their email address.
69
 
70
  = v140921 =
71
 
72
- - (s2Member/s2Member Pro) **WP v4 over SSL Compat.** A compatibility issue with the `home_url()` function in the latest release of WordPress has been resolved with this release. Some site owners reported that their s2Member menu pages were appearing without any CSS/JavaScript being loaded; i.e. the graphical UI was not appearing as one would expect under certain scenarios.
73
  - (s2Member/s2Member Pro) **WP v4 Compat.** This release brings s2Member up-to-date with the latest changes to the `like_escape()` function in WP v4.0. The `like_escape()` function is now deprecated in favor of `wpdb::esc_like()`. s2Member has been updated in this release, but also remains compatible with previous versions of WordPress. See [this GitHub issue](https://github.com/websharks/s2member/issues/329) if you'd like further details.
74
  - (s2Member Pro) **[s2MOP /] Shortcode Enhancment** The `[s2MOP /]` shortcode allows for a new `required_value=""` attribute. Please see [this KB article](http://www.s2member.com/kb/s2mop-shortcode/) for details about the `[s2MOP /]` shortcode. See also: [this GitHub issue](https://github.com/websharks/s2member-pro/issues/51) if you'd like further details.
75
 
76
  = v140909 =
77
 
78
  - (s2Member/s2Member Pro) **Compatibility:** Several instances of `site_url()` (a WordPress core function) have been converted to `home_url()` instead. This provides better compatibility with WordPress installations running from a sub-directory. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/293) if you'd like further details.
79
- - (s2Member Pro) **Bug Fix:** Ampersands; i.e. `&` symbols in a ClickBank button `desc=""` attribute are now converted to the word `and` automatically. The symbol itself causes issues in ClickBank's IPN processing. Fixed in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/253) if you'd like further details.
80
  - (s2Member) **Bug Fix:** Improving compatibility with Mozilla/Firefox for the default `wp-login.php?action=register` handler. This release corrects an issue where `<select>` fields contained text with too large a font-size for Mozilla browsers to deal with. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/244) if you'd like further details.
81
  - (s2Member) **WP v4.0 / bbPress Compat.** A conflict between WordPress v4.0, bbPress v2.5.4 and the previous release of s2Member has been resolved. A symptom of this issue was to see the leading topic post missing from your bbPress forum threads.
82
 
@@ -85,7 +98,7 @@
85
  However, please note that some other 3rd-party plugins may still conflict in this way; when running the latest version of bbPress under WordPress v4.0. We are working to notify bbPress and other plugin authors about this issue; just to help others avoid the problem. While unconfirmed, some site owners reported that the Relevanssi search plugin may have some trouble with this also.
86
  For further details, please see <http://bbpress.org/?p=151839>. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/321) if you'd like all of the details regarding this workaround in the s2Member software.
87
  - (s2Member) **WP v4.0 Compat.** Updating s2Member's use of the now-deprecated `get_all_category_ids()`. Using `get_terms()` instead. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/322) if you'd like further details.
88
- - (s2Member Pro) **Stripe Bug Fix:** This release corrects an issue with Stripe Pro Forms and a `$0` trial period. A symptom of this bug was to find a customer's Stripe token value missing from their Customer object in the Stripe Dashboard. This issue impacted Pro Forms whenever a 100% free trial was offered (i.e. with a `$0` sale amount). Resolved by this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/326) if you'd like the details.
89
 
90
  = v140816 =
91
 
@@ -100,25 +113,25 @@
100
 
101
  See also: [this GitHub issue](https://github.com/websharks/s2member/issues/179) if you'd like more info.
102
  * (s2Member Pro) **Authorize.Net** This release introduces a new configurable EOT Behavior option for site owners integrated with Authorize.Net. It is now possible to configure a Max Failed Payments threshold; after which s2Member will automatically trigger an EOT (End Of Term). See: `Dashboard ⥱ s2Member ⥱ Authorize.Net Options ⥱ EOT Behavior`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/141) if you'd like more info.
103
- * (s2Member/s2Member Pro) **Enhancement** A new Replacement Code (`%%current_user_nicename%%`) has been introduced by s2Member to improve compatibility with plugins like BuddyPress and bbPress. If you are currently using a Special Login Redirection URL as your s2Member Login Welcome Page, and you've used `%%current_user_login%%` (i.e. the old way), we suggest that you update your Special Redirection URL to use `%%current_user_nicename%%`. Please see [Jason's comments here](https://github.com/websharks/s2member/issues/276#issuecomment-51706582) for further details. See also: `Dashboard ⥱ s2Member ⥱ General Options ⥱ Login Welcome Page`. It is this area of your Dashboard where a Special Redirection URL can be configured.
104
  * (s2Member Pro) **Pro Login Widget** There are some new Replacement Codes available for the "My Account" page URL, and the "My Profile" page URL whenever you configure the s2Member Pro Login Widget in WordPress. s2Member Pro now supports things like `%%current_user_nicename%%` and `%%current_user_level%%` in these customizable URLs. See: `Dashboard ⥱ Appearance ⥱ Widgets ⥱ s2Member Pro Login Widget` for further details. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/276#issuecomment-51706582) if you'd like more info.
105
- * (s2Member Pro) **Stripe Bug Fix** Wrapping multiple Stripe Pro Form Shortcodes together as "Checkout Options" was not working properly in the previous release. The dropdown for Checkout Options was not maintaining the underlying variable needed to keep a Checkout Option selected. Fixed in this release. If you'd like more info, please see [this GitHub issue](https://github.com/websharks/s2member/issues/296).
106
  * (s2Member Pro) **Stripe Bug Fix** A few site owners reported issues between s2Member's integration with Stripe and other plugins that also depend on the Stripe SDK for PHP. Fixed in this release. If you'd like more info, please see [this GitHub issue](https://github.com/websharks/s2member/issues/295).
107
- * (s2Member Pro) **Pro Cancellation Forms** This release introduces a new Shortcode Attribute that can be used with Pro Cancellation Forms. The new Shortcode Attribute is `unsub=""`. Setting this to a value of `unsub="1"` will enable an automatic unsubscribe upon cancellation. To clarify, this is related to any List Servers (e.g. MailChimp, AWeber, GetResponse) that you integrate with s2Member. If `unsub="1"` when a customer cancels future billing they will also be removed from the mailing list they are currently subscribed to, according to your List Server configuration in s2Member. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/238) if you'd like more info.
108
- * (s2Member/s2Member Pro) **Child Theme Compabitility** Portions of s2Member (e.g. Return-Page templates, s2Stream templates, Pro Form templates, IP Restriction templates, and more) allow advanced site owners to use custom templates of their own. In the past these templates *had* to be created inside the parent theme directory or inside of your `/wp-content/` directory. Starting with this release, s2Member will also look for custom templates inside of your current Child Theme directory too (i.e. `get_stylesheet_dir()`). Please see [this GitHub issue](https://github.com/websharks/s2member/issues/271) if you'd like more info.
109
  * (s2Member/s2Member Pro) **S3/CloudFront Compatibility** The latest release of s2Member has been made compatible with the latest changes at Amazon S3/CloudFront [regarding IAM users](http://aws.amazon.com/blogs/aws/updated-iam-console/). If you've been running s2Member together with Amazon S3/CloudFront there are no changes necessary in s2Member configuration. If you are just integrating s2Member with Amazon S3/CloudFront you are advised to setup an IAM user instead of using your AWS Root Keys. s2Member will continue to work with either Root Keys or with IAM user keys. Either are fine. If you'd like more info, please see [this GitHub issue](https://github.com/websharks/s2member/issues/297).
110
  * (s2Member/s2Member Pro) **MySQLi Compatiblity** This release brings s2Member into full compatibility with the MySQLi extension. In the previous release it was reported that one specific routine in s2Member that checks the total number of users in your WordPress database was incompatible with MySQLi. Resolved in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/274) if you'd like more info.
111
  * (s2Member/s2Member Pro) **Amazon CloudFront** s2Member now makes a new button [Reset CloudFront Configuration] available inside of your WP Dashboard under: `s2Member ⥱ Download Options ⥱ Amazon CloudFront`. This button allows a site owner (if necessary) to do a quick reset of s2Member's current integration with Amazon CloudFront Distributions. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/270) if you'd like more info.
112
  * (s2Member/s2Member Pro) **Bug Fix** This release corrects a bug in s2Member's log of a user's WordPress Capability access times. This bug had no serious impact on previous releases of s2Member. However, it was a bug that needed fixing nonetheless. Resolved in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/237) if you'd like more info.
113
- * (s2Member/s2Member Pro) **Responsive Compatibility** This release of s2Member makes the `/wp-login.php` file (the WordPress Login/Registration system) Responsive; i.e. more compatible with mobile devices and tablets. This only impacts site owners that use s2Member's Login/Registration Design options to improve and customize the appearance of this core WordPress component. No changes necessary to existing installations for this to kick-in. It's automatic (assuming you are using this s2Member feature). Please see [this GitHub issue](https://github.com/websharks/s2member/issues/211) if you'd like more info.
114
  * (s2Member Pro) **Remote Operations API** This release of s2Member makes it possible to change the API Key assigned to your WordPress installation. The Pro Remote Operations API is one way for developers to integrate with some of s2Member's functionality. See: `Dashboard ⥱ s2Member ⥱ API / Scripting ⥱ Remote Operations API`. This is where it's possible to change your API Key if you'd like to. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/206) if you'd like more info.
115
- * (s2Member/s2Member Pro) **Enhancement** s2Member's Auto-Return system (i.e. default Thank-You page handler) which integrates with: PayPal Standard Buttons, ClickBank, and Google Wallet; has been updated in this release. If a customer happens to find their way back to a self-expiring Auto-Return URL (a rare occurrence); instead of an unfriendly error message about duplicate return data, s2Member now provides a more friendly note that asks the customer to check their email for the details needed to access what they paid for. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/205) if you'd like more info.
116
  * (s2Member/s2Member Pro) **Enhancement** s2Member's Security Encryption Key configuration panel in the Dashboard was updated to include additional details related to the use of your Security Encryption Key. This additional information explains s2Member's use of this key in greater detail. See: `Dashboard ⥱ s2Member ⥱ General Options ⥱ Security Encryption Key`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/182) if you'e like more info.
117
- * (s2Member Pro) **One-Time-Offers Upon Login** This release resolves a conflict between s2Member's Pro Login Widget and One-Time-Offers (Upon Login) a feature that can be configured with s2Member Pro. One-Time-Offers (Upon Login) now take precedence over certain automatic login redirections that can occur through the Pro Login Widget, removing the chance of a conflict. If a visitor logs into their account with a default login redirection URL (i.e. a redirection URL formulated dynamically by the Pro Login Widget that is not related to a visitor's request to access a specific page of the site), and a One-Time-Offer is triggered at the same time, the One-Time-Offer will take precendence. The visitor will see the One-Time-Offer instead of being redirected to the default location specified by the Pro Login Widget. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/119) if you'd like more info.
118
 
119
  = v140725 =
120
 
121
- * (s2Member Pro) **NEW: Stripe Pro Forms (Beta)** Holy hula hoop! s2Member now has a deep integration with Stripe for on-site credit card processing that uses a Stripe overlay. Stripe is an almost-free service that is super easy to setup and work with. We think you'll find that Stripe is quickly becoming the MOST popular of all payment gateways in the industry. A few bugs have [already been zapped](https://github.com/websharks/s2member/issues?milestone=5&page=1&state=closed) prior to this release after some initial beta testing was completed over the last few weeks. Everything is looking great so far, but please do [report any new issues via GitHub](https://github.com/websharks/s2member/issues?page=1&state=open).
122
 
123
  **If you are an s2Member Pro site owner** you can upgrade to the latest version of s2Member Pro at anytime you like; then enable Stripe as an additional payment gateway option. See: `Dashboard ⥱ s2Member Pro ⥱ Other Gateways`.
124
 
@@ -128,21 +141,21 @@
128
  * (s2Member Pro) **ClickBank Bug Fix** Nillable fields causing some issues (only under one specific scenario) after a recent change in the ClickBank API. Fixed in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/250) if you would like further detais.
129
  * (s2Member Pro) **Compatibility** Resolved a minor single-quote issue in the Visual Editor. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/228) if you'd like further details.
130
  * (s2Member/s2Member Pro) **Bug Fix** Non-HTML whitespace being trimmed inside the `[s2If /]` shortcode. Resolved in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/224) if you'd like further details.
131
- * (s2Member Pro) **Pro Forms Bug Fix** Related to List Server processing during an upgrade. This bug impacted all payment gateways integrated with s2Member's Pro Forms; including PayPal, Authorize.Net and now Stripe. Resolved in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/216) if you'd like further details.
132
  * (s2Member Pro) **Bug Fix** An `array_intersect()` error in the `[s2MOP /]` shortcode under the right conditions. Fixed in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/204) if you'd like further details.
133
- * (s2Member Pro) **Enhancement** Free Registration Pro Forms can now be included in a list of nested Checkout Option drop-downs. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/197) if you'd like further details. See also: `Dashboard ⥱ s2Member ⥱ [Your Payment Gateway] Pro Forms ⥱ Checkout Options`; where there is more information about how to use Checkout Options with s2Member Pro Forms.
134
  * (s2Member/s2Member Pro) **Bug Fix / List Servers** This release corrects a bug that may cause members to be unsubscribed from a List Server if they forget to check the box again while editing their profile. In the previous release we introduced a feature that allows the checkbox to be pre-checked if the user already chose to subscribe once before. However, the internal tracking for this was not as reliable as it could be. There is still more work to be done on this front, but the immediate issue has now been resolved. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/196) if you'd like further details.
135
  * (s2Member/s2Member Pro) **WP_DEBUG Compat.** Some older portions of s2Member's codebase were cleaned up and reformatted in this update. This is an ongoing process to bring s2Member into full compatibility with `WP_DEBUG` mode in WordPress. This is intended to help other developers in the WP community. Much progress has been made on this front now, but still some more work to do. We will continue to update s2Member's codebase little-by-little with each release.
136
  * (s2Member/s2Member Pro) **Uninstall vs. Deactivation** s2Member has always made a strong effort to cleanup after itself should you decide (for whatever reason) to uninstall it. However, we are also very concerned with preserving any data associated with such a powerful plugin that comes with so many options. Nobody wants accidental data loss, right!?
137
 
138
 
139
- In the past, s2Member came with a feature called "Deactivation Safeguards". These (if disabled) attached themselves to a plugin "deactivation" hook within WordPress and they would uninstall s2Member when you deactivated the plugin. Starting with this release ,s2Member's Deactivation routines have been changed. We now attach them to the WordPress "uninstall" hook which is triggered only on plugin deletion.
140
 
141
  In this way, deactivating s2Member will never result in a loss of any data. Instead of "Deactivation Safeguards", s2Member now calls this feature "Plugin Deletion Safeguards". In short, to tell s2Member to uninstall itself (including any data/options associated with s2Member), you can simply disable s2Member's Plugin Deletion Safeguards under: `s2Member ⥱ General Options`, and then deactivate _and delete_ the s2Member plugin entirely. Whenever you delete the plugin this automatically and silently triggers s2Member's uninstaller which cleans up after itself nicely :-)
142
 
143
  = v140630 =
144
 
145
- - (s2Member Pro) **Stripe Payment Gateway** Hooray! s2Member Pro now integrates with [Stripe](http://www.s2member.com/r/stripe). s2Member Pro Forms, Coupon Codes, Tax Settings, Checkout Options, and all of the other great features provided by s2Member Pro are now compatible with Stripe. To enable Stripe in your installation of s2Member, please see: `Dashboard ⥱ s2Member ⥱ Other Gateways ⥱ Stripe`.
146
 
147
  We expect Stripe to become the most popular payment gateway integration for s2Member Pro over the next few months. Stripe is nearly free; it's easier to setup, easier to maintain; and just more flexible overall in our opinion.
148
 
@@ -151,21 +164,21 @@
151
 
152
  = v140614 =
153
 
154
- * (s2Member/s2Member Pro) **Quick Cache Compat.** This release makes it possible for Quick Cache to cache content protected by s2Member. If, and only if, you have [Quick Cache Pro](http://www.websharks-inc.com/product/quick-cache/) configured to enable user-specific caching; i.e. to cache when users are logged into the site. See [this GitHub issue](https://github.com/websharks/s2member/issues/172) if you'd like further details.
155
  * (s2Member Pro) **ClickBank API Compat.** This release brings s2Member's ClickBank integration into full compatibility with a recent change in the ClickBank API. Under the right conditions, a symptom of this bug was to see an error regarding an unexpected `txnType` upon returning from checkout via ClickBank. Resolved in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/199) if you'd like further details.
156
 
157
  = v140603 =
158
 
159
- * (s2Member/s2Member Pro) **Profile Sync to List Servers** If you have a supported List Server integrated with s2Member (e.g. MailChimp, GetResponse, AWeber), the opt-in checkbox will now also be displayed in user profile editing panels (i.e. by the `[s2Member-Profile /]` shortcode, or if you integrate s2Member with BuddyPress profiles).
160
 
161
- If a user updates their profile, their profile on the List Server is updated too (i.e. s2Member updates their first name, last name, along with any merge vars or customs you've integrated through s2Member hooks/filters). If a user changes their email address, they will be subscribed with the new email address. Leaving the box unchecked during a profile update will effectively unsubscribe the user from the lists you have configured at their current Membership Level.
162
 
163
- *s2Member's AWeber integration does not yet support profile updates (i.e. changes in first/last name or other details); it only supports the ability to either subscribe or unsubscribe; and/or changes in email address.*
164
 
165
  See [this GitHub issue](https://github.com/websharks/s2member/issues/146) if you'd like further details.
166
  * (s2Member/s2Member Pro) **BuddyPress Compatibility** This release makes s2Member and s2Member Pro compatible with the latest release of BuddyPress. BuddyPress v2.01 broke some of s2Member's previous integration. Fixed in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/162) if you'd like further details.
167
  * (s2Member/s2Member Pro) **Multisite Lost Password URL** In a multisite environment (given the WordPress default behavior), a lost password URL generated by WordPress will force all users to recover their password from the Main Site in the Network; which is usually NOT desirable. This release of s2Member fixes this odd behavior in the WordPress core by allowing users to recover their password in the UI for the current Child Blog they are accessing. See [this GitHub issue](https://github.com/websharks/s2member/issues/138) if you'd like further details and ways to enable/disable; or even customize this further.
168
- * (s2Member/s2Member Pro) **Default EOT Behavior** By popular demand, this release changes s2Member's default EOT (End Of Term) Behavior option with respect to refunds/chargebacks. The new default behavior (assuming you have not yet configured s2Member) is to force an EOT on chargebacks only; not on a refund of any kind. A refund is just a refund (in many cases) and an EOT (if one should occur) is something that a site owner generally should decide on their own; i.e. to review refunds manually and if an EOT should occur, the site owner will mostly like prefer to terminate a user's account access on their own; and on a case-by-case basis.
169
 
170
  **Further clarification...** EOT (End Of Term) is meant to occur when a subscription ends, and since a refund doesn't necessarily end a subscription, it's not the default anymore to trigger an automatic EOT on a refund event. Site owners that want an EOT on refunds still have that option available to them however. See `Dashboard ⥱ s2Member ⥱ [Payment Gateway] Options ⥱ Auto EOT Behavior ⥱ Chargebacks/Refunds`.
171
 
@@ -182,7 +195,7 @@
182
 
183
  * (s2Member Pro) **`[s2Member-List /]` Shortcode** Amazing new feature! It is now possible to list members using a powerful shortcode, and even make it possible for members to view and search for each other. See [this KB article](http://www.s2member.com/?p=62860) for all the details on this feature. Very cool!
184
  * (s2Member/s2Member Pro) **Server-Side Validation** For Registration/Profile Fields that you configure on your own (using the s2Member software), there is now support within all areas of the s2Member codebase for both JavaScript and *now server-side validation too*. In the past, all validations applied to custom fields was via JavaScript only. With server-side validation too, now it is impossible for required and/or invalid fields that you configure to go missing. This also resolves a few issues related to spam bots attempting to bypass JavaScript validation. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/99) if you'd like further details.
185
- * (s2Member Pro) **Button Processing Animation**. Improving the processing animation used in submit buttons across Pro Forms. Instead of a script-based solution (rather jumpy), we are now taking advantage of CSS3 for a much smoother animation. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/114) if you'd like further details.
186
  * (s2Member Pro) **`[s2Drip /]` Shortcode Enhancement** This release adds support for a new `access=""` shortcode attribute that can parse `and` / `or` logic. Also, it is now possible for `[s2Drip /]` to be used with Custom Capabilities too! See [this KB article](http://www.s2member.com/kb/s2drip-shortcode/) for all the details.
187
  * (s2Member/s2Member Pro) **Bug Fix, Custom Fields UI** This release corrects a bug related to the `jquery/.htaccess` file that ships with s2Member. A symptom was to have issues with the Registration/Profile Fields UI and find a JS error in the browser's developer console. Fixed in this release. See also, [this GitHub issue](https://github.com/WebSharks/s2Member/issues/144#issuecomment-43198045) if you'd like further details.
188
  * (s2Member Pro) **Bug Fix, Clickbank HTTPS** This release corrects a bug in the default Auto-Return Page for ClickBank, when/if it's served over the `https` protocol. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/145) if you'd like further details.
@@ -238,22 +251,22 @@
238
  * (s2Member) **Cosmetics**. Updating s2Member's adminitrative menu pages to enhance their appearance in the most recent versions of WordPress. Nothing significant, just minor tweaks.
239
  * (s2Member Pro) **PayPal IPNs**. Adding support for `txn_type` values: `merch_pmt`, `mp_signup`, and `mp_cancel` to enhance s2Member's support for Billing Agreements when integrated together with a PayPal Pro (Payflow Edition) account that processes Express Checkout transactions. This also resolves a bug related to cancellation IPN processing in a specific scenario. Fixed in this release.
240
  * (s2Member Pro) **[s2If][else]**. Requires s2Member Pro. This release adds support for a new `[else]` condition when using the `[s2If]` shortcode to protect parts of your content. For examples, please see: `Dashboard ⥱ s2Member ⥱ API Scripting ⥱ Simple Shortcode Conditionals`. See also: `Dashboard ⥱ s2Member ⥱ Restriction Options ⥱ Simple Shortcode Conditionals`.
241
- * (s2Member Pro) **[s2If php=""]**. Requires s2Member Pro. This release adds support for a new `php` Shortcode Attribute; e.g. `[s2If php=""]`. This makes it possible to use arbitrary PHP code in your `[s2If]` shortcodes. For examples, please see: `Dashboard ⥱ s2Member ⥱ API Scripting ⥱ Simple Shortcode Conditionals`. **Note: this feature is disabled by default.** You must enable the `php` attribute for this to work as expected. Please see: `Dashboard ⥱ s2Member ⥱ Restriction Options ⥱ Simple Shortcode Conditionals` (with s2Member Pro installed).
242
  * (s2Member) **Bug Fix**. Correcting an issue related to Login Redirections and an E_NOTICE. Please see [this GitHub issue](https://github.com/WebSharks/s2Member/issues/75) if you'd like further details.
243
  * (s2Member Pro) **[s2Drip]**. Requires s2Member Pro. This release adds support for a new easy-to-use shortcode that can drip content to paying Members based on their Membership Level. For examples, please see: `Dashboard ⥱ s2Member ⥱ API Scripting ⥱ Content Dripping`.
244
  * (s2Member Pro) **ClickBank**. Adding support for the `vtid` parameter in ClickBank Button Shortcodes. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/44) if you'd like further details.
245
- * (s2Member Pro) **PayPal Express Checkout**. Updating PayPal Express Checkout cancellation links so they will automatically use the PayPal Merchant ID supplied by a site owner (i.e. PayPal's recommended behavior). If you'd like further details please see [this GitHub commit](https://github.com/WebSharks/s2Member-Pro/commit/5efbe35eed352868a956c94e51ab09f8e561892a).
246
- * (s2Member Pro) **Pro Forms**. Adding a new filter for developers `s2member_pro_cancels_old_rp_before_new_rp`. This is true by default. If you'd like to prevent s2Member from terminating an existing Recurring Profile (before creating a new one); i.e. during an upgrade... you can set this to a FALSE value. Not recommended, but there are a few edge cases where it could be helpful for developers. See also [this GitHub commit](https://github.com/WebSharks/s2Member-Pro/commit/19a84c81070bb0e1869b5dbd9d0325cc458fd016).
247
  * (s2Member Pro) **ClickBank Bug Fix**. Adding support for alphabetics in ClickBank Item Numbers. This bug impacted the ClickBank Button Generator only, it did not prevent alphabetics from being used in a raw Shortcode. Still, this has been resolved now. See also [this GitHub issue](https://github.com/WebSharks/s2Member/issues/46).
248
  * (s2Member Pro) **PayPal Mobile Bug Fix**. This release corrects a bug related to the `MAXAMT` PayPal specification that is sent via Express Checkout on a mobile device. A symptom of this bug was to sometimes see PayPal's awkward default amount of `$25` when completing checkout on a mobile device. Fixed in this release.
249
- * (s2Member Pro) **PayPal Express Checkout**. This release addresses an issue where a customer reaches PayPal.com with an item description that inadequately reads "Future payment" (i.e. missing the description provided by a site owner). PayPal's latest improvements have made it possible for s2Member to get this right. Resolved in this release.
250
  * (s2Member/s2Member Pro) **Partial Refunds**. This release adds support for Partial Refunds in the s2Member EOT Behavior Options. See also [this GitHub issue](https://github.com/WebSharks/s2Member/issues/40).
251
 
252
  = v140105 =
253
 
254
- * (s2Member/s2Member Pro) **Compatibility**. Updated to support WordPress v3.8. Nothing significant, just minor UI tweaks in the Login/Registration Design for WordPress v3.8; e.g. `wp-login.php` got some minor improvements in this release of s2Member and s2Member Pro.
255
- * (s2Member) **Compatibility**. PayPal's API for Subscription Cancellation Buttons was changed recently. PayPal now requires a Merchant ID instead of the site owner's email address. This issue has been causing an error when a user attempts to cancel a PayPal Subscription through an s2Member-generated PayPal Subscription Cancellation "Button" (i.e. this affects Buttons only, not Pro Forms). Fixed in this release. Site owners using PayPal Buttons should update their PayPal Merchant ID for s2Member. Please see: `Dashboard ⥱ s2Member ⥱ PayPal Options ⥱ Account Details`.
256
- * (s2Member/s2Member Pro) **Compatibility**. Default s2Member option value for CSS/JS Lazy Loading is now off instead of on; e.g. s2Member's CSS/JS JavaScript libraries are now loaded on every page by default. Lazy loading must now be enabled by a site owner as a performance enhancement (optional). For further details, please see: `Dashboard ⥱ s2Member ⥱ General Options ⥱ CSS/JS Lazy Loading`.
257
  * (s2Member Pro) **Remote Operations API**. This release introduces two new API methods; `auth_check_user` and `get_user`. These methods (combined with those which already exist in s2Member Pro) now make s2Member Pro's Remote Operations API a pleasure to work with. For further details, please see: `Dashboard ⥱ s2Member Pro ⥱ API Scripting ⥱ Pro Remote Operations API`. Here you will find the API Key for your installation, along with several code samples.
258
  * (s2Member/s2Member Pro) **Logging**. s2Member's core payment gateway processors now log to files `gateway-core-ipn.log` and `gateway-core-rtn.log`. These log file names were changed in this release. In previous versions of s2Member these log entries were kept inside `paypal-ipn.log` and `paypal-rtn.log`.
259
  * (s2Member/s2Member Pro) **Updates**. The XML/RSS feed box for the most recent s2Member Updates (for site owners only; in the Dashboard); has been updated to our newest feed location at: `http://feeds.feedburner.com/s2member`.
@@ -265,27 +278,27 @@
265
  * (s2Member/s2Member Pro) **Compatibility**. Adding support for `$_SERVER['HTTP_AUTHORIZATION']` when s2Member is used for Remote Auth file hosting. Some servers do not support `$_SERVER['PHP_AUTH_USER']`. Instead, s2Member can get the username/password by parsing them out of `$_SERVER['HTTP_AUTHORIZATION']` when/if necessary. Fixed in this release.
266
  * (s2Member/s2Member Pro) **PHP Debug Notices.** Updating s2Member's source code to further prevent PHP debug notices when running in `WP_DEBUG` mode. This is part of an ongoing effort keep s2Member running smoothly in PHP strict mode; and to maintain conformity with WordPress standards.
267
  * (s2Member Pro) **Bug Fix**. Define `abbr_bytes()` method as static to prevent issues during automatic upgrades of s2Member Pro. Please see <https://github.com/WebSharks/s2Member/issues/37> for further details.
268
- * (s2Member/s2Member Pro) **Backward Compatibility.** Updating calls to `get_post()`. We're adding a `NULL` argument via `$null` to prevent warnings in older releases of WordPress where an argument was required; e.g. `$null = NULL; get_post($null)`.
269
 
270
  = v131126 =
271
 
272
  * (s2Member Pro) **Google Wallet.** s2Member Pro now supports [Google Wallet for Digital Goods](https://developers.google.com/commerce/wallet/digital/).
273
 
274
- In the past we offered support for Google "Checkout" (Google "Checkout" is no longer available as of November 2013). s2Member's previous integration with Google Checkout has now been fully updated to support Google "Wallet" for Digital Goods. To enable Google Wallet in your installation of s2Member Pro, please see: `Dashboard -› s2Member Pro -› Other Gateways -› Google Wallet`. For setup instructions, please see: `Dashboard -› s2Member Pro -› Google Options`.
275
 
276
  At the time of this writing, there are no KB articles related to Google Wallet at s2Member.com (yet); these will come soon. Until then, you might find it helpful to review dev notes by Jason Caldwell (Lead Developer). Please see: <https://github.com/WebSharks/s2Member/issues/19>.
277
 
278
- We also suggest that you review the documentation in your Dashboard under: `s2Member Pro -› Google Options` and `s2Member Pro -› Google Buttons`. If you were previously using Google "Checkout", please review [this notice posted by Google](https://support.google.com/checkout/sell/answer/3080449?hl=en).
279
- * (s2Member/s2Member Pro) **Bootstrap Compatibility.** s2Member has been updated throughout to support the [Twitter Bootstrap](http://getbootstrap.com/) CSS framework when running on a WordPress theme that's been built on Bootstrap. For instance; profile editing forms, custom registration/profile fields, s2Member Pro Forms for checkout/registration; these will now look good on sites powered by Bootstrap. This change has no impact on functionality, only on appearance; and only IF your site is powered by Bootstrap. Throughout s2Member's HTML code we've added CSS class names that follow a Bootstrap standard. These Bootstrap classes are blended together with default structural styles that makes s2Member compatible with all WordPress themes. This way s2Member (and s2Member Pro) can produce a clean/professional appearance on just about any WordPress theme; and now Bootstrap is supported too!
280
- * (s2Member Pro) **Configurable Emails.** We've added new configuration panels into the s2Member UI for a Modification Confirmation Email and also for the Custom Capability Confirmation Email. These are now configurable for all payment gateways integrated with s2Member Pro; e.g. AliPay, ccBill, ClickBank, Google Wallet, Authorize.Net, PayPal Standard, and PayPal Pro. These emails have always existed, but up until now customization required a WordPress filter. Now it's easier, you can customize these from the Dashboard! Please note: this feature comes only with s2Member Pro. As one example, please check your Dashboard under: `s2Member Pro -› PayPal Options -› Modification Confirmation Email`.
281
  * (s2Member/s2Member Pro) **Snippets/Redirects.** Snippets and Redirects no longer carry the s2Member Restriction Options meta box in the Post/Page editing station; there's no need for Restrictions against these two special Post Types. This change, together with the latest improvements in these two plugins: [WP Snippets](http://wordpress.org/plugins/wp-snippets/) and [WP Redirects](http://wordpress.org/plugins/wp-redirects/) (also produced by our team) offer a more powerful solution now that all of these plugins are more compatible with each other.
282
- * (s2Member/s2Member Pro) **Backward Compatibility.** Updating calls to `get_post()`. We're adding a `NULL` argument to prevent warnings in older releases of WordPress where an argument was required; e.g. `get_post(NULL)`.
283
- * (s2Member/s2Member Pro) **Forward Compatibility.** Adding support for the `relative` scheme in SSL filters that deal with `set_url_scheme()` in the latest versions of WordPress. This improves s2Member's "force SSL mode" where a site owner sets the Custom Field for a Post/Page; e.g. `s2member_force_ssl` is set to `yes`. This change will better support themes/plugins that use absolute relative paths together with WordPress core functions like `site_url()` and `home_url()`.
284
  * (s2Member/s2Member Pro) **PHP Debug Notices.** Updating s2Member's source code to further prevent PHP debug notices when running in `WP_DEBUG` mode. This is part of an ongoing effort keep s2Member running smoothly in PHP strict mode; and to maintain conformity with WordPress standards.
285
- * (s2Member Pro) **Unlimited Membership Levels.** Updating the built-in software documentation for s2Member Pro to cover an edge case where a site owner many choose to exceed the recommended maximum for Membership Levels when running s2Member Pro. If you intend to use more than `100` Membership Levels (this is not recommended); but if you do, please see: `Dashboard -› s2Member Pro -› General Options -› Membership Levels/Labels` for the latest details on this.
286
  * (s2Member/s2Member Pro) **Dropping IE8 Support.** s2Member and s2Member Pro will no longer support IE8 in any official capacity. s2Member's HTML output and CSS files have been cleaned up; and all hacks related to IE8 have been removed. Out with the old, in with the new! We need to keep s2Member up-to-date with the latest improvements offered by IE9 and other modern browsers. While s2Member may continue to function relatively well in IE8, hacks used in the past to accomodate edge cases in this buggy browser have been removed in favor of standards compliance.
287
- * (s2Member/s2Member Pro) **Lazy Loading CSS/JS.** s2Member now offers site owners the option to enable/disable lazy loading of CSS/JS libraries provided by the s2Member software. For further details, please see: `Dashboard -› s2Member -› General Options -› CSS/JS Lazy Loading`.
288
- * (s2Member/s2Member Pro) **Bug Fix.** s2Member and s2Member Pro have both been updated to prevent spaces in a comma-delimited list of Custom Capabilities; e.g. `ccaps="music, videos"` should be `ccaps="music,videos"` please. Spaces in this list have never been allowed, but now there is better server-side validation to prevent this from happening; reducing the chance of error when a site owner configures a Button or Pro Form shortcode with s2Member.
289
  * (s2Member/s2Member Pro) **Other Minor Bug Fixes.** Please see: <https://github.com/WebSharks/s2Member/commits/000000-dev>
290
 
291
  = v131109 =
@@ -296,9 +309,9 @@
296
  * (s2Member/s2Member Pro) **Improvement (Speed)** Lazy load s2Member's CSS file at all times. Done, this release will speed your site up for first-time visitors.
297
  * (s2Member/s2Member Pro) **Improvement (Speed)** Load s2Member's JS library in the footer if at all possible (instead of the `<head>`). Done, this will improve the speed of your site for first-time visitors.
298
  * (s2Member/s2Member Pro) **Debug Notices** Resolved all of the most obvious PHP notices when running s2Member in debug mode. This improvement impacts developers only.
299
- * (s2Member/s2Member Pro) **Password Strength Meter** Removed dependence on `password-strength-meter` (a JavaScript library) from the WordPress core. This was causing some SSL issues for site owners. In the past it was necessary for s2Member to load an additional JS resource for registration/checkout and Pro Forms (`password-strength-meter`). Starting with this release, s2Member handles password strength meters all by itself, thereby avoiding the additional overhead; and also the issues associated with this core functionality over SSL pages. Fixed in this release.
300
  * (s2Member/s2Member Pro) **Mobile Devices** The s2Stream shortcode (for protected audio/video files) was updated to better support mobile device playback. See also: <http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#s2stream-mobile-devices>.
301
- * (s2Member Pro) **Checkout Options** Improving support for multiple Checkout Options. When a customer changes to a new Checkout Option by selecting an option from the drop-down menu in a Pro Form; this action will now result in a hash jump back to the location of the Pro Form on any given page; instead of to the top of the page, which could potentially result in a confusing experience on some sites (depending on the implementation). Aside: for developers, it's helpful to know that all s2Member Pro Forms now have a hashable ID `#s2p-form` that is cleaner than the longer (product-specific) IDs associated with Pro Forms; e.g. `s2member-pro-paypal-form...`, etc. If you need to hash a Pro Form, please use the more general `#s2p-form` on the end of a URL. This will take a customer directly to that Pro Form in the context of any given page.
302
 
303
  = v131026 =
304
 
@@ -308,7 +321,7 @@
308
  = v131025 =
309
 
310
  * (s2Member) **WordPress v3.7 Compatibility** s2Member updated to support WordPress v3.7. s2Member remains compatible with WP v3.3 (or higher).
311
- * (s2Member Pro) **New Feature: Simultaneous Login Monitoring** Available only w/ s2Member Pro. s2Member Pro has been updated to support configurations that limit the number of simultaneous logins a single username can receive. For further details, please see: `Dashboard -› s2Member -› Restriction Options -› Simultaneous Login Restrictions` (when s2Member Pro is installed).
312
  * (s2Member) **Post Restrictions (#3)** Adding support for `all-[post type]` in addition to the existing `all-[post type]s` (plural) currently supported by s2Member's Restriction Options for Posts. This makes it possible for a site owner to type only the Post Type after the keyword prefix `all-`; and excluding the plural `s` in cases when this is necessary.
313
  * (s2Member) **Documentation Update (#3)** Adding note in Download Options panel regarding `raw` shortcode tags around Shortcodes when using the inFocus theme. See also [this thread](http://mysitemyway.com/support/topic/infocus-adding-tags-into-plugin-content) for further details.
314
  * (s2Member) **Documentation Cleanup (#12)** General cleanup in several s2Member panels to improve inline documentation that comes w/ the software. Branding improvements, padding adjustments, and subtle textual changes.
@@ -317,11 +330,11 @@
317
  * (s2Member) **Quick Start Video (#10)** Adding Quick Start playlist to Quick Start section.
318
  * (s2Member) **UI (#6)** Reducing padding around section headers in s2Member option panels to reduce the amount of space these consume.
319
  * (s2Member Pro) **SSL** Forcing all automatic updates of s2Member Pro to occur over SSL for improved security.
320
- * (s2Member) **Login Welcome Page** Improving support for new feature (force HTTP redirection). See: `s2Member -› General Options -› Login Welcome Page` for details on how this feature works.
321
  * (s2Member) **Bug Fix: Registration/Profile Fields** Adding space between checkboxes and their labels.
322
  * (s2Member) **ClickBank IPN Filter** Adding a new filter to s2Member's ClickBank IPN handler for developers integrating s2Member in creative ways: `c_ws_plugin__s2member_pro_clickbank_notify_handles_completions`. Defaults to a TRUE value. Forcing this filter to a FALSE value will prevent s2Member from handling term completions via IPN communication; in cases where it's preferred that a site owner deal with this specific scenario manually.
323
  * (s2Member) **Conformity** Updating calls to `$wpdb->escape` changing to `esc_sql` to conform w/ WordPress standards.
324
- * (s2Member Pro) **Compatibility: Checkout Options** Improving theme support for Checkout Options created using Pro Forms by wrapping other Pro Form Shortcodes. Some site owners reported line break injections in the previous verison. Fixed in this release.
325
  * (s2Member) **See also: s2Member Repo** https://github.com/WebSharks/s2Member/commits/000000-dev
326
 
327
  = v130816 =
@@ -329,28 +342,28 @@
329
  * (s2Member Pro) **Compatibility, ClickBank (#467)** Improving support for ClickBank PitchPlus Upsell Flows. Please see [this thread](http://www.s2member.com/forums/topic/clickbank-buttons-not-working/#post-55725) for further details.
330
  * (s2Member/s2Member Pro) **User Search on Multisite Networks (#468)** User search functionality was partially broken for Child Blogs in a Multisite Network after some improvements were implemented in s2Member® v130731. The issue has now been corrected in this release for Multisite Networks. For further details, please see [this thread](http://www.s2member.com/forums/topic/user-search-no-longer-working/#post-55778).
331
  * (s2Member/s2Member Pro) **Z-Index in Menu Pages (#461)** Stacking order against a WordPress® installation running a Dashboard with a collapsed sidebar menu (left side) was causing some UI problems. Fixed in this release.
332
- * (s2Member/s2Member Pro) **SSL Compatibility (#437)** Adding a new option in the `s2Member® -› General Options -› Login Welcome Page` section. The default value for this new option is always `yes`. However, the default functionality can be turned off (if you prefer). This new option allows site owners to better integrate with a core WordPress® feature commonly referred to as `FORCE_SSL_LOGIN` and/or `FORCE_SSL_ADMIN`. This new feature can be used, or not. It is intended mainly for site owners running w/ `FORCE_SSL_LOGIN` and/or `FORCE_SSL_ADMIN`.
333
- * (s2Member/s2Member Pro) **Login/Registration Design Option (#437)** Adding a new option in the `s2Member® -› General Options -› Login/Registration Design` section. This new option (found at the bottom of `s2Member® -› General Options -› Login/Registration Design`) allows a site owner to show/hide the `Back To Home Page` link at the bottom of the default WordPress® Login/Registration system. This can be useful for site owners running w/ `FORCE_SSL_LOGIN` and/or `FORCE_SSL_ADMIN`; where they would prefer NOT to link customers back to the main site under a default HTTPS link; but rather, create their own link and gain better control over this area of their site.
334
- * (s2Member/s2Member Pro) **Videos (#467)** Updating internal documentation found in `Dashboard -› s2Member® -› Quick-Start`. Specifically, the video player here was integrated with an older version of the YouTube® API and was not working properly. Fixed in this release.
335
 
336
  = v130802 =
337
 
338
- * (s2Member Pro) **Compatibility, WordPress® v3.6** Updating s2Member® Pro Form templates and their underlying CSS. This update improves their appearance against the Twenty Thirteen theme that comes with WordPress® v3.6. Specifically, some of the Pro Form buttons were a little out of place in this new default theme. Fixed in this release.
339
- * (s2Member Pro) **Compatibility, Checkout Options (#443)** Revision 3. Updating this feature to support a wider variety of WordPress® configurations and content filters. This update also resolves an empty `desc=""` attribute error reported by some site owners. Feature description... It is now possible to build dropdown menus offering your customers a variety of options using a Single Pro Form. This is accomplished quite easily using Shortcodes. For full details and examples, please check this section of your Dashboard in the latest release. See: `s2Member® -› PayPal® Pro Forms -› Wrapping Multiple Shortcodes as "Checkout Options"`. NOTE: this works for PayPal® Pro Forms, and ALSO for Authorize.Net Pro Forms.
340
 
341
  = v130801 =
342
 
343
- * (s2Member Pro) **New Feature; Checkout Options (#403)** Revision 2. Updating documentation on this new feature to prevent conufusion for site owners. s2Member® Pro now supports "Checkout Options". It is now possible to build dropdown menus offering your customers a variety of options using a Single Pro Form. This is accomplished quite easily using Shortcodes. For full details and examples, please check this section of your Dashboard in the latest release. See: `s2Member® -› PayPal® Pro Forms -› Wrapping Multiple Shortcodes as "Checkout Options"`. NOTE: this works for PayPal® Pro Forms, and ALSO for Authorize.Net Pro Forms.
344
 
345
  = v130731 =
346
 
347
- * (s2Member Pro) **New Feature; Checkout Options (#403)** s2Member® Pro now supports "Checkout Options". It is now possible to build dropdown menus offering your customers a variety of options using a Single Pro Form. This is accomplished quite easily using Shortcodes. For full details and examples, please check this section of your Dashboard in the latest release. See: `s2Member® -› PayPal® Pro Forms -› Wrapping Multiple Shortcodes as "Checkout Options"`. NOTE: this works for PayPal® Pro Forms, and ALSO for Authorize.Net Pro Forms.
348
- * (s2Member Pro) **Free Checkout (#403)** It is now possible to offer a 100% free checkout experience using any of s2Member's Pro Form Shortcodes. In previous releases of s2Member® it was not possible to set the `ra=""` Attribute to a zero dollar amount. Now it is! This works for PayPal® Pro Forms, and also for Authorize.Net Pro Forms.
349
- * (s2Member Pro) **100% Off Coupons (#403)** It is now possible to offer a 100% off coupon. This works for PayPal® Pro Forms, and also for Authorize.Net Pro Forms. See: `s2Member® -› Pro Coupon Codes` for details and examples.
350
- * (s2Member Pro) **Expiration Date Dropdowns (#428)** This release improves all s2Member® Pro Form templates by adding dropdown menus for the customer's credit card expiration month/year instead of the simple text input field used in previous releases.
351
- * (s2Member/s2Member Pro) **MySQL Wait Timeout (#349)** s2Member now automatically increases the MySQL `wait_timeout` to `300` seconds during s2Member processing routines. Reason for increase: should any 3rd party service API result in unexpected connection timeouts (such as PayPal, Authorize.Net, Amazon, MailChimp, AWeber, etc); this may cause a delay that could potentially exceed the default `wait_timeout` of `30` seconds on the MySQL resource handle that is global to all of WordPress. Increasing `wait_timeout` before transaction processing will decrease the chance of failure after a timeout is exceeded. Among other things, this resolves an elusive bug where there are mysterious 404 errors after checkout under the right scenario (e.g. when an unexpected timeout occurs). This may also resolve problems associated w/ some mysterious reports where emails were not sent during s2Member's attempt to complete post-processing of a transaction (and/or where other portions of post-processing failed under rare circumstances).
352
- * (s2Member/s2Member Pro) **Alternative Views (#300)** This release gives s2Member® the ability to hide protected content in widgets that list protected WordPress® Pages. This is a new Alternative View in the Dashboard. See: `s2Member® -› Restriction Options -› Alternative Views` for further details please.
353
- * (s2Member/s2Member Pro) **Documentation Update (#350)** Subtle improvements to the built-in documentation pertaining to s2Member's Automatic List Transitioning feature in the Dashboard. See: `s2Member® -› API/List Servers -› Automatic Unsubscribes` for further details please.
354
  * (s2Member/s2Member Pro) **Bug Fix (#387)** In s2Member® Only mode, a recursive scan for the WordPress® `/wp-load.php` file was failing somtimes when/if a custom directory was configured for plugins. Fixed in this release. See [this thread](http://www.s2member.com/forums/topic/problem-with-wordpress-folder-search-code/) for further details.
355
  * (s2Member/s2Member Pro) **Bug Fix (#418)** Fixed incorrect pagination of user search results in the Dashboard.
356
  * (s2Member/s2Member Pro) **Bug Fix (#418)** Fixed slow query against user searches in the Dashboard.
@@ -358,20 +371,20 @@
358
 
359
  = v130617 =
360
 
361
- * (s2Member/s2Member Pro) **IP Restrictions (#148)** It is now possible to introduce a custom template file that controls the error message displayed when/if a user breaches security by exceeding your maximum unique IP addresses; as configured under `s2Member -› Restriction Options -› Unique IP Restrictions`. If you would like to use a custom template for this message, please copy the default template file from `/s2member/includes/templates/errors/ip-restrictions.php` and place this file into your active WordPress® theme directory (or into the `/wp-content/` directory if you prefer). s2Member will automatically find your custom template in one of these locations; and s2Member will use your custom template instead of the built-in default.
362
- * (s2Member Pro) **Bug Fix (#302)** Updating Authorize.Net Pro Form Generator to support max days of `365` instead of `7`.
363
  * (s2Member Pro) **Amazon S3 Secret Access Keys (#321)** Updating UI configuration panel to better explain what a Secret Access Key is; and adding a note about Secret Access Keys for Amazon S3 integration. Amazon® is deprecating Secret Access Keys, but they ARE still required for digitally signed URLs. This update changes nothing in s2Member's functionality. It simply adds some additional detail to a configuration field that will assist site owners integrating s2Member w/ Amazon S3 for the first time.
364
  * (s2Member/s2Member Pro) **Translations (#317)** Updating `/s2member/includes/translations/translations.txt` (README file). Adding blurp about fuzzy translation entries in existing PO files that translate s2Member. This contains some additional tips on how to update existing PO files upon each release of s2Member and/or s2Member Pro.
365
  * (s2Member/s2Member Pro) **Bug Fix (#321)** Fixing bug in `/s2member/includes/templates/cfg-files/s2-cross-xml.php` for S3 Buckets (resulting in `crossdomain.xml`). This file contained a parse error and was causing some problems for site owners integrating Adobe Flash content served via Amazon S3. Fixed in this release.
366
- * (s2Member Pro) **PayPal Pro Forms (#315)** Adding note in the Dashboard here (`s2Member -› PayPal Pro Forms -› Shortcode Attributes Explained`). In the list of Shortcode Attributes we are adding a note regarding max character length for the `desc=""` Attribute in a PayPal Pro Form Shortcode. This can be as long as you like. However, all descriptions passed through PayPal® APIs are truncated automatically to 60 characters max (e.g. the maximum allowed length for PayPal® descriptions is 60 characters). Nothing new here, but we thought it would be a good idea to clarify this behavior in the documentation. Updated in this release.
367
- * (s2Member Pro) **PayPal Pro Forms (#312)** Preventing the use of multiple Pro Forms in the same Post/Page. This has never been possible, it is known to break the functionality of s2Member Pro Forms. Please limit Pro Form Shortcodes to ONE for each Post/Page; and do NOT attempt to use more than one Pro Form Shortcode on the same Post/Page (at the same time). In this release we have added a friendly JavaScript alert/warning for site owners that attempt this, so that problems and confusion can be avoided in this unlikely scenario.
368
 
369
  = v130513 =
370
 
371
  * (s2Member/s2Member Pro) **s2Stream Shortcode Bug Fix (#256)** Fixing a bug first introduced in the previous release where we added support for `player_aspectratio`. This quick update corrects the PHP parse error at line #154 of `sc-files-in.inc.php`. It also corrects the behavior of the `player_height=""` and `player_aspectratio=""` Shortcode Attributes for the `s2Stream` Shortcode. Many thanks to everyone that reported this bug.
372
  * (s2Member Pro) **Codestyling Localization** Removing symlink creator for Codestyleing Localization compatibility. There have been some reports of problems during WordPress® automatic upgrades (when/if the symlink exists). Until we can find a way to avoid this, we're disabling the automatic symlink generator. If you're running the Codestyling Localization plugin together with s2Member Pro, you will need to create the symlink yourself if you want to make s2Member fully compatible. Please create a symlink here: `/wp-content/plugins/s2member/s2member-pro` that points to the s2Member Pro directory: `/wp-content/plugins/s2member-pro`. See notes in previous changelog for further details on this.
373
 
374
- **IMPORTANT NOTE:** If you upgraded previously to v130510 (and you ran the Codestyling Localization plugin together with s2Member® v130510 at any time); please delete this symlink via FTP: `/wp-content/plugins/s2member/s2member-pro`. Please do this BEFORE attempting an automatic upgrade via WordPress®.
375
 
376
  If you missed this note and you've already attempted an automatic upgrade, you will have trouble. Here's how to correct the problem.
377
 
@@ -382,9 +395,9 @@
382
 
383
  = v130510 =
384
 
385
- * (s2Member Pro) **Authorize.Net UK (and Other Currencies) (#104)** Adding support for Authorize.Net UK and other currencies too. s2Member Pro now officially supports Authorize.Net UK Edition. It is now possible to change your Authorize.Net Pro Form Shortcode Attribute `cc="USD"` to one of these values: `cc="USD"`, or `cc="CAD"` or `cc="EUR"` or `cc="GBP"`. For further details, please see: `Dashboard -› Authorize.Net Pro Forms -› Shortcode Attributes (Explained)`.
386
- * (s2Member Pro) **ClickBank Skins (#227)** Adding support for the `cbskin=""` Shortcode Attribute. For further details, please see: `Dashboard -› ClickBank Buttons -› Shortcode Attributes (Explained)`.
387
- * (s2Member Pro) **ClickBank PitchPlus Upsell Flows (#227)** Adding support for ClickBank PitchPlus Upsell Flows via new Shortcode Attributes: `cbfid=""`, `cbur=""`, `cbf="auto"`. s2Member Pro now officially supports ClickBank PitchPlus Upsell Flows. We support PitchPlus Basic and PitchPlus Advanced too. For further details, please see: `Dashboard -› ClickBank Buttons -› Shortcode Attributes (Explained)`.
388
  * (s2Member/s2Member Pro) **Codestyling Localization** Adding automatic symlink creator for improved compatibility with the CodeStyling Localization plugin. A symlink is created automatically whenever the CodeStyling Localization plugin is installed, and s2Member® Pro is installed as well. The symlink allows the CodeStyling Localization plugin to scan files from the s2Member® Pro directory too; instead of only scanning the s2Member® Framework directory. s2Member and s2Member Pro are now both compatible with the Codestyling Localization plugin (optional).
389
  * (s2Member/s2Member Pro) **Custom Templates w/ s2Stream Shortcode** Adding support for custom templates to be used in conjunction with the `s2Stream` Shortcode. It is now possible to take the default player templates from `/s2member/includes/templates/players/` and put these files inside your own WordPress® theme directory (or inside the `/wp-content/` directory). s2Member will automatically find your template files in these locations. Your custom template files will then be used instead of the built-in defaults.
390
  * (s2Member/s2Member Pro) **Bug Fix (#59)** Resending a password to a User from the Dashboard (while changing the User's email address at the same time); resulted in the email being sent to the previous email address instead of the new one. Fixed in this release.
@@ -408,7 +421,7 @@
408
  For further details, please see [this thread](http://www.s2member.com/forums/topic/paypal-expired-security-token/page/2/) (or monitor the [s2Member KB](http://www.s2member.com/kb/) for new articles on this subject).
409
  * (s2Member Pro) **Payflow API Docs** Updating `s2m-pro-extras.zip` to include the latest versions of the PayPal Pro (Payflow Edition) APIs. s2Member Pro customers can download this optional ZIP file from their My Account page at s2Member.com. These are extras only, they are NOT part of the s2Member application.
410
  * (s2Member Pro) **Google Checkout Bug Fix (#214)** Updating s2Member's Google Checkout integration to properly support the `rrt` Shortcode Attribute. Fixed in this release. For further details please see [this thread](http://www.s2member.com/forums/topic/google-recurring-problem/#post-48218).
411
- * (s2Member Pro) **Username Validation (#246)** Now forcing user input (during Pro Form registration) to lowercase on Multisite Networks to prevent unnecessary validation errors during checkout (saving a customer time). Also, s2Member now validates a customer's Username before it is passed through `sanitize_user()` (a core WordPress® function). This prevents confusion for a customer where certain characters were stripped out automatically, causing them problems when attempting to log in for the first time (e.g. the customer thinks their Username is `john~doe`; when it is actually `johndoe` because WordPress (when running a Multisite Network) removes anything that is NOT `a-z0-9 _.-@` (and s2Member removes whitespace as well).
412
 
413
  = v130406 =
414
 
@@ -437,21 +450,21 @@
437
  = v130220 =
438
 
439
  * (s2Member Pro) **Feature Enhancement/User Exportation (#33)** Adding support for UTF-8 BOM in CSV User export files. Now a configurable option during User/Member Exportation.
440
- * (s2Member/s2Member Pro) **Feature Enhancement/Emails (#21)** Adding additional Replacement Codes for New User Email Notifications (for both the User/Member Notification and also for the Administrator Notification). The following Replacement Codes are now possible: `%%role%%`, `%%label%%`, `%%level%%`, `%%ccaps%%`. Also adding four new Filters for developers. These include: `ws_plugin__s2member_welcome_email_sbj`, `ws_plugin__s2member_welcome_email_msg`, `ws_plugin__s2member_admin_new_user_email_sbj`, `ws_plugin__s2member_admin_new_user_email_msg`. See `Dashboard -› General Options -› Email Configuration` for further details.
441
- * (s2Member/s2Member Pro) **Feature Enhancement/Emails (#30)** Adding support for PHP tags in the following emails: New User Notification, Administrative New User Notification, Signup Confirmation Email, Specific Post/Page Confirmation Email. See the relevant sections in your Dashboard for further details. Such as: `s2Member® -› General Options -› Email Configuration` and `s2Member® -› PayPal® Options -› Signup Confirmation Email`.
442
- * (s2Member/s2Member Pro) **Feature Enhancement/Shortcodes (#23)** Adding support for the `lang=""` Attribute in PayPal Buttons, PayPal Pro Forms, and in Google Checkout Buttons. This is a bit different from the existing `lc=""` value. The `lc=""` value controls the interface at PayPal, while the `lang=""` value controls the language of the Standard and/or Express Checkout Button itself (with respect to s2Member®). For further details, please see: `Dashboard -› PayPal Buttons (or Pro Forms) -› Shortcode Attributes (Explained)`.
443
  * (s2Member/s2Member Pro) **Bug Fix** Fixing bug in User Access Package. Now checking if `$cap_enabled` also is `TRUE`; just in case another plugin or hack file attempts to disable Custom Capabilities without removing them. Not likely, but we can support this easily with a quick update in this release. Note... this has no impact on s2Member's existing functionality. Custom Capabilities continue to work just as they always have.
444
- * (s2Member/s2Member Pro) **Feature Enhancement/Logging** Adding new logger. Logs to file `reg-handler.log`. Includes all User/Member registrations handled by s2Member® (either directly or indirectly). Only if logging is enabled. For further details, please check your Dashboard here: `s2Member® -› Log Files (Debug)`.
445
- * (s2Member/s2Member Pro) **Feature Enhancement/EOTs (#29)** Adding UI option for EOT Grace Time. For further details, please see: `Dashboard -› PayPal Options -› Automatic EOT Behavior`. Also adding a new Filter for developers: `ws_plugin__s2member_eot_grace_time`.
446
- * (s2Member/s2Member Pro) **Feature Enhancement/EOTs** Adding UI option for EOT Custom Capability Removal. For further details, please see: `Dashboard -› PayPal Options -› Automatic EOT Behavior`. Also adding a new Filter for developers: `ws_plugin__s2member_remove_ccaps_during_eot_events`.
447
- * (s2Member/s2Member Pro) **Feature Enhancement/s2Stream Shortcode (#32)** Adding additional support for JW Player™ Captions, Titles, Descriptions, and Media IDs (i.e. `player_title=""`, `player_description=""`, `player_mediaid=""`, `player_captions=""`). Please check the Shortcode Attributes tab in [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes) for further details.
448
 
449
  = v130214 =
450
 
451
  * **(Maintenance Release) Upgrade immediately.**
452
  * (s2Member/s2Member Pro) **Log Viewer (#16)** Additional log file descriptions have been added to the Dashboard, along with some other UI enhancements in this section.
453
  * (s2Member/s2Member Pro) **Bug Fix (#18)** Usernames consisting of all numeric values were not always being redirected to the Login Welcome Page upon logging in, even when s2Member® was configured to do so. Fixed in this release. Discussed in [this thread](http://www.s2member.com/forums/topic/subscribers-not-taken-to-welcome-page/page/2/#post-41663).
454
- * (s2Member Pro) **Coupon Codes (#19)** Adding new Replacement Codes: `%%full_coupon_code%%`, `%%coupon_code%%` and `%%coupon_affiliate_id%%`. These are now available in all API Tracking Codes, in all Custom Return URLs for Pro Forms, and in most API Notifications.
455
  * (s2Member Pro) **Coupon Codes (#19)** Deprecating the `%%affiliate_id%%` Replacement Code for tracking Affiliate Coupon Codes in favor of `%%coupon_affiliate_id%%`.
456
  * (s2Member/s2Member Pro) **Last Login Time** Improving readability of Last Login Time in list of Users/Members.
457
  * (s2Member/s2Member Pro) **Compatibility** Improving support for WordPress® v3.6-alpha with respect to `tabindex` values on `/wp-login.php`.
@@ -460,39 +473,39 @@
460
  = v130213 =
461
 
462
  * **(Maintenance Release) Upgrade immediately.**
463
- * (s2Member Pro) **Compatibility (#13)** PayPal® Payments Pro, PayPal® Payments Pro (Payflow Edition), and Authorize.Net. s2Member® Pro now supports a recurring interval of Semi-Yearly (e.g. charges occur every six months). This has always been possible by manipulating Shortcode Attribues, but it's now officially supported by Pro Form Generators in your Dashboard which come with s2Member® Pro.
464
  * (s2Member Pro) **Compatibility (#13)** ClickBank® Recurring Products. ClickBank® has started allowing a Weekly recurring interval and stopped allowing Yearly. s2Member® has been updated in this release to support a Weekly recurring interval with ClickBank®; and to remove the Yearly option in the ClickBank® Button Generator.
465
  * (s2Member Pro) **Compatibility (#13)** PayPal® Payments Pro (Payflow Edition). PayPal® Payments Pro (Payflow Edition) has started allowing a Daily recurring interval. s2Member® has been updated in this release to support a Daily recurring interval with PayPal® Payments Pro (Payflow Edition). Daily recurring intervals remain possible with PayPal® Pro accounts that do not include the additional Payflow API. This change simply adds official support for Daily recurring billing with PayPal® Payments Pro (Payflow Edition).
466
  * (s2Member) **Debugging Assistance** Updating s2Member's PayPal® PDT/Auto-Return handler to better handle scenarios where a site owner is missing a PayPal® PDT Identity Token in their s2Member® configuration, or has incorrectly set the `custom=""` Shortcode Attribute in Payment Buttons generated with s2Member®. Administrative notices are now displayed in the Dashboard when/if this occurs and s2Member® can catch the issue during post-processing of a transaction.
467
  * (s2Member/s2Member Pro) **General Code Cleanup** Removing all `/**/` markers in the s2Member® codebase. These were used in conjunction with PolyStyle® code formatting tools to preserve line breaks in the code. The WebSharks™ development team no longer uses PolyStyle®, making these obsolete now. Removed in this release to improve readability for developers.
468
  * (s2Member/s2Member Pro) **General Code Cleanup** Removing all unnecessary uses of PHP's `eval()` function in s2Member's codebase. These were used to keep repetitive code all in a single line; part of a standard the WebSharks™ development team is now moving away from. Removed in this release to improve readability for developers; and to prevent unnecessary confusion.
469
  * (s2Member/s2Member Pro) **Auto-EOT System** Updated s2Member's Auto-EOT System. s2Member® now leaves an additional note behind after a demotion, which references the Paid Subscr. Gateway and Paid Subscr. ID values before the demotion occurred. This way there is a better reference left behind after an automatic demotion occurs.
470
- * (s2Member/s2Member Pro) **Searching Users** Updating search function in list of Users (i.e. `Dashboard -› Users -› [Search Box]`) to include the Administrative Notes field when searching for Users. This allows references to old Paid Subscr. IDs in the Administrative Notes field to be considered when searching Users/Members.
471
  * (s2Member/s2Member Pro) **Last Login Time** Adding new User Option value (tracked by s2Member®). This option value tracks the last time each User/Member logged into your site. Ex: `get_user_option("s2member_last_login_time")`.
472
  * (s2Member/s2Member Pro) **Last Login Time** Adding new User data column to list of Users in the Dashboard: `Last Login Time`.
473
  * (s2Member/s2Member Pro) **Last Login Time** Adding new API Function: [`s2member_last_login_time()`](http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/).
474
  * (s2Member/s2Member Pro) **ezPHP** Updated all internal documentation references that pointed to Exec-PHP or the PHP Execution plugin as recommendations for developers that wish to integrate PHP tags into Posts/Pages/Widgets. These old references now point to the [ezPHP](http://www.s2member.com/kb/ezphp-plugin/) plugin by s2Member® Lead Developer: Jason Caldwell. s2Member® remains compatible with other PHP plugins, but we recommend [ezPHP](http://www.s2member.com/kb/ezphp-plugin/) for the best compatibility with both s2Member® and WordPress® itself.
475
- * (s2Member/s2Member Pro) **Simple Shortcode Conditionals** Adding a [Simple Shortcode Conditionals](http://www.s2member.com/kb/simple-shortcode-conditionals/) section to `s2Member® -› Restriction Options` in the Dashboard. This way more site owners will be aware of this feature from the start.
476
- * (s2Member/s2Member Pro) **Login/Registration Design** Login/Registration Design with s2Member® is now optional (e.g. this feature can be disabled now if you prefer). See: `Dashboard -› s2Member® -› General Options -› Login/Registration Design`. This feature is enabled by default on all s2Member® installations.
477
  * (s2Member/s2Member Pro) **Inline Documentation** Adding more links to KB articles throughout the Dashboard area.
478
- * (s2Member/s2Member Pro) **Inline Documentation** Updating all spaced parenthesis like `( something... )` to remove the grammatical errors by removing the extra spaces inside these brackets.
479
- * (s2Member/s2Member Pro) **Inline Documentation** Removing all references to PriMoThemes and/or primothemes.com within the application itself. PriMoThemes is now s2Member® (as of Jan 2012 it's been awhile; so time to remove these obviously).
480
  * (s2Member/s2Member Pro) **Inline Documentation** Adding link to "more updates..." in the Dashboard, pointing to the s2Member® KB. Increasing number of recent KB udpates from 3 up to 5. These are visible from any s2Member® page in the Dashboard (top of the right-hand column).
481
- * (s2Member/s2Member Pro) **Inline Documentation** Adding [s2Member® Pro](http://www.s2member.com/pro/) (a recommended upgrade) to the Quick-Start Guide for s2Member® — in the Dashboard.
482
- * (s2Member/s2Member Pro) **Inline Documentation** Adding Troubleshooting section to the Quick-Start Guide for s2Member® — in the Dashboard.
483
- * (s2Member/s2Member Pro) **Inline Documentation** Adding Perfect Theme section to the Quick-Start Guide for s2Member® — in the Dashboard.
484
- * (s2Member/s2Member Pro) **Inline Documentation** Adding video tutorial to the `Dashboard -› s2Member® -› API / Scripting -› Custom Capabilities` section.
485
- * (s2Member/s2Member Pro) **Logging Functionality** Adding an s2Member® Log Viewer to the Dashboard for all site owners; and also for s2Member® Support Reps to use when running diagnostics. See: `Dashboard -› s2Member® -› Log Files (Debug)` for further details.
486
- * (s2Member/s2Member Pro) **Logging Functionality** Logging routines are now enabled by default on all new installations of s2Member®. Existing installations of s2Member® are advised to enable logging, by visiting this section of your Dashboard. See: `s2Member® -› PayPal® Options (or Authorize.Net, ClickBank, etc) -› Account Details -› Logging`.
487
- * (s2Member/s2Member Pro) **Logging Functionality** Additional logging routines that will track all s2Member® HTTP communication within WordPress® is now enabled by default. This new log file will be located inside `/wp-content/plugins/s2member-logs`. It is named: `s2-http-api-debug.log`. See: `Dashboard -› s2Member® -› Log Files (Debug)` for further details.
488
- * (s2Member/s2Member Pro) **Logging Functionality** Additional logging routines that will track *all* HTTP communication within WordPress® are now possible (these are quite extensive). See: `Dashboard -› s2Member® -› Log Files (Debug) -› Logging Configuration` for further details. This more extensive logging is disabled by default; it must be enabled by a site owner. For debugging only this should NEVER be enabled on a live site.
489
  * (s2Member/s2Member Pro) **Logging Functionality** Adding date/time to all log entries maintained by s2Member®.
490
- * (s2Member/s2Member Pro) **GZIP Conflicts** Adding additional lines of defense against GZIP conflicts during file downloads, with calls to `@apache_setenv("no-gzip", "1")` in other areas not just during public file downloads (e.g. also during User/Member exporations, log file downloads, etc).
491
  * (s2Member/s2Member Pro) **GZIP Conflicts** Adding an additional line of defense against GZIP conflicts during file downloads, with this line now appearing in the `.htaccess` file snippet added by the s2Member® software application: `RewriteCond %{QUERY_STRING} (^|\?|&)no-gzip\=1`.
492
  * (s2Member/s2Member Pro) **GZIP Conflicts** Adding an additional line of defense against GZIP conflicts during User/Member exporations, log file downloads, and other downloads that come straight from the Dashboard area to site owners via web browsers. s2Member® now sends `Content-Encoding: none` to prevent Apache's `mod_deflate` from interfering with s2Member® under these special scenarios. A `Content-Encoding: none` header value is technically invalid, but it's known to prevent issues with `mod_deflate`. Since a `Content-Encoding: none` header value is technically invalid, s2Member® does NOT implement this during public file downloads; where we need to provide wider support for a long list of devices that may choke on this incorrect value. This is only implemented for site owners in the administrative areas of WordPress; and only for file downloads related to CSV export files and logs.
493
  * (s2Member/s2Member Pro) **Bug Fix** Fixed incorrect `preg_split` limit against `$paypal['item_number']` in IPN handler for `subscr_payment` and `subscr_cancel` transaction types. Doesn't appear to have affected anything negatively, but it was wrong none the less. Fixed in this release.
494
  * (s2Member/s2Member Pro) **Bug Fix** Fixed incorrect handling of a single opt-in checkbox on BuddyPress registration forms, which was not being wrapped with s2Member's BuddyPress container divs at all times. A symptom of this bug was to see a checkbox on your BuddyPress registration that was out of alignment or out of position. Fixed in this release.
495
- * (s2Member/s2Member Pro) **Compatibility** Updated all of s2Member's IPN handlers to accept `$_REQUEST` data for Proxy-related variables like `s2member_paypal_proxy_return_url`. This allows s2Member® itself to use `$_POST` variables for Proxy-related variables; and it further reduces the likelihood of 403 Forbidden errors caused by [paranoid Mod Security configurations](http://www.s2member.com/kb/mod-security-random-503-403-errors/). One issue this should help to correct, is a mysterious case where a `success=""` Shortcode Attribute is not working as you might expect. This can be caused by [paranoid Mod Security configurations](http://www.s2member.com/kb/mod-security-random-503-403-errors/) at places like HostGator®, because a URL is passing through a query string. This release will help to prevent this from becoming a problem, because `success=""` URLs will be passed through `$_POST` variables now in all Pro Form integrations.
496
 
497
  = v130207 =
498
 
@@ -506,13 +519,13 @@
506
  = v130203 =
507
 
508
  * **(New Release) Please read this changelog for important details.**
509
- * (s2Member Pro) **Remote Ops API (`create_user`)** s2Member® Pro's Remote Operations API, for the `create_user` Operation has been updated to support a new specification: `modify_if_login_exists`. For further details, please check your s2Member® Pro Dashboard here: `s2Member® -› API / Scripting -› Remote Operations API`.
510
- * (s2Member Pro) **Remote Ops API (`modify_user`,`delete_user`)** s2Member® Pro's Remote Operations API has been updated to support two additional Operations: `update_user` and `delete_user`. For further details on these new Operations, please check your s2Member® Pro Dashboard here: `s2Member® -› API / Scripting -› Remote Operations API`.
511
  * (s2Member Pro) **Remote Ops API (`init` hook priority)** s2Member® Pro's Remote Operations API has been updated to prevent conflicts when running in concert with BuddyPress v1.6.4+. Hook priority now running at default value of `11`, right after BuddyPress v1.6.4 at hook priority `10`.
512
- * (s2Member/s2Member Pro) **s2Stream Shortcode (#88)** s2Member® now supports JW Player® license keys (for the professional edition) using Shortcode Attribute `player_key=""` (or they can be specified sitewide via JavaScript provided by Longtail Video optional). See [this discussion](http://www.s2member.com/forums/topic/jwplayer-shortcode-for-poster-not-working/#post-40435). See also: [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes).
513
  * (s2Member/s2Member Pro) **s2Stream Shortcode (#88)** Bug fix. The s2Stream Shortcode was not working properly (with respect to a specific Shortcode Attribute: `player_image=""`). Fixed in this release. See [this discussion](http://www.s2member.com/forums/topic/jwplayer-shortcode-for-poster-not-working/#post-40128). See also: [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes).
514
- * (s2Member Pro) **User Exportation (#89)** s2Member® Pro's User Exportation now includes separate data columns for each Custom Registration/Profile Field that you've configured with s2Member®. Also, s2Member® Pro will now include ALL Custom Registration/Profile Fields (even if there is no value associated with certain Fields, for specific Users/Members e.g. empty column values will now be included by s2Member® Pro). This provides a more consistent/readable CSV export file; a major improvement. Discussed in [this KB article](http://www.s2member.com/kb/importing-updating-users/#custom-registration-profile-fields).
515
- * (s2Member Pro) **User Importation (#89)** s2Member® Pro's User/Member Import format changed in this release (with respect to Custom Registration/Profile Fields only). If you are importing Custom Registration/Profile Fields, please review [this KB article](http://www.s2member.com/kb/importing-updating-users/#custom-registration-profile-fields) before you import new Users/Members or mass update any existing Users/Members. ALSO NOTE: User/Member CSV Export Files generated by previous versions of s2Member® Pro (if they contained any Custom Registration/Profile Fields) will NOT be compatible with this latest release (e.g. you should NOT attempt to re-import those old files in an effort to mass update existing Users/Members). Please generate a new User/Member CSV Export File in the latest release of s2Member® Pro before attempting to edit and/or mass update existing Users/Members with applications like MS Excel or OpenOffice.
516
 
517
  = v130123 =
518
 
@@ -520,16 +533,16 @@
520
  * (s2Member/s2Member Pro) **s2Stream Shortcode (#78)** s2Member® now supports JW Player® license keys using Shortcode Attribute `player_key=""`. See [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/) please. Also discussed in [this thread](http://www.s2member.com/forums/topic/new-jw-player-6-s2-video-audio-shortcodes/#post-38768).
521
  * (s2Member/s2Member Pro) **s2Stream Shortcode (#79)** s2Member® now supports JW Player® [Advanced Option Blocks](http://www.longtailvideo.com/support/jw-player/28839/embedding-the-player) using Shortcode Attribute `player_option_blocks=""`. Example: `player_option_blocks="sharing:{}"`. See [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/) please. Also discussed in [this thread](http://www.s2member.com/forums/topic/new-jw-player-6-s2-video-audio-shortcodes/#post-38768).
522
  * (s2Member Pro) **User Exportation (#80)** s2Member® Pro User Exportation now occurs with MySQL `ORDER BY ID`, instead of no `ORDER BY` at all. This helps to prevent confusion and buggy behavior. Discussed in [this thread](http://www.s2member.com/forums/topic/user-export-not-working-properly/#post-39123).
523
- * (s2Member Pro) **User Exportation (#81)** s2Member Pro's User Exportation now supports the exporation of up to `1000` User/Member table rows at once. Of course it remains possible to export ALL of your Users/Members with s2Member® Pro. All we've done here is bump the default limit from `250` up to `1000` at a time. In addition, there is a new Filter making it possible to extend this limit further on servers that can handle it. Use Filter: `ws_plugin__s2member_pro_export_users_limit` if you would like to export more Users all at once. See also: `Dashboard -› s2Member® Pro -› User/Member Exportation`.
524
  * (s2Member/s2Member Pro) **KB Articles** Inline documentation updated in some areas, with a few links pointing to helpful/related KB articles.
525
 
526
  = v130121 =
527
 
528
  * **(Maintenance Release) Upgrade immediately.**
529
- * **New Feature** s2Member® now comes with a new Shortcode `[s2Stream file_download="video.mp4" player="jwplayer-v6" ... /]`, making it MUCH easier for site owners to implemement RTMP streams of audio/video content. For further details, please check your Dashboard under: `s2Member® -› Download Options -› JW Player® v6 and RTMP Protocol Examples`. See also: `s2Member® -› Download Options -› Shortcode Attributes (Explained)`.
530
  * **Compatibility (#75)** Updated s2Member's local file storage engine (for File Downloads via s2Member®), to support special characters in file names. Discussed in [this thread](http://www.s2member.com/forums/topic/problem-with-quotes-in-filename-downloads/#post-38395).
531
  * **Bug Fix (#71)** A bug first introduced in the previous release of v130116, where we added support for byte-range requests to s2Member's File Download functionality, was causing multiple byte-range requests (processed by s2Member) to count against each User/Member as multiple File Downloads. Fixed in this release.
532
- * **Compatibility** Updated s2Member's integration with Amazon® S3 to extend the default 30 second connection timeout (which was too conservative for many integrations) up to 24 hours by default, making it match the same as s2Member's Amazon® CloudFront connection timeout. For further details, please check your Dashboard under: `s2Member® -› Download Options -› Amazon® S3/CDN Storage -› Dev Note w/Technical Details`. It is possible to modify this connection timeout through a Filter discussed there.
533
 
534
  = v130116 =
535
 
1
+ = v150225 =
2
+
3
+ - (s2Member Pro) **Accept Bitcoin via Stripe!** This release of s2Member Pro comes integrated with the latest version of the Stripe API, where it is now possible to accept Bitcoin right along with most major credit cards—made possible by [Stripe's latest update to support Bitcoin](https://stripe.com/bitcoin). It's as easy as flipping a switch :-) Please see: `Dashboard ⥱ s2Member Pro ⥱ Stripe Options ⥱ Account Details ⥱ Accept Bitcoin`. Referencing [this GitHub issue](https://github.com/websharks/s2member/issues/482); i.e., the original feature request.
4
+ - (s2Member Pro) **Stripe API Upgrade:** This release of s2Member Pro updates the Stripe SDK and Stripe API to the latest version (Stripe API version: `2015-02-18`). In addition, this release forces a specific version of the Stripe API in all communication between Stripe and s2Member; thereby avoiding a scenario where the Stripe API could be updated again in the future, in ways that might prevent s2Member Pro from operating as intended. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/484) if you'd like technical details. Props to @pauloz1890 for reporting this.
5
+ - (s2Member/s2Member Pro) **Security Badge Sizes:** This release of s2Member corrects an issue with the `[s2Member-Security-Badge v="1" /]` shortcode. If you set `v="2"` or `v="3"`, the dimensions were miscalculated. Props to @Mizagorn See [this GitHub issue](https://github.com/websharks/s2member/pull/466) if you'd like additional details.
6
+ - (s2Member Pro) **Bug Fix:** Opt-in checkbox state (and some custom fields) were losing state when switching from one type of Pro Form to another—whenever Pro Form Checkout Options were in use. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/468) if you'd like additional details. Props to @zenzoidman for finding this!
7
+ - (s2Member) **Bug Fix:** Alt. View Restrictions stopped working on navigation menu items in the previous release of s2Member v150203 due to a default argument value being misinterpreted by a sub-routine. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/475) if you'd like further details.
8
+ - (s2Member/s2Member Pro) **Bug Fix:** Some site owners reported "paying" customers being left with a Membership Level of `0` at seemingly random times that may have occurred only once in every 300+ transactions. The issue was related to a regular expression being performed against encrypted binary data with an ungreedy `?` in the regex pattern. Certain characters in the binary output would be lost when specific character sequences were encountered; resulting in a random failure to decrypt cookies set by s2Member. In short, the underlying cause was identified and corrected in this release. Thanks to all who reported this. Our appreciation goes out to everyone who helped to test for this elusive bug. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/397) if you'd like additional technical details.
9
+ - (s2Member/s2Member Pro) **UI Enhancements:** This release includes an enhanced UI, along with many subtle improvements to the inline documentation/instructions provided within the WordPress Dashboard.
10
+ - (s2Member Pro) **Retiring Google Wallet:** Google [announced that they are retiring Google Wallet for Digital Goods](https://support.google.com/wallet/business/answer/6107573). s2Member Pro continues to support Google Wallet, but this release updates the "Other Gateways" section in the Dashboard to make it clear that Google Wallet will not be supported in future versions of s2Member Pro. In fact, Google Wallet for Digital Goods will [close March 2nd, 2015](https://support.google.com/wallet/business/answer/6107573).
11
+ - (s2Member/s2Member) **bbPress Bug Fix:** This release resolves a security issue when running a Multisite Network with bbPress + s2Member. Level 0 access was being granted by the bbPress plugin across all sites in a network. That behavior is fine for bbPress, but is unexpected when s2Member is running in a Network environment. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/465) if you'd like additional details. **IMPORTANT TIP:** If you experienced this issue, please read through [these important comments](https://github.com/websharks/s2member/issues/465#issuecomment-76039842) about bbPress Participants needing to be removed from child blogs in order to fully rid yourself of this problem; i.e., once you complete the update of s2Member, you should [also read this please](https://github.com/websharks/s2member/issues/465#issuecomment-76039842).
12
+ - (s2Member/s2Member Pro) **404 / Alt. Views Bug Fix:** This release of s2Member corrects a rare issue where the Membership Options Page (or other pages) can produce random 404 errors whenever s2Member's Alt. View Restrictions are enabled, and there is another plugin installed which runs a DB query using the `WP_Query` class _before_ the Main WP Query has been run. Resolved through the use of `->is_main_query()` instead of tracking it statically via `$initial_query`. See also [this GitHub issue](https://github.com/websharks/s2member/issues/481) if you'd like additional technical details.
13
+
14
  = v150203 =
15
 
16
+ - (s2Member Pro) **Gift/Redemption Codes:** This release adds a powerful new shortcode: `[s2Member-Gift-Codes /]`. This makes it easy to generate and sell access to gift codes (i.e., gift certificates) and/or to a list of redemption codes. For instance, where a single team leader might like to purchase multiple accounts they can distribute to others on a team, or in a group. Video demo here: https://www.youtube.com/watch?v=T3N_vygowbM&feature=youtu.be ~ See also: [this GitHub issue](https://github.com/websharks/s2member/issues/386) for additional technical details.
17
+ - (s2Member Pro) **User-Specific Coupon Codes:** This release of s2Member makes it possible to configure Pro-Form Coupon Codes that are connected (i.e., only valid) when entered by specific Users/Members who are logged into the site. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/403) for additional technical details.
18
  - (s2Member Pro) **Coupon Code Max Uses:** This release of s2Member Pro adds the ability to set a maximum number of times that a Coupon Code can be used. This makes it easy to create Coupon Codes that are designed to be used only one time, for instance; or for X number of times. After a Coupon Code is used X number of times, it will expire automatically. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
19
  - (s2Member Pro) **Coupon Code Usage Tracking:** This release of s2Member Pro adds the ability to track the number of times that each of your Coupon Codes have been used. It is also possible to alter the number of uses, and/or set a maximum number of uses. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
20
  - (s2Member Pro) **Coupon Code Active/Expires Dates:** This release of s2Member Pro makes it possible to establish both a start and end time for each of your Pro Coupon Codes. In previous versions of s2Member, it was only possible to set an expiration date. You can now create Coupon Codes that will become active at some point in the future automatically. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
21
  - (s2Member Pro) **Coupon Code UI Enhancements:** This release of s2Member Pro comes with an updated UI that makes it easier to manage your Pro Coupon Codes. See: `Dashboard ⥱ s2Member ⥱ Pro Coupon Codes`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/285) for technical details.
22
+ - (s2Member Pro) **Store Coupon Codes for Each User:** s2Member Pro now stores a list of all coupon codes that a customer has used on your site. See: `Dashboard ⥱ Users ⥱ Choose User [Edit]`. Scrolling down to the set of s2-related fields will reveal a new list of coupon codes. This list will be filled for new customers only; i.e., s2Member does not have this data for past purchases; only for new customers that you acquire after updating to the latest release. See also [this GitHub issue](https://github.com/websharks/s2member/issues/462) if you'd additional details.
23
  - (s2Member/s2Member Pro) **EOT Custom Value:** In this release of s2Member, the `get_user_option('s2member_custom')` value is preserved after an EOT has taken place, making it possible for site owners to continue to read this value (along with any custom pipe-delimited values they have injected there), even after an EOT has taken place. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/449).
24
  - (s2Member/s2Member Pro) **JW Player Broken Links:** This release corrects some broken links referenced by the inline documentation for s2Member in the WordPress Dashboard. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/448) if you'd like further details.
25
+ - (s2Member/s2Member Pro) **Security:** This release of s2Member checks for the existence of the WordPress PHP Constant: `WPINC` instead of looking for the less reliable `$_SERVER['SCRIPT_FILENAME']`. Some site owners reported this was causing trouble in a localhost environment during testing, or when running s2Member on some hosts that are missing the `SCRIPT_FILENAME` environment variable; e.g., some Windows servers. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/454) if you'd like additional details.
26
  - (s2Member Pro) **Advanced Import/Export Compat:** This release of s2Member Pro includes compatibility and a bug fix when running on WordPress v4.1+. Three PHP notices during importation, along with some quirky behavior associated with the `role` CSV column have been corrected. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/455) for technical details.
27
  - (s2Member Pro) **`[s2Member-List /]` Bug Fix:** This release resolves an issue with pagination in the `[s2Member-List /]` shortcode after recent improvements to the search functionality. See [this GitHub issue](https://github.com/websharks/s2member/issues/155#issuecomment-69403120) if you'd like additional details.
28
+ - (s2Member Pro) **`[s2Member-List /]` Enhancement:** This release improves search functionality in the `[s2Member-List /]` shortcode, making it so that all searches default to `*[query]*`; i.e., are automatically wrapped by wildcards. If a user enters a wildcard explicitly (or a double quote), this default behavior is overridden and the search query is taken as given in such a scenario. This makes the search functionality easier for end-users to work with, since it no longer requires an exact match. Default behavior is now a fuzzy match. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/394) if you'd like further details.
29
  - (s2Member/s2Member Pro) **AWS v4 Authentication:** This release of s2Member adds AWS v4 Authentication support for Amazon Web Service Regions that only accept the AWS v4 authentication scheme. If you had trouble in the recent past when attempting to integrate s2Member with S3 Buckets (or with CloudFront) in regions outside the USA, this release should resolve those issues for you. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/440) if you'd like additional technical details.
30
+ - (s2Member Pro) **Bug Fix:** Pro-Form Checkout Options not working in all cases whenever they are used together with Free Registration Forms. Resolved in this release.
31
 
32
  = v150102 =
33
 
34
  - (s2Member/s2Member Pro) **Custom Field Mapping:** This release of s2Member adds an internal mapping from s2Member's Custom Field values for each user, to the `get_user_option()` function in the WordPress core. This makes it possible to retrieve user custom field values like always via `get_user_field()` or now through the native `get_user_option()` function also. The benefit of this is that s2Member's custom fields are now more compatible with other themes/plugins for WordPress.
35
+ - (s2Member Pro) **[s2Member-List /] Shortcode:** It is now possible to search through custom fields created with s2Member using the `search_columns=""` attribute; e.g., `search_columns="user_login,user_email,s2member_custom_field_MYFIELDID"`; where `MYFIELDID` can be replaced with a field ID that you generate with s2Member via `Dashboard ⥱ s2Member ⥱ General Options ⥱ Registration/Profile Fields`. See also: [this KB article](http://www.s2member.com/kb/s2member-list-shortcode/) for further details. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/155) for details regarding this improvement.
36
  - (s2Member/s2Member Pro) **MailChimp Bug Fix** This release fixes a bug first introduced in the previous release, which was causing Interest Groups configured w/ s2Member to not be added properly. Resolved in this release. Props to @ethanpil Thanks!
37
  - (s2Member Pro) **ccBill Buttons** This release updates all ccBill button graphics. The MasterCard logo has been removed, and a new set of buttons was created to improve upon the set provided in previous versions of s2Member Pro. See: [this GitHub issue](https://github.com/websharks/s2member/issues/392) if you'd like further details.
38
+ - (s2Member Pro) **Authorize.Net** The `AUD` currency code is now supported by Authorize.Net, and thus, s2Member Pro has been updated to support the `AUD` currency code for Pro-Forms integrated with Authorize.Net. See [this GitHub issue](https://github.com/websharks/s2member/issues/383) if you'd like further details.
39
  - (s2Member Pro) **Subscr. CID for Stripe** This release corrects a bug which made it impossible to update the Subscr. CID value (for Stripe) using the user edit form in the Dashboard. For further details, please see [this GitHub issue](https://github.com/websharks/s2member/issues/380).
40
  - (s2Member/s2Member Pro) **Bug fix** s2Member's membership access times log was failing to collect all required access times under certain scenarios where multiple CCAPS were being added or removed in succession within the same process, but across multiple function calls. This resulted in unexpected behaviors (in rare cases) when attempting to use the `[s2Drip /]` shortcode. Fixed in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/406) for technical details.
41
  - (s2Member/s2Member Pro) **Compatibility** This release includes a fix for s2Member's Multisite Network patches applied to the `wp-admin/user-new.php` file whenever you configure s2Member on a Multisite Network. This change makes s2Member compatible with the coming release of WordPress v4.1 and v4.2-beta as it exists now. See: [this GitHub issue](https://github.com/websharks/s2member/issues/410) if you'd like additional details.
42
  - (s2Member Pro) **Bug Fix:** A feature that was previously introduced in v140816, which made it possible for site owners to set a failed payment threshold (in s2Member's Authorize.Net integration), was suffering from an off-by-one issue during total failed payment calculations. Fixed in this release. See also [this GitHub issue](https://github.com/websharks/s2member/issues/416) if you'd like further details.
43
+ - (s2Member Pro) **Feature Enhancement:** Whenever a failed payment threshold is reached (in s2Member's Authorize.Net integration), not only will s2Member terminate on-site access, but now the underlying ARB (Automated Recurring Profile) is cancelled at the same exact time. This way future billing attempts on the Authorize.Net side will not be possible; i.e., it ensures that a failed payment threshold will always terminate both on-site access and the ARB itself together at the same time, as opposed to allowing the ARB termination to occur automatically via Authorize.Net, _whenever_. See also [this GitHub issue](https://github.com/websharks/s2member/issues/416) if you'd like further details.
44
  - (s2Member Pro) **ClickBank Disclaimer:** This release of s2Member adds a default Auto-Return Header Template (customizable from `s2Member ⥱ ClickBank Options` in the Dashboard) which includes a disclaimer that ClickBank requires of most merchants before final approval.
45
 
46
  _This default template should help to reduce the time it takes new merchants to receive final approval from ClickBank when first starting out in the ClickBank network. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/412) if you'd like further details._
47
+ - (s2Member Pro) **Bug Fix:** PayPal Pro-Forms for Specific Post/Page Access, and configured with `accept="paypal"` (i.e., to accept PayPal only) were not hiding the entire Billing Method section as intended. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/399) if you'd like further details.
48
+ - (s2Member Pro) **Bug Fix:** PayPal Pro-Forms using Express Checkout for Billing Agreements under a non-native currency (i.e., under a different currency than their own PayPal account) were failing under some scenarios (notably with the `BRL` currency code). Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/414) if you'd like technical details.
49
  - (s2Member Pro) **Stripe API:** s2Member's Stripe integration has been updated to use the new `statement_descriptor` field in favor of the now deprecated `statement_description`. See [this GitHub issue](https://github.com/websharks/s2member/issues/422) for technical details.
50
  - (s2Member Pro) **Stripe Bug Fix:** In the case of a global tax rate having been applied to the total cost, there were certain scenarios where s2Member Pro would kick back an error message, "Invalid Parameters to Stripe". Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/425) if you'd like technical details.
51
  - (s2Member/s2Member Pro) **WP Core Compat.:** This version of s2Member forces the `wptexturize` filter off in WordPress, due to a bug that was introduced in recent versions of the WordPress core; which results in broken shortcodes in some scenarios. Until the underlying bug is fixed in the WP core, the `wptexturize` filter must be disabled to prevent corruption of any WordPress shortcode that may contain `<` or `>` symbols.
56
  - (s2Member Pro) **Coupon Codes:** All transaction-related email templates now support three additional Replacement Codes: `%%full_coupon_code%%`, `%%coupon_code%%`, and `%%coupon_affiliate_id%%`. These have been documented in your Dashboard in places where transaction-related email templates are configured. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/384) if you'd like additional details.
57
  - (s2Member Pro) **Stripe Tax Info:** s2Member now attaches metadata to Stripe Charges and to Stripe Subscriptions which includes a JSON-encoded object containing two or more properties when tax applies.
58
 
59
+ These metadata properties can be found in your Stripe Dashboard with the metadata key: `tax_info`; which contains the following JSON-encoded properties: `tax` (total tax that was or will be charged on the regular amount), `tax_per` (tax percentage rate that was applied based on your configuration of s2Member Pro); along with `trial_tax` and `trial_tax_per` in the case of a Stripe Subscription that includes an initial/trial period that requires payment; i.e., the tax applied (if any) to an initial/trial payment on a Subscription.
60
 
61
  We hope this additional information being recorded by s2Member and stored securely within your Stripe account will make it easier for you to maintain accurate bookkeeping records moving forward. This additional metadata is generated for new customers only. It will not be backfilled for any past transactions.
62
 
63
+ - (s2Member Pro) **Stripe Tax Info:** s2Member now passes the tax location; i.e., `address_state`, `address_zip`, and `address_country` to each Stripe Card object associated with a Stripe Customer.
64
 
65
  We hope this additional information being recorded by s2Member and stored securely within your Stripe account will make it easier for you to maintain accurate bookkeeping records moving forward. This additional cardholder data is collected and stored for new customers only; it will not be backfilled for any past transactions.
66
 
68
 
69
  - (s2Member Pro) **Stripe Coupon Code:** s2Member now attaches metadata w/ a coupon code used by your customer (if applicable) to each Stripe Charge and/or Stripe Subscription object.
70
 
71
+ This metadata property can be found in your Stripe Dashboard with the metadata key: `coupon`; which contains the following JSON-encoded property: `code` i.e., the full coupon code used by your customer. This additional metadata is generated for new customers only. It will not be backfilled for any past transactions. Filled only for transactions that use a coupon code.
72
  - (s2Member Pro) **Stripe Invoice:** This release corrects a bug in s2Member's Stripe integration whereby `subscr-signup-as-subscr-payment` was not always being forced into the core gateway processor; resulting in a miscalculation of the `last_payment_time` under certain scenarios. Fixed in this release. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/396) if you'd like additional details.
73
 
74
  = v141007 =
75
 
76
  - (s2Member Pro) **ClickBank IPN v6:** This release enables a new integration option for site owners integrated with ClickBank. You may now choose to integrate with v6 of ClickBank's IPN service, since all previous versions are slowly being phased out by ClickBank. Please see: `Dashboard ⥱ s2Member ⥱ ClickBank Options ⥱ IPN Integration` for v6 config. options. See also [this GitHub issue](https://github.com/websharks/s2member/issues/256) if you'd like further details regarding this topic. See also: [this article @ ClickBank](https://support.clickbank.com/entries/22803622-instant-notification-service).
77
+ - (s2Member/s2Member Pro) **AWeber API Integration:** This release of s2Member adds a new option for site owners using AWeber. It is now possible to integrate with the new [s2Member App](http://www.s2member.com/r/aweber-api-key) for AWeber; i.e., via the AWeber API instead of via email-based communication. For further details, please see: `Dashboard ⥱ s2Member ⥱ API / List Servers ⥱ AWeber Integration`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/303) if you'd like additional details.
78
  - (s2Member/s2Member Pro) **Bug Fix:** The EOT Behavior option for `refunds,partial_refunds,reversals` was not being accepted by s2Member. Fixed in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/345) if you'd like further details.
79
+ - (s2Member/s2Member Pro) **MailChimp API Wrapper:** This release of s2Member comes with an updated API wrapper class for MailChimp integration. No change in functionality, just a smoother, slightly faster, and more bug-free interaction with the MailChimp API. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/303) if you'd like further details regarding this improvement. See also: [the official MailChimp API class](https://bitbucket.org/mailchimp/mailchimp-api-php); i.e., what s2Member has been updated to in this release.
80
+ - (s2Member/s2Member Pro) **URI Restrictions caSe-insensitive (Security Fix)** This release of s2Member changes the way URI Restrictions work. All URI Restrictions are now caSe-insensitive (i.e., `/some-path/` is now the same as `/some-Path/`), allowing s2Member to automatically pick up different variations used in attempts to exploit the behavior of certain slugs within the WordPress core. You can also change this new default behavior, if you prefer. Please see: `Dashboard ⥱ s2Member ⥱ Restriction Options ⥱ URI Restrictions`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/354) for the details about why this was changed in the most recent copy of s2Member.
81
  - (s2Member/s2Member) **AWeber Role-Based Emails:** In this release we're adding a note in the s2Member UI regarding role-based email addresses being rejected by AWeber. AWeber does not allow role-based emails like: `admin@` or `webmaster@` to be subscribed. It is suggested that you enable s2Member's config. option: "Force Personal Emails" if you intend to integrate with AWeber. Please see: `Dashboard ⥱ s2Member ⥱ General Options ⥱ Registration/Profile Fields`; where you can tell s2Member for force personal email addresses when someone registers on-site. This will prevent a potential subscriber from entering something like `admin@example.com` as their email address.
82
 
83
  = v140921 =
84
 
85
+ - (s2Member/s2Member Pro) **WP v4 over SSL Compat.** A compatibility issue with the `home_url()` function in the latest release of WordPress has been resolved with this release. Some site owners reported that their s2Member menu pages were appearing without any CSS/JavaScript being loaded; i.e., the graphical UI was not appearing as one would expect under certain scenarios.
86
  - (s2Member/s2Member Pro) **WP v4 Compat.** This release brings s2Member up-to-date with the latest changes to the `like_escape()` function in WP v4.0. The `like_escape()` function is now deprecated in favor of `wpdb::esc_like()`. s2Member has been updated in this release, but also remains compatible with previous versions of WordPress. See [this GitHub issue](https://github.com/websharks/s2member/issues/329) if you'd like further details.
87
  - (s2Member Pro) **[s2MOP /] Shortcode Enhancment** The `[s2MOP /]` shortcode allows for a new `required_value=""` attribute. Please see [this KB article](http://www.s2member.com/kb/s2mop-shortcode/) for details about the `[s2MOP /]` shortcode. See also: [this GitHub issue](https://github.com/websharks/s2member-pro/issues/51) if you'd like further details.
88
 
89
  = v140909 =
90
 
91
  - (s2Member/s2Member Pro) **Compatibility:** Several instances of `site_url()` (a WordPress core function) have been converted to `home_url()` instead. This provides better compatibility with WordPress installations running from a sub-directory. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/293) if you'd like further details.
92
+ - (s2Member Pro) **Bug Fix:** Ampersands; i.e., `&` symbols in a ClickBank button `desc=""` attribute are now converted to the word `and` automatically. The symbol itself causes issues in ClickBank's IPN processing. Fixed in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/253) if you'd like further details.
93
  - (s2Member) **Bug Fix:** Improving compatibility with Mozilla/Firefox for the default `wp-login.php?action=register` handler. This release corrects an issue where `<select>` fields contained text with too large a font-size for Mozilla browsers to deal with. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/244) if you'd like further details.
94
  - (s2Member) **WP v4.0 / bbPress Compat.** A conflict between WordPress v4.0, bbPress v2.5.4 and the previous release of s2Member has been resolved. A symptom of this issue was to see the leading topic post missing from your bbPress forum threads.
95
 
98
  However, please note that some other 3rd-party plugins may still conflict in this way; when running the latest version of bbPress under WordPress v4.0. We are working to notify bbPress and other plugin authors about this issue; just to help others avoid the problem. While unconfirmed, some site owners reported that the Relevanssi search plugin may have some trouble with this also.
99
  For further details, please see <http://bbpress.org/?p=151839>. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/321) if you'd like all of the details regarding this workaround in the s2Member software.
100
  - (s2Member) **WP v4.0 Compat.** Updating s2Member's use of the now-deprecated `get_all_category_ids()`. Using `get_terms()` instead. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/322) if you'd like further details.
101
+ - (s2Member Pro) **Stripe Bug Fix:** This release corrects an issue with Stripe Pro-Forms and a `$0` trial period. A symptom of this bug was to find a customer's Stripe token value missing from their Customer object in the Stripe Dashboard. This issue impacted Pro-Forms whenever a 100% free trial was offered (i.e., with a `$0` sale amount). Resolved by this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/326) if you'd like the details.
102
 
103
  = v140816 =
104
 
113
 
114
  See also: [this GitHub issue](https://github.com/websharks/s2member/issues/179) if you'd like more info.
115
  * (s2Member Pro) **Authorize.Net** This release introduces a new configurable EOT Behavior option for site owners integrated with Authorize.Net. It is now possible to configure a Max Failed Payments threshold; after which s2Member will automatically trigger an EOT (End Of Term). See: `Dashboard ⥱ s2Member ⥱ Authorize.Net Options ⥱ EOT Behavior`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/141) if you'd like more info.
116
+ * (s2Member/s2Member Pro) **Enhancement** A new Replacement Code (`%%current_user_nicename%%`) has been introduced by s2Member to improve compatibility with plugins like BuddyPress and bbPress. If you are currently using a Special Login Redirection URL as your s2Member Login Welcome Page, and you've used `%%current_user_login%%` (i.e., the old way), we suggest that you update your Special Redirection URL to use `%%current_user_nicename%%`. Please see [Jason's comments here](https://github.com/websharks/s2member/issues/276#issuecomment-51706582) for further details. See also: `Dashboard ⥱ s2Member ⥱ General Options ⥱ Login Welcome Page`. It is this area of your Dashboard where a Special Redirection URL can be configured.
117
  * (s2Member Pro) **Pro Login Widget** There are some new Replacement Codes available for the "My Account" page URL, and the "My Profile" page URL whenever you configure the s2Member Pro Login Widget in WordPress. s2Member Pro now supports things like `%%current_user_nicename%%` and `%%current_user_level%%` in these customizable URLs. See: `Dashboard ⥱ Appearance ⥱ Widgets ⥱ s2Member Pro Login Widget` for further details. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/276#issuecomment-51706582) if you'd like more info.
118
+ * (s2Member Pro) **Stripe Bug Fix** Wrapping multiple Stripe Pro-Form Shortcodes together as "Checkout Options" was not working properly in the previous release. The dropdown for Checkout Options was not maintaining the underlying variable needed to keep a Checkout Option selected. Fixed in this release. If you'd like more info, please see [this GitHub issue](https://github.com/websharks/s2member/issues/296).
119
  * (s2Member Pro) **Stripe Bug Fix** A few site owners reported issues between s2Member's integration with Stripe and other plugins that also depend on the Stripe SDK for PHP. Fixed in this release. If you'd like more info, please see [this GitHub issue](https://github.com/websharks/s2member/issues/295).
120
+ * (s2Member Pro) **Pro Cancellation Forms** This release introduces a new Shortcode Attribute that can be used with Pro Cancellation Forms. The new Shortcode Attribute is `unsub=""`. Setting this to a value of `unsub="1"` will enable an automatic unsubscribe upon cancellation. To clarify, this is related to any List Servers (e.g., MailChimp, AWeber, GetResponse) that you integrate with s2Member. If `unsub="1"` when a customer cancels future billing they will also be removed from the mailing list they are currently subscribed to, according to your List Server configuration in s2Member. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/238) if you'd like more info.
121
+ * (s2Member/s2Member Pro) **Child Theme Compabitility** Portions of s2Member (e.g., Return-Page templates, s2Stream templates, Pro-Form templates, IP Restriction templates, and more) allow advanced site owners to use custom templates of their own. In the past these templates *had* to be created inside the parent theme directory or inside of your `/wp-content/` directory. Starting with this release, s2Member will also look for custom templates inside of your current Child Theme directory too (i.e., `get_stylesheet_dir()`). Please see [this GitHub issue](https://github.com/websharks/s2member/issues/271) if you'd like more info.
122
  * (s2Member/s2Member Pro) **S3/CloudFront Compatibility** The latest release of s2Member has been made compatible with the latest changes at Amazon S3/CloudFront [regarding IAM users](http://aws.amazon.com/blogs/aws/updated-iam-console/). If you've been running s2Member together with Amazon S3/CloudFront there are no changes necessary in s2Member configuration. If you are just integrating s2Member with Amazon S3/CloudFront you are advised to setup an IAM user instead of using your AWS Root Keys. s2Member will continue to work with either Root Keys or with IAM user keys. Either are fine. If you'd like more info, please see [this GitHub issue](https://github.com/websharks/s2member/issues/297).
123
  * (s2Member/s2Member Pro) **MySQLi Compatiblity** This release brings s2Member into full compatibility with the MySQLi extension. In the previous release it was reported that one specific routine in s2Member that checks the total number of users in your WordPress database was incompatible with MySQLi. Resolved in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/274) if you'd like more info.
124
  * (s2Member/s2Member Pro) **Amazon CloudFront** s2Member now makes a new button [Reset CloudFront Configuration] available inside of your WP Dashboard under: `s2Member ⥱ Download Options ⥱ Amazon CloudFront`. This button allows a site owner (if necessary) to do a quick reset of s2Member's current integration with Amazon CloudFront Distributions. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/270) if you'd like more info.
125
  * (s2Member/s2Member Pro) **Bug Fix** This release corrects a bug in s2Member's log of a user's WordPress Capability access times. This bug had no serious impact on previous releases of s2Member. However, it was a bug that needed fixing nonetheless. Resolved in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/237) if you'd like more info.
126
+ * (s2Member/s2Member Pro) **Responsive Compatibility** This release of s2Member makes the `/wp-login.php` file (the WordPress Login/Registration system) Responsive; i.e., more compatible with mobile devices and tablets. This only impacts site owners that use s2Member's Login/Registration Design options to improve and customize the appearance of this core WordPress component. No changes necessary to existing installations for this to kick-in. It's automatic (assuming you are using this s2Member feature). Please see [this GitHub issue](https://github.com/websharks/s2member/issues/211) if you'd like more info.
127
  * (s2Member Pro) **Remote Operations API** This release of s2Member makes it possible to change the API Key assigned to your WordPress installation. The Pro Remote Operations API is one way for developers to integrate with some of s2Member's functionality. See: `Dashboard ⥱ s2Member ⥱ API / Scripting ⥱ Remote Operations API`. This is where it's possible to change your API Key if you'd like to. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/206) if you'd like more info.
128
+ * (s2Member/s2Member Pro) **Enhancement** s2Member's Auto-Return system (i.e., default Thank-You page handler) which integrates with: PayPal Standard Buttons, ClickBank, and Google Wallet; has been updated in this release. If a customer happens to find their way back to a self-expiring Auto-Return URL (a rare occurrence); instead of an unfriendly error message about duplicate return data, s2Member now provides a more friendly note that asks the customer to check their email for the details needed to access what they paid for. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/205) if you'd like more info.
129
  * (s2Member/s2Member Pro) **Enhancement** s2Member's Security Encryption Key configuration panel in the Dashboard was updated to include additional details related to the use of your Security Encryption Key. This additional information explains s2Member's use of this key in greater detail. See: `Dashboard ⥱ s2Member ⥱ General Options ⥱ Security Encryption Key`. See also: [this GitHub issue](https://github.com/websharks/s2member/issues/182) if you'e like more info.
130
+ * (s2Member Pro) **One-Time-Offers Upon Login** This release resolves a conflict between s2Member's Pro Login Widget and One-Time-Offers (Upon Login)—a feature that can be configured with s2Member Pro. One-Time-Offers (Upon Login) now take precedence over certain automatic login redirections that can occur through the Pro Login Widget, removing the chance of a conflict. If a visitor logs into their account with a default login redirection URL (i.e., a redirection URL formulated dynamically by the Pro Login Widget that is not related to a visitor's request to access a specific page of the site), and a One-Time-Offer is triggered at the same time, the One-Time-Offer will take precendence. The visitor will see the One-Time-Offer instead of being redirected to the default location specified by the Pro Login Widget. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/119) if you'd like more info.
131
 
132
  = v140725 =
133
 
134
+ * (s2Member Pro) **NEW: Stripe Pro-Forms (Beta)** Holy hula hoop! s2Member now has a deep integration with Stripe for on-site credit card processing that uses a Stripe overlay. Stripe is an almost-free service that is super easy to setup and work with. We think you'll find that Stripe is quickly becoming the MOST popular of all payment gateways in the industry. A few bugs have [already been zapped](https://github.com/websharks/s2member/issues?milestone=5&page=1&state=closed) prior to this release after some initial beta testing was completed over the last few weeks. Everything is looking great so far, but please do [report any new issues via GitHub](https://github.com/websharks/s2member/issues?page=1&state=open).
135
 
136
  **If you are an s2Member Pro site owner** you can upgrade to the latest version of s2Member Pro at anytime you like; then enable Stripe as an additional payment gateway option. See: `Dashboard ⥱ s2Member Pro ⥱ Other Gateways`.
137
 
141
  * (s2Member Pro) **ClickBank Bug Fix** Nillable fields causing some issues (only under one specific scenario) after a recent change in the ClickBank API. Fixed in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/250) if you would like further detais.
142
  * (s2Member Pro) **Compatibility** Resolved a minor single-quote issue in the Visual Editor. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/228) if you'd like further details.
143
  * (s2Member/s2Member Pro) **Bug Fix** Non-HTML whitespace being trimmed inside the `[s2If /]` shortcode. Resolved in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/224) if you'd like further details.
144
+ * (s2Member Pro) **Pro-Forms Bug Fix** Related to List Server processing during an upgrade. This bug impacted all payment gateways integrated with s2Member's Pro-Forms; including PayPal, Authorize.Net and now Stripe. Resolved in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/216) if you'd like further details.
145
  * (s2Member Pro) **Bug Fix** An `array_intersect()` error in the `[s2MOP /]` shortcode under the right conditions. Fixed in this release. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/204) if you'd like further details.
146
+ * (s2Member Pro) **Enhancement** Free Registration Pro-Forms can now be included in a list of nested Checkout Option drop-downs. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/197) if you'd like further details. See also: `Dashboard ⥱ s2Member ⥱ [Your Payment Gateway] Pro-Forms ⥱ Checkout Options`; where there is more information about how to use Checkout Options with s2Member Pro-Forms.
147
  * (s2Member/s2Member Pro) **Bug Fix / List Servers** This release corrects a bug that may cause members to be unsubscribed from a List Server if they forget to check the box again while editing their profile. In the previous release we introduced a feature that allows the checkbox to be pre-checked if the user already chose to subscribe once before. However, the internal tracking for this was not as reliable as it could be. There is still more work to be done on this front, but the immediate issue has now been resolved. Please see [this GitHub issue](https://github.com/websharks/s2member/issues/196) if you'd like further details.
148
  * (s2Member/s2Member Pro) **WP_DEBUG Compat.** Some older portions of s2Member's codebase were cleaned up and reformatted in this update. This is an ongoing process to bring s2Member into full compatibility with `WP_DEBUG` mode in WordPress. This is intended to help other developers in the WP community. Much progress has been made on this front now, but still some more work to do. We will continue to update s2Member's codebase little-by-little with each release.
149
  * (s2Member/s2Member Pro) **Uninstall vs. Deactivation** s2Member has always made a strong effort to cleanup after itself should you decide (for whatever reason) to uninstall it. However, we are also very concerned with preserving any data associated with such a powerful plugin that comes with so many options. Nobody wants accidental data loss, right!?
150
 
151
 
152
+ In the past, s2Member came with a feature called "Deactivation Safeguards". These (if disabled) attached themselves to a plugin "deactivation" hook within WordPress and they would uninstall s2Member when you deactivated the plugin. Starting with this release ,s2Member's Deactivation routines have been changed. We now attach them to the WordPress "uninstall" hook—which is triggered only on plugin deletion.
153
 
154
  In this way, deactivating s2Member will never result in a loss of any data. Instead of "Deactivation Safeguards", s2Member now calls this feature "Plugin Deletion Safeguards". In short, to tell s2Member to uninstall itself (including any data/options associated with s2Member), you can simply disable s2Member's Plugin Deletion Safeguards under: `s2Member ⥱ General Options`, and then deactivate _and delete_ the s2Member plugin entirely. Whenever you delete the plugin this automatically and silently triggers s2Member's uninstaller which cleans up after itself nicely :-)
155
 
156
  = v140630 =
157
 
158
+ - (s2Member Pro) **Stripe Payment Gateway** Hooray! s2Member Pro now integrates with [Stripe](http://www.s2member.com/r/stripe). s2Member Pro-Forms, Coupon Codes, Tax Settings, Checkout Options, and all of the other great features provided by s2Member Pro are now compatible with Stripe. To enable Stripe in your installation of s2Member, please see: `Dashboard ⥱ s2Member ⥱ Other Gateways ⥱ Stripe`.
159
 
160
  We expect Stripe to become the most popular payment gateway integration for s2Member Pro over the next few months. Stripe is nearly free; it's easier to setup, easier to maintain; and just more flexible overall in our opinion.
161
 
164
 
165
  = v140614 =
166
 
167
+ * (s2Member/s2Member Pro) **Quick Cache Compat.** This release makes it possible for Quick Cache to cache content protected by s2Member. If, and only if, you have [Quick Cache Pro](http://www.websharks-inc.com/product/quick-cache/) configured to enable user-specific caching; i.e., to cache when users are logged into the site. See [this GitHub issue](https://github.com/websharks/s2member/issues/172) if you'd like further details.
168
  * (s2Member Pro) **ClickBank API Compat.** This release brings s2Member's ClickBank integration into full compatibility with a recent change in the ClickBank API. Under the right conditions, a symptom of this bug was to see an error regarding an unexpected `txnType` upon returning from checkout via ClickBank. Resolved in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/199) if you'd like further details.
169
 
170
  = v140603 =
171
 
172
+ * (s2Member/s2Member Pro) **Profile Sync to List Servers** If you have a supported List Server integrated with s2Member (e.g., MailChimp, GetResponse, AWeber), the opt-in checkbox will now also be displayed in user profile editing panels (i.e., by the `[s2Member-Profile /]` shortcode, or if you integrate s2Member with BuddyPress profiles).
173
 
174
+ If a user updates their profile, their profile on the List Server is updated too (i.e., s2Member updates their first name, last name, along with any merge vars or customs you've integrated through s2Member hooks/filters). If a user changes their email address, they will be subscribed with the new email address. Leaving the box unchecked during a profile update will effectively unsubscribe the user from the lists you have configured at their current Membership Level.
175
 
176
+ *s2Member's AWeber integration does not yet support profile updates (i.e., changes in first/last name or other details); it only supports the ability to either subscribe or unsubscribe; and/or changes in email address.*
177
 
178
  See [this GitHub issue](https://github.com/websharks/s2member/issues/146) if you'd like further details.
179
  * (s2Member/s2Member Pro) **BuddyPress Compatibility** This release makes s2Member and s2Member Pro compatible with the latest release of BuddyPress. BuddyPress v2.01 broke some of s2Member's previous integration. Fixed in this release. See [this GitHub issue](https://github.com/websharks/s2member/issues/162) if you'd like further details.
180
  * (s2Member/s2Member Pro) **Multisite Lost Password URL** In a multisite environment (given the WordPress default behavior), a lost password URL generated by WordPress will force all users to recover their password from the Main Site in the Network; which is usually NOT desirable. This release of s2Member fixes this odd behavior in the WordPress core by allowing users to recover their password in the UI for the current Child Blog they are accessing. See [this GitHub issue](https://github.com/websharks/s2member/issues/138) if you'd like further details and ways to enable/disable; or even customize this further.
181
+ * (s2Member/s2Member Pro) **Default EOT Behavior** By popular demand, this release changes s2Member's default EOT (End Of Term) Behavior option with respect to refunds/chargebacks. The new default behavior (assuming you have not yet configured s2Member) is to force an EOT on chargebacks only; not on a refund of any kind. A refund is just a refund (in many cases) and an EOT (if one should occur) is something that a site owner generally should decide on their own; i.e., to review refunds manually and if an EOT should occur, the site owner will mostly like prefer to terminate a user's account access on their own; and on a case-by-case basis.
182
 
183
  **Further clarification...** EOT (End Of Term) is meant to occur when a subscription ends, and since a refund doesn't necessarily end a subscription, it's not the default anymore to trigger an automatic EOT on a refund event. Site owners that want an EOT on refunds still have that option available to them however. See `Dashboard ⥱ s2Member ⥱ [Payment Gateway] Options ⥱ Auto EOT Behavior ⥱ Chargebacks/Refunds`.
184
 
195
 
196
  * (s2Member Pro) **`[s2Member-List /]` Shortcode** Amazing new feature! It is now possible to list members using a powerful shortcode, and even make it possible for members to view and search for each other. See [this KB article](http://www.s2member.com/?p=62860) for all the details on this feature. Very cool!
197
  * (s2Member/s2Member Pro) **Server-Side Validation** For Registration/Profile Fields that you configure on your own (using the s2Member software), there is now support within all areas of the s2Member codebase for both JavaScript and *now server-side validation too*. In the past, all validations applied to custom fields was via JavaScript only. With server-side validation too, now it is impossible for required and/or invalid fields that you configure to go missing. This also resolves a few issues related to spam bots attempting to bypass JavaScript validation. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/99) if you'd like further details.
198
+ * (s2Member Pro) **Button Processing Animation**. Improving the processing animation used in submit buttons across Pro-Forms. Instead of a script-based solution (rather jumpy), we are now taking advantage of CSS3 for a much smoother animation. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/114) if you'd like further details.
199
  * (s2Member Pro) **`[s2Drip /]` Shortcode Enhancement** This release adds support for a new `access=""` shortcode attribute that can parse `and` / `or` logic. Also, it is now possible for `[s2Drip /]` to be used with Custom Capabilities too! See [this KB article](http://www.s2member.com/kb/s2drip-shortcode/) for all the details.
200
  * (s2Member/s2Member Pro) **Bug Fix, Custom Fields UI** This release corrects a bug related to the `jquery/.htaccess` file that ships with s2Member. A symptom was to have issues with the Registration/Profile Fields UI and find a JS error in the browser's developer console. Fixed in this release. See also, [this GitHub issue](https://github.com/WebSharks/s2Member/issues/144#issuecomment-43198045) if you'd like further details.
201
  * (s2Member Pro) **Bug Fix, Clickbank HTTPS** This release corrects a bug in the default Auto-Return Page for ClickBank, when/if it's served over the `https` protocol. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/145) if you'd like further details.
251
  * (s2Member) **Cosmetics**. Updating s2Member's adminitrative menu pages to enhance their appearance in the most recent versions of WordPress. Nothing significant, just minor tweaks.
252
  * (s2Member Pro) **PayPal IPNs**. Adding support for `txn_type` values: `merch_pmt`, `mp_signup`, and `mp_cancel` to enhance s2Member's support for Billing Agreements when integrated together with a PayPal Pro (Payflow Edition) account that processes Express Checkout transactions. This also resolves a bug related to cancellation IPN processing in a specific scenario. Fixed in this release.
253
  * (s2Member Pro) **[s2If][else]**. Requires s2Member Pro. This release adds support for a new `[else]` condition when using the `[s2If]` shortcode to protect parts of your content. For examples, please see: `Dashboard ⥱ s2Member ⥱ API Scripting ⥱ Simple Shortcode Conditionals`. See also: `Dashboard ⥱ s2Member ⥱ Restriction Options ⥱ Simple Shortcode Conditionals`.
254
+ * (s2Member Pro) **[s2If php=""]**. Requires s2Member Pro. This release adds support for a new `php` Shortcode Attribute; e.g., `[s2If php=""]`. This makes it possible to use arbitrary PHP code in your `[s2If]` shortcodes. For examples, please see: `Dashboard ⥱ s2Member ⥱ API Scripting ⥱ Simple Shortcode Conditionals`. **Note: this feature is disabled by default.** You must enable the `php` attribute for this to work as expected. Please see: `Dashboard ⥱ s2Member ⥱ Restriction Options ⥱ Simple Shortcode Conditionals` (with s2Member Pro installed).
255
  * (s2Member) **Bug Fix**. Correcting an issue related to Login Redirections and an E_NOTICE. Please see [this GitHub issue](https://github.com/WebSharks/s2Member/issues/75) if you'd like further details.
256
  * (s2Member Pro) **[s2Drip]**. Requires s2Member Pro. This release adds support for a new easy-to-use shortcode that can drip content to paying Members based on their Membership Level. For examples, please see: `Dashboard ⥱ s2Member ⥱ API Scripting ⥱ Content Dripping`.
257
  * (s2Member Pro) **ClickBank**. Adding support for the `vtid` parameter in ClickBank Button Shortcodes. See [this GitHub issue](https://github.com/WebSharks/s2Member/issues/44) if you'd like further details.
258
+ * (s2Member Pro) **PayPal Express Checkout**. Updating PayPal Express Checkout cancellation links so they will automatically use the PayPal Merchant ID supplied by a site owner (i.e., PayPal's recommended behavior). If you'd like further details please see [this GitHub commit](https://github.com/WebSharks/s2Member-Pro/commit/5efbe35eed352868a956c94e51ab09f8e561892a).
259
+ * (s2Member Pro) **Pro-Forms**. Adding a new filter for developers `s2member_pro_cancels_old_rp_before_new_rp`. This is true by default. If you'd like to prevent s2Member from terminating an existing Recurring Profile (before creating a new one); i.e., during an upgrade... you can set this to a FALSE value. Not recommended, but there are a few edge cases where it could be helpful for developers. See also [this GitHub commit](https://github.com/WebSharks/s2Member-Pro/commit/19a84c81070bb0e1869b5dbd9d0325cc458fd016).
260
  * (s2Member Pro) **ClickBank Bug Fix**. Adding support for alphabetics in ClickBank Item Numbers. This bug impacted the ClickBank Button Generator only, it did not prevent alphabetics from being used in a raw Shortcode. Still, this has been resolved now. See also [this GitHub issue](https://github.com/WebSharks/s2Member/issues/46).
261
  * (s2Member Pro) **PayPal Mobile Bug Fix**. This release corrects a bug related to the `MAXAMT` PayPal specification that is sent via Express Checkout on a mobile device. A symptom of this bug was to sometimes see PayPal's awkward default amount of `$25` when completing checkout on a mobile device. Fixed in this release.
262
+ * (s2Member Pro) **PayPal Express Checkout**. This release addresses an issue where a customer reaches PayPal.com with an item description that inadequately reads "Future payment" (i.e., missing the description provided by a site owner). PayPal's latest improvements have made it possible for s2Member to get this right. Resolved in this release.
263
  * (s2Member/s2Member Pro) **Partial Refunds**. This release adds support for Partial Refunds in the s2Member EOT Behavior Options. See also [this GitHub issue](https://github.com/WebSharks/s2Member/issues/40).
264
 
265
  = v140105 =
266
 
267
+ * (s2Member/s2Member Pro) **Compatibility**. Updated to support WordPress v3.8. Nothing significant, just minor UI tweaks in the Login/Registration Design for WordPress v3.8; e.g., `wp-login.php` got some minor improvements in this release of s2Member and s2Member Pro.
268
+ * (s2Member) **Compatibility**. PayPal's API for Subscription Cancellation Buttons was changed recently. PayPal now requires a Merchant ID instead of the site owner's email address. This issue has been causing an error when a user attempts to cancel a PayPal Subscription through an s2Member-generated PayPal Subscription Cancellation "Button" (i.e., this affects Buttons only, not Pro-Forms). Fixed in this release. Site owners using PayPal Buttons should update their PayPal Merchant ID for s2Member. Please see: `Dashboard ⥱ s2Member ⥱ PayPal Options ⥱ Account Details`.
269
+ * (s2Member/s2Member Pro) **Compatibility**. Default s2Member option value for CSS/JS Lazy Loading is now off instead of on; e.g., s2Member's CSS/JS JavaScript libraries are now loaded on every page by default. Lazy loading must now be enabled by a site owner as a performance enhancement (optional). For further details, please see: `Dashboard ⥱ s2Member ⥱ General Options ⥱ CSS/JS Lazy Loading`.
270
  * (s2Member Pro) **Remote Operations API**. This release introduces two new API methods; `auth_check_user` and `get_user`. These methods (combined with those which already exist in s2Member Pro) now make s2Member Pro's Remote Operations API a pleasure to work with. For further details, please see: `Dashboard ⥱ s2Member Pro ⥱ API Scripting ⥱ Pro Remote Operations API`. Here you will find the API Key for your installation, along with several code samples.
271
  * (s2Member/s2Member Pro) **Logging**. s2Member's core payment gateway processors now log to files `gateway-core-ipn.log` and `gateway-core-rtn.log`. These log file names were changed in this release. In previous versions of s2Member these log entries were kept inside `paypal-ipn.log` and `paypal-rtn.log`.
272
  * (s2Member/s2Member Pro) **Updates**. The XML/RSS feed box for the most recent s2Member Updates (for site owners only; in the Dashboard); has been updated to our newest feed location at: `http://feeds.feedburner.com/s2member`.
278
  * (s2Member/s2Member Pro) **Compatibility**. Adding support for `$_SERVER['HTTP_AUTHORIZATION']` when s2Member is used for Remote Auth file hosting. Some servers do not support `$_SERVER['PHP_AUTH_USER']`. Instead, s2Member can get the username/password by parsing them out of `$_SERVER['HTTP_AUTHORIZATION']` when/if necessary. Fixed in this release.
279
  * (s2Member/s2Member Pro) **PHP Debug Notices.** Updating s2Member's source code to further prevent PHP debug notices when running in `WP_DEBUG` mode. This is part of an ongoing effort keep s2Member running smoothly in PHP strict mode; and to maintain conformity with WordPress standards.
280
  * (s2Member Pro) **Bug Fix**. Define `abbr_bytes()` method as static to prevent issues during automatic upgrades of s2Member Pro. Please see <https://github.com/WebSharks/s2Member/issues/37> for further details.
281
+ * (s2Member/s2Member Pro) **Backward Compatibility.** Updating calls to `get_post()`. We're adding a `NULL` argument via `$null` to prevent warnings in older releases of WordPress where an argument was required; e.g., `$null = NULL; get_post($null)`.
282
 
283
  = v131126 =
284
 
285
  * (s2Member Pro) **Google Wallet.** s2Member Pro now supports [Google Wallet for Digital Goods](https://developers.google.com/commerce/wallet/digital/).
286
 
287
+ In the past we offered support for Google "Checkout" (Google "Checkout" is no longer available as of November 2013). s2Member's previous integration with Google Checkout has now been fully updated to support Google "Wallet" for Digital Goods. To enable Google Wallet in your installation of s2Member Pro, please see: `Dashboard s2Member Pro Other Gateways Google Wallet`. For setup instructions, please see: `Dashboard s2Member Pro Google Options`.
288
 
289
  At the time of this writing, there are no KB articles related to Google Wallet at s2Member.com (yet); these will come soon. Until then, you might find it helpful to review dev notes by Jason Caldwell (Lead Developer). Please see: <https://github.com/WebSharks/s2Member/issues/19>.
290
 
291
+ We also suggest that you review the documentation in your Dashboard under: `s2Member Pro Google Options` and `s2Member Pro Google Buttons`. If you were previously using Google "Checkout", please review [this notice posted by Google](https://support.google.com/checkout/sell/answer/3080449?hl=en).
292
+ * (s2Member/s2Member Pro) **Bootstrap Compatibility.** s2Member has been updated throughout to support the [Twitter Bootstrap](http://getbootstrap.com/) CSS framework when running on a WordPress theme that's been built on Bootstrap. For instance; profile editing forms, custom registration/profile fields, s2Member Pro-Forms for checkout/registration; these will now look good on sites powered by Bootstrap. This change has no impact on functionality, only on appearance; and only IF your site is powered by Bootstrap. Throughout s2Member's HTML code we've added CSS class names that follow a Bootstrap standard. These Bootstrap classes are blended together with default structural styles that makes s2Member compatible with all WordPress themes. This way s2Member (and s2Member Pro) can produce a clean/professional appearance on just about any WordPress theme; and now Bootstrap is supported too!
293
+ * (s2Member Pro) **Configurable Emails.** We've added new configuration panels into the s2Member UI for a Modification Confirmation Email and also for the Custom Capability Confirmation Email. These are now configurable for all payment gateways integrated with s2Member Pro; e.g., AliPay, ccBill, ClickBank, Google Wallet, Authorize.Net, PayPal Standard, and PayPal Pro. These emails have always existed, but up until now customization required a WordPress filter. Now it's easier, you can customize these from the Dashboard! Please note: this feature comes only with s2Member Pro. As one example, please check your Dashboard under: `s2Member Pro PayPal Options Modification Confirmation Email`.
294
  * (s2Member/s2Member Pro) **Snippets/Redirects.** Snippets and Redirects no longer carry the s2Member Restriction Options meta box in the Post/Page editing station; there's no need for Restrictions against these two special Post Types. This change, together with the latest improvements in these two plugins: [WP Snippets](http://wordpress.org/plugins/wp-snippets/) and [WP Redirects](http://wordpress.org/plugins/wp-redirects/) (also produced by our team) offer a more powerful solution now that all of these plugins are more compatible with each other.
295
+ * (s2Member/s2Member Pro) **Backward Compatibility.** Updating calls to `get_post()`. We're adding a `NULL` argument to prevent warnings in older releases of WordPress where an argument was required; e.g., `get_post(NULL)`.
296
+ * (s2Member/s2Member Pro) **Forward Compatibility.** Adding support for the `relative` scheme in SSL filters that deal with `set_url_scheme()` in the latest versions of WordPress. This improves s2Member's "force SSL mode" where a site owner sets the Custom Field for a Post/Page; e.g., `s2member_force_ssl` is set to `yes`. This change will better support themes/plugins that use absolute relative paths together with WordPress core functions like `site_url()` and `home_url()`.
297
  * (s2Member/s2Member Pro) **PHP Debug Notices.** Updating s2Member's source code to further prevent PHP debug notices when running in `WP_DEBUG` mode. This is part of an ongoing effort keep s2Member running smoothly in PHP strict mode; and to maintain conformity with WordPress standards.
298
+ * (s2Member Pro) **Unlimited Membership Levels.** Updating the built-in software documentation for s2Member Pro to cover an edge case where a site owner many choose to exceed the recommended maximum for Membership Levels when running s2Member Pro. If you intend to use more than `100` Membership Levels (this is not recommended); but if you do, please see: `Dashboard s2Member Pro General Options Membership Levels/Labels` for the latest details on this.
299
  * (s2Member/s2Member Pro) **Dropping IE8 Support.** s2Member and s2Member Pro will no longer support IE8 in any official capacity. s2Member's HTML output and CSS files have been cleaned up; and all hacks related to IE8 have been removed. Out with the old, in with the new! We need to keep s2Member up-to-date with the latest improvements offered by IE9 and other modern browsers. While s2Member may continue to function relatively well in IE8, hacks used in the past to accomodate edge cases in this buggy browser have been removed in favor of standards compliance.
300
+ * (s2Member/s2Member Pro) **Lazy Loading CSS/JS.** s2Member now offers site owners the option to enable/disable lazy loading of CSS/JS libraries provided by the s2Member software. For further details, please see: `Dashboard s2Member General Options CSS/JS Lazy Loading`.
301
+ * (s2Member/s2Member Pro) **Bug Fix.** s2Member and s2Member Pro have both been updated to prevent spaces in a comma-delimited list of Custom Capabilities; e.g., `ccaps="music, videos"` should be `ccaps="music,videos"` please. Spaces in this list have never been allowed, but now there is better server-side validation to prevent this from happening; reducing the chance of error when a site owner configures a Button or Pro-Form shortcode with s2Member.
302
  * (s2Member/s2Member Pro) **Other Minor Bug Fixes.** Please see: <https://github.com/WebSharks/s2Member/commits/000000-dev>
303
 
304
  = v131109 =
309
  * (s2Member/s2Member Pro) **Improvement (Speed)** Lazy load s2Member's CSS file at all times. Done, this release will speed your site up for first-time visitors.
310
  * (s2Member/s2Member Pro) **Improvement (Speed)** Load s2Member's JS library in the footer if at all possible (instead of the `<head>`). Done, this will improve the speed of your site for first-time visitors.
311
  * (s2Member/s2Member Pro) **Debug Notices** Resolved all of the most obvious PHP notices when running s2Member in debug mode. This improvement impacts developers only.
312
+ * (s2Member/s2Member Pro) **Password Strength Meter** Removed dependence on `password-strength-meter` (a JavaScript library) from the WordPress core. This was causing some SSL issues for site owners. In the past it was necessary for s2Member to load an additional JS resource for registration/checkout and Pro-Forms (`password-strength-meter`). Starting with this release, s2Member handles password strength meters all by itself, thereby avoiding the additional overhead; and also the issues associated with this core functionality over SSL pages. Fixed in this release.
313
  * (s2Member/s2Member Pro) **Mobile Devices** The s2Stream shortcode (for protected audio/video files) was updated to better support mobile device playback. See also: <http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#s2stream-mobile-devices>.
314
+ * (s2Member Pro) **Checkout Options** Improving support for multiple Checkout Options. When a customer changes to a new Checkout Option by selecting an option from the drop-down menu in a Pro-Form; this action will now result in a hash jump back to the location of the Pro-Form on any given page; instead of to the top of the page, which could potentially result in a confusing experience on some sites (depending on the implementation). Aside: for developers, it's helpful to know that all s2Member Pro-Forms now have a hashable ID `#s2p-form` that is cleaner than the longer (product-specific) IDs associated with Pro-Forms; e.g., `s2member-pro-paypal-form...`, etc. If you need to hash a Pro-Form, please use the more general `#s2p-form` on the end of a URL. This will take a customer directly to that Pro-Form in the context of any given page.
315
 
316
  = v131026 =
317
 
321
  = v131025 =
322
 
323
  * (s2Member) **WordPress v3.7 Compatibility** s2Member updated to support WordPress v3.7. s2Member remains compatible with WP v3.3 (or higher).
324
+ * (s2Member Pro) **New Feature: Simultaneous Login Monitoring** Available only w/ s2Member Pro. s2Member Pro has been updated to support configurations that limit the number of simultaneous logins a single username can receive. For further details, please see: `Dashboard s2Member Restriction Options Simultaneous Login Restrictions` (when s2Member Pro is installed).
325
  * (s2Member) **Post Restrictions (#3)** Adding support for `all-[post type]` in addition to the existing `all-[post type]s` (plural) currently supported by s2Member's Restriction Options for Posts. This makes it possible for a site owner to type only the Post Type after the keyword prefix `all-`; and excluding the plural `s` in cases when this is necessary.
326
  * (s2Member) **Documentation Update (#3)** Adding note in Download Options panel regarding `raw` shortcode tags around Shortcodes when using the inFocus theme. See also [this thread](http://mysitemyway.com/support/topic/infocus-adding-tags-into-plugin-content) for further details.
327
  * (s2Member) **Documentation Cleanup (#12)** General cleanup in several s2Member panels to improve inline documentation that comes w/ the software. Branding improvements, padding adjustments, and subtle textual changes.
330
  * (s2Member) **Quick Start Video (#10)** Adding Quick Start playlist to Quick Start section.
331
  * (s2Member) **UI (#6)** Reducing padding around section headers in s2Member option panels to reduce the amount of space these consume.
332
  * (s2Member Pro) **SSL** Forcing all automatic updates of s2Member Pro to occur over SSL for improved security.
333
+ * (s2Member) **Login Welcome Page** Improving support for new feature (force HTTP redirection). See: `s2Member General Options Login Welcome Page` for details on how this feature works.
334
  * (s2Member) **Bug Fix: Registration/Profile Fields** Adding space between checkboxes and their labels.
335
  * (s2Member) **ClickBank IPN Filter** Adding a new filter to s2Member's ClickBank IPN handler for developers integrating s2Member in creative ways: `c_ws_plugin__s2member_pro_clickbank_notify_handles_completions`. Defaults to a TRUE value. Forcing this filter to a FALSE value will prevent s2Member from handling term completions via IPN communication; in cases where it's preferred that a site owner deal with this specific scenario manually.
336
  * (s2Member) **Conformity** Updating calls to `$wpdb->escape` changing to `esc_sql` to conform w/ WordPress standards.
337
+ * (s2Member Pro) **Compatibility: Checkout Options** Improving theme support for Checkout Options created using Pro-Forms by wrapping other Pro-Form Shortcodes. Some site owners reported line break injections in the previous verison. Fixed in this release.
338
  * (s2Member) **See also: s2Member Repo** https://github.com/WebSharks/s2Member/commits/000000-dev
339
 
340
  = v130816 =
342
  * (s2Member Pro) **Compatibility, ClickBank (#467)** Improving support for ClickBank PitchPlus Upsell Flows. Please see [this thread](http://www.s2member.com/forums/topic/clickbank-buttons-not-working/#post-55725) for further details.
343
  * (s2Member/s2Member Pro) **User Search on Multisite Networks (#468)** User search functionality was partially broken for Child Blogs in a Multisite Network after some improvements were implemented in s2Member® v130731. The issue has now been corrected in this release for Multisite Networks. For further details, please see [this thread](http://www.s2member.com/forums/topic/user-search-no-longer-working/#post-55778).
344
  * (s2Member/s2Member Pro) **Z-Index in Menu Pages (#461)** Stacking order against a WordPress® installation running a Dashboard with a collapsed sidebar menu (left side) was causing some UI problems. Fixed in this release.
345
+ * (s2Member/s2Member Pro) **SSL Compatibility (#437)** Adding a new option in the `s2Member® General Options Login Welcome Page` section. The default value for this new option is always `yes`. However, the default functionality can be turned off (if you prefer). This new option allows site owners to better integrate with a core WordPress® feature commonly referred to as `FORCE_SSL_LOGIN` and/or `FORCE_SSL_ADMIN`. This new feature can be used, or not. It is intended mainly for site owners running w/ `FORCE_SSL_LOGIN` and/or `FORCE_SSL_ADMIN`.
346
+ * (s2Member/s2Member Pro) **Login/Registration Design Option (#437)** Adding a new option in the `s2Member® General Options Login/Registration Design` section. This new option (found at the bottom of `s2Member® General Options Login/Registration Design`) allows a site owner to show/hide the `Back To Home Page` link at the bottom of the default WordPress® Login/Registration system. This can be useful for site owners running w/ `FORCE_SSL_LOGIN` and/or `FORCE_SSL_ADMIN`; where they would prefer NOT to link customers back to the main site under a default HTTPS link; but rather, create their own link and gain better control over this area of their site.
347
+ * (s2Member/s2Member Pro) **Videos (#467)** Updating internal documentation found in `Dashboard s2Member® Quick-Start`. Specifically, the video player here was integrated with an older version of the YouTube® API and was not working properly. Fixed in this release.
348
 
349
  = v130802 =
350
 
351
+ * (s2Member Pro) **Compatibility, WordPress® v3.6** Updating s2Member® Pro-Form templates and their underlying CSS. This update improves their appearance against the Twenty Thirteen theme that comes with WordPress® v3.6. Specifically, some of the Pro-Form buttons were a little out of place in this new default theme. Fixed in this release.
352
+ * (s2Member Pro) **Compatibility, Checkout Options (#443)** Revision 3. Updating this feature to support a wider variety of WordPress® configurations and content filters. This update also resolves an empty `desc=""` attribute error reported by some site owners. Feature description... It is now possible to build dropdown menus offering your customers a variety of options using a Single Pro-Form. This is accomplished quite easily using Shortcodes. For full details and examples, please check this section of your Dashboard in the latest release. See: `s2Member® PayPal® Pro-Forms Wrapping Multiple Shortcodes as "Checkout Options"`. NOTE: this works for PayPal® Pro-Forms, and ALSO for Authorize.Net Pro-Forms.
353
 
354
  = v130801 =
355
 
356
+ * (s2Member Pro) **New Feature; Checkout Options (#403)** Revision 2. Updating documentation on this new feature to prevent conufusion for site owners. s2Member® Pro now supports "Checkout Options". It is now possible to build dropdown menus offering your customers a variety of options using a Single Pro-Form. This is accomplished quite easily using Shortcodes. For full details and examples, please check this section of your Dashboard in the latest release. See: `s2Member® PayPal® Pro-Forms Wrapping Multiple Shortcodes as "Checkout Options"`. NOTE: this works for PayPal® Pro-Forms, and ALSO for Authorize.Net Pro-Forms.
357
 
358
  = v130731 =
359
 
360
+ * (s2Member Pro) **New Feature; Checkout Options (#403)** s2Member® Pro now supports "Checkout Options". It is now possible to build dropdown menus offering your customers a variety of options using a Single Pro-Form. This is accomplished quite easily using Shortcodes. For full details and examples, please check this section of your Dashboard in the latest release. See: `s2Member® PayPal® Pro-Forms Wrapping Multiple Shortcodes as "Checkout Options"`. NOTE: this works for PayPal® Pro-Forms, and ALSO for Authorize.Net Pro-Forms.
361
+ * (s2Member Pro) **Free Checkout (#403)** It is now possible to offer a 100% free checkout experience using any of s2Member's Pro-Form Shortcodes. In previous releases of s2Member® it was not possible to set the `ra=""` Attribute to a zero dollar amount. Now it is! This works for PayPal® Pro-Forms, and also for Authorize.Net Pro-Forms.
362
+ * (s2Member Pro) **100% Off Coupons (#403)** It is now possible to offer a 100% off coupon. This works for PayPal® Pro-Forms, and also for Authorize.Net Pro-Forms. See: `s2Member® Pro Coupon Codes` for details and examples.
363
+ * (s2Member Pro) **Expiration Date Dropdowns (#428)** This release improves all s2Member® Pro-Form templates by adding dropdown menus for the customer's credit card expiration month/year instead of the simple text input field used in previous releases.
364
+ * (s2Member/s2Member Pro) **MySQL Wait Timeout (#349)** s2Member now automatically increases the MySQL `wait_timeout` to `300` seconds during s2Member processing routines. Reason for increase: should any 3rd party service API result in unexpected connection timeouts (such as PayPal, Authorize.Net, Amazon, MailChimp, AWeber, etc); this may cause a delay that could potentially exceed the default `wait_timeout` of `30` seconds on the MySQL resource handle that is global to all of WordPress. Increasing `wait_timeout` before transaction processing will decrease the chance of failure after a timeout is exceeded. Among other things, this resolves an elusive bug where there are mysterious 404 errors after checkout under the right scenario (e.g., when an unexpected timeout occurs). This may also resolve problems associated w/ some mysterious reports where emails were not sent during s2Member's attempt to complete post-processing of a transaction (and/or where other portions of post-processing failed under rare circumstances).
365
+ * (s2Member/s2Member Pro) **Alternative Views (#300)** This release gives s2Member® the ability to hide protected content in widgets that list protected WordPress® Pages. This is a new Alternative View in the Dashboard. See: `s2Member® Restriction Options Alternative Views` for further details please.
366
+ * (s2Member/s2Member Pro) **Documentation Update (#350)** Subtle improvements to the built-in documentation pertaining to s2Member's Automatic List Transitioning feature in the Dashboard. See: `s2Member® API/List Servers Automatic Unsubscribes` for further details please.
367
  * (s2Member/s2Member Pro) **Bug Fix (#387)** In s2Member® Only mode, a recursive scan for the WordPress® `/wp-load.php` file was failing somtimes when/if a custom directory was configured for plugins. Fixed in this release. See [this thread](http://www.s2member.com/forums/topic/problem-with-wordpress-folder-search-code/) for further details.
368
  * (s2Member/s2Member Pro) **Bug Fix (#418)** Fixed incorrect pagination of user search results in the Dashboard.
369
  * (s2Member/s2Member Pro) **Bug Fix (#418)** Fixed slow query against user searches in the Dashboard.
371
 
372
  = v130617 =
373
 
374
+ * (s2Member/s2Member Pro) **IP Restrictions (#148)** It is now possible to introduce a custom template file that controls the error message displayed when/if a user breaches security by exceeding your maximum unique IP addresses; as configured under `s2Member Restriction Options Unique IP Restrictions`. If you would like to use a custom template for this message, please copy the default template file from `/s2member/includes/templates/errors/ip-restrictions.php` and place this file into your active WordPress® theme directory (or into the `/wp-content/` directory if you prefer). s2Member will automatically find your custom template in one of these locations; and s2Member will use your custom template instead of the built-in default.
375
+ * (s2Member Pro) **Bug Fix (#302)** Updating Authorize.Net Pro-Form Generator to support max days of `365` instead of `7`.
376
  * (s2Member Pro) **Amazon S3 Secret Access Keys (#321)** Updating UI configuration panel to better explain what a Secret Access Key is; and adding a note about Secret Access Keys for Amazon S3 integration. Amazon® is deprecating Secret Access Keys, but they ARE still required for digitally signed URLs. This update changes nothing in s2Member's functionality. It simply adds some additional detail to a configuration field that will assist site owners integrating s2Member w/ Amazon S3 for the first time.
377
  * (s2Member/s2Member Pro) **Translations (#317)** Updating `/s2member/includes/translations/translations.txt` (README file). Adding blurp about fuzzy translation entries in existing PO files that translate s2Member. This contains some additional tips on how to update existing PO files upon each release of s2Member and/or s2Member Pro.
378
  * (s2Member/s2Member Pro) **Bug Fix (#321)** Fixing bug in `/s2member/includes/templates/cfg-files/s2-cross-xml.php` for S3 Buckets (resulting in `crossdomain.xml`). This file contained a parse error and was causing some problems for site owners integrating Adobe Flash content served via Amazon S3. Fixed in this release.
379
+ * (s2Member Pro) **PayPal Pro-Forms (#315)** Adding note in the Dashboard here (`s2Member PayPal Pro-Forms Shortcode Attributes Explained`). In the list of Shortcode Attributes we are adding a note regarding max character length for the `desc=""` Attribute in a PayPal Pro-Form Shortcode. This can be as long as you like. However, all descriptions passed through PayPal® APIs are truncated automatically to 60 characters max (e.g., the maximum allowed length for PayPal® descriptions is 60 characters). Nothing new here, but we thought it would be a good idea to clarify this behavior in the documentation. Updated in this release.
380
+ * (s2Member Pro) **PayPal Pro-Forms (#312)** Preventing the use of multiple Pro-Forms in the same Post/Page. This has never been possible, it is known to break the functionality of s2Member Pro-Forms. Please limit Pro-Form Shortcodes to ONE for each Post/Page; and do NOT attempt to use more than one Pro-Form Shortcode on the same Post/Page (at the same time). In this release we have added a friendly JavaScript alert/warning for site owners that attempt this, so that problems and confusion can be avoided in this unlikely scenario.
381
 
382
  = v130513 =
383
 
384
  * (s2Member/s2Member Pro) **s2Stream Shortcode Bug Fix (#256)** Fixing a bug first introduced in the previous release where we added support for `player_aspectratio`. This quick update corrects the PHP parse error at line #154 of `sc-files-in.inc.php`. It also corrects the behavior of the `player_height=""` and `player_aspectratio=""` Shortcode Attributes for the `s2Stream` Shortcode. Many thanks to everyone that reported this bug.
385
  * (s2Member Pro) **Codestyling Localization** Removing symlink creator for Codestyleing Localization compatibility. There have been some reports of problems during WordPress® automatic upgrades (when/if the symlink exists). Until we can find a way to avoid this, we're disabling the automatic symlink generator. If you're running the Codestyling Localization plugin together with s2Member Pro, you will need to create the symlink yourself if you want to make s2Member fully compatible. Please create a symlink here: `/wp-content/plugins/s2member/s2member-pro` that points to the s2Member Pro directory: `/wp-content/plugins/s2member-pro`. See notes in previous changelog for further details on this.
386
 
387
+ **IMPORTANT NOTE:** If you upgraded previously to v130510 (and you ran the Codestyling Localization plugin together with s2Member® v130510—at any time); please delete this symlink via FTP: `/wp-content/plugins/s2member/s2member-pro`. Please do this BEFORE attempting an automatic upgrade via WordPress®.
388
 
389
  If you missed this note and you've already attempted an automatic upgrade, you will have trouble. Here's how to correct the problem.
390
 
395
 
396
  = v130510 =
397
 
398
+ * (s2Member Pro) **Authorize.Net UK (and Other Currencies) (#104)** Adding support for Authorize.Net UK and other currencies too. s2Member Pro now officially supports Authorize.Net UK Edition. It is now possible to change your Authorize.Net Pro-Form Shortcode Attribute `cc="USD"` to one of these values: `cc="USD"`, or `cc="CAD"` or `cc="EUR"` or `cc="GBP"`. For further details, please see: `Dashboard Authorize.Net Pro-Forms Shortcode Attributes (Explained)`.
399
+ * (s2Member Pro) **ClickBank Skins (#227)** Adding support for the `cbskin=""` Shortcode Attribute. For further details, please see: `Dashboard ClickBank Buttons Shortcode Attributes (Explained)`.
400
+ * (s2Member Pro) **ClickBank PitchPlus Upsell Flows (#227)** Adding support for ClickBank PitchPlus Upsell Flows via new Shortcode Attributes: `cbfid=""`, `cbur=""`, `cbf="auto"`. s2Member Pro now officially supports ClickBank PitchPlus Upsell Flows. We support PitchPlus Basic and PitchPlus Advanced too. For further details, please see: `Dashboard ClickBank Buttons Shortcode Attributes (Explained)`.
401
  * (s2Member/s2Member Pro) **Codestyling Localization** Adding automatic symlink creator for improved compatibility with the CodeStyling Localization plugin. A symlink is created automatically whenever the CodeStyling Localization plugin is installed, and s2Member® Pro is installed as well. The symlink allows the CodeStyling Localization plugin to scan files from the s2Member® Pro directory too; instead of only scanning the s2Member® Framework directory. s2Member and s2Member Pro are now both compatible with the Codestyling Localization plugin (optional).
402
  * (s2Member/s2Member Pro) **Custom Templates w/ s2Stream Shortcode** Adding support for custom templates to be used in conjunction with the `s2Stream` Shortcode. It is now possible to take the default player templates from `/s2member/includes/templates/players/` and put these files inside your own WordPress® theme directory (or inside the `/wp-content/` directory). s2Member will automatically find your template files in these locations. Your custom template files will then be used instead of the built-in defaults.
403
  * (s2Member/s2Member Pro) **Bug Fix (#59)** Resending a password to a User from the Dashboard (while changing the User's email address at the same time); resulted in the email being sent to the previous email address instead of the new one. Fixed in this release.
421
  For further details, please see [this thread](http://www.s2member.com/forums/topic/paypal-expired-security-token/page/2/) (or monitor the [s2Member KB](http://www.s2member.com/kb/) for new articles on this subject).
422
  * (s2Member Pro) **Payflow API Docs** Updating `s2m-pro-extras.zip` to include the latest versions of the PayPal Pro (Payflow Edition) APIs. s2Member Pro customers can download this optional ZIP file from their My Account page at s2Member.com. These are extras only, they are NOT part of the s2Member application.
423
  * (s2Member Pro) **Google Checkout Bug Fix (#214)** Updating s2Member's Google Checkout integration to properly support the `rrt` Shortcode Attribute. Fixed in this release. For further details please see [this thread](http://www.s2member.com/forums/topic/google-recurring-problem/#post-48218).
424
+ * (s2Member Pro) **Username Validation (#246)** Now forcing user input (during Pro-Form registration) to lowercase on Multisite Networks to prevent unnecessary validation errors during checkout (saving a customer time). Also, s2Member now validates a customer's Username before it is passed through `sanitize_user()` (a core WordPress® function). This prevents confusion for a customer where certain characters were stripped out automatically, causing them problems when attempting to log in for the first time (e.g., the customer thinks their Username is `john~doe`; when it is actually `johndoe` because WordPress (when running a Multisite Network) removes anything that is NOT `a-z0-9 _.-@` (and s2Member removes whitespace as well).
425
 
426
  = v130406 =
427
 
450
  = v130220 =
451
 
452
  * (s2Member Pro) **Feature Enhancement/User Exportation (#33)** Adding support for UTF-8 BOM in CSV User export files. Now a configurable option during User/Member Exportation.
453
+ * (s2Member/s2Member Pro) **Feature Enhancement/Emails (#21)** Adding additional Replacement Codes for New User Email Notifications (for both the User/Member Notification and also for the Administrator Notification). The following Replacement Codes are now possible: `%%role%%`, `%%label%%`, `%%level%%`, `%%ccaps%%`. Also adding four new Filters for developers. These include: `ws_plugin__s2member_welcome_email_sbj`, `ws_plugin__s2member_welcome_email_msg`, `ws_plugin__s2member_admin_new_user_email_sbj`, `ws_plugin__s2member_admin_new_user_email_msg`. See `Dashboard General Options Email Configuration` for further details.
454
+ * (s2Member/s2Member Pro) **Feature Enhancement/Emails (#30)** Adding support for PHP tags in the following emails: New User Notification, Administrative New User Notification, Signup Confirmation Email, Specific Post/Page Confirmation Email. See the relevant sections in your Dashboard for further details. Such as: `s2Member® General Options Email Configuration` and `s2Member® PayPal® Options Signup Confirmation Email`.
455
+ * (s2Member/s2Member Pro) **Feature Enhancement/Shortcodes (#23)** Adding support for the `lang=""` Attribute in PayPal Buttons, PayPal Pro-Forms, and in Google Checkout Buttons. This is a bit different from the existing `lc=""` value. The `lc=""` value controls the interface at PayPal, while the `lang=""` value controls the language of the Standard and/or Express Checkout Button itself (with respect to s2Member®). For further details, please see: `Dashboard PayPal Buttons (or Pro-Forms) Shortcode Attributes (Explained)`.
456
  * (s2Member/s2Member Pro) **Bug Fix** Fixing bug in User Access Package. Now checking if `$cap_enabled` also is `TRUE`; just in case another plugin or hack file attempts to disable Custom Capabilities without removing them. Not likely, but we can support this easily with a quick update in this release. Note... this has no impact on s2Member's existing functionality. Custom Capabilities continue to work just as they always have.
457
+ * (s2Member/s2Member Pro) **Feature Enhancement/Logging** Adding new logger. Logs to file `reg-handler.log`. Includes all User/Member registrations handled by s2Member® (either directly or indirectly). Only if logging is enabled. For further details, please check your Dashboard here: `s2Member® Log Files (Debug)`.
458
+ * (s2Member/s2Member Pro) **Feature Enhancement/EOTs (#29)** Adding UI option for EOT Grace Time. For further details, please see: `Dashboard PayPal Options Automatic EOT Behavior`. Also adding a new Filter for developers: `ws_plugin__s2member_eot_grace_time`.
459
+ * (s2Member/s2Member Pro) **Feature Enhancement/EOTs** Adding UI option for EOT Custom Capability Removal. For further details, please see: `Dashboard PayPal Options Automatic EOT Behavior`. Also adding a new Filter for developers: `ws_plugin__s2member_remove_ccaps_during_eot_events`.
460
+ * (s2Member/s2Member Pro) **Feature Enhancement/s2Stream Shortcode (#32)** Adding additional support for JW Player™ Captions, Titles, Descriptions, and Media IDs (i.e., `player_title=""`, `player_description=""`, `player_mediaid=""`, `player_captions=""`). Please check the Shortcode Attributes tab in [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes) for further details.
461
 
462
  = v130214 =
463
 
464
  * **(Maintenance Release) Upgrade immediately.**
465
  * (s2Member/s2Member Pro) **Log Viewer (#16)** Additional log file descriptions have been added to the Dashboard, along with some other UI enhancements in this section.
466
  * (s2Member/s2Member Pro) **Bug Fix (#18)** Usernames consisting of all numeric values were not always being redirected to the Login Welcome Page upon logging in, even when s2Member® was configured to do so. Fixed in this release. Discussed in [this thread](http://www.s2member.com/forums/topic/subscribers-not-taken-to-welcome-page/page/2/#post-41663).
467
+ * (s2Member Pro) **Coupon Codes (#19)** Adding new Replacement Codes: `%%full_coupon_code%%`, `%%coupon_code%%` and `%%coupon_affiliate_id%%`. These are now available in all API Tracking Codes, in all Custom Return URLs for Pro-Forms, and in most API Notifications.
468
  * (s2Member Pro) **Coupon Codes (#19)** Deprecating the `%%affiliate_id%%` Replacement Code for tracking Affiliate Coupon Codes in favor of `%%coupon_affiliate_id%%`.
469
  * (s2Member/s2Member Pro) **Last Login Time** Improving readability of Last Login Time in list of Users/Members.
470
  * (s2Member/s2Member Pro) **Compatibility** Improving support for WordPress® v3.6-alpha with respect to `tabindex` values on `/wp-login.php`.
473
  = v130213 =
474
 
475
  * **(Maintenance Release) Upgrade immediately.**
476
+ * (s2Member Pro) **Compatibility (#13)** PayPal® Payments Pro, PayPal® Payments Pro (Payflow Edition), and Authorize.Net. s2Member® Pro now supports a recurring interval of Semi-Yearly (e.g., charges occur every six months). This has always been possible by manipulating Shortcode Attribues, but it's now officially supported by Pro-Form Generators in your Dashboard—which come with s2Member® Pro.
477
  * (s2Member Pro) **Compatibility (#13)** ClickBank® Recurring Products. ClickBank® has started allowing a Weekly recurring interval and stopped allowing Yearly. s2Member® has been updated in this release to support a Weekly recurring interval with ClickBank®; and to remove the Yearly option in the ClickBank® Button Generator.
478
  * (s2Member Pro) **Compatibility (#13)** PayPal® Payments Pro (Payflow Edition). PayPal® Payments Pro (Payflow Edition) has started allowing a Daily recurring interval. s2Member® has been updated in this release to support a Daily recurring interval with PayPal® Payments Pro (Payflow Edition). Daily recurring intervals remain possible with PayPal® Pro accounts that do not include the additional Payflow API. This change simply adds official support for Daily recurring billing with PayPal® Payments Pro (Payflow Edition).
479
  * (s2Member) **Debugging Assistance** Updating s2Member's PayPal® PDT/Auto-Return handler to better handle scenarios where a site owner is missing a PayPal® PDT Identity Token in their s2Member® configuration, or has incorrectly set the `custom=""` Shortcode Attribute in Payment Buttons generated with s2Member®. Administrative notices are now displayed in the Dashboard when/if this occurs and s2Member® can catch the issue during post-processing of a transaction.
480
  * (s2Member/s2Member Pro) **General Code Cleanup** Removing all `/**/` markers in the s2Member® codebase. These were used in conjunction with PolyStyle® code formatting tools to preserve line breaks in the code. The WebSharks™ development team no longer uses PolyStyle®, making these obsolete now. Removed in this release to improve readability for developers.
481
  * (s2Member/s2Member Pro) **General Code Cleanup** Removing all unnecessary uses of PHP's `eval()` function in s2Member's codebase. These were used to keep repetitive code all in a single line; part of a standard the WebSharks™ development team is now moving away from. Removed in this release to improve readability for developers; and to prevent unnecessary confusion.
482
  * (s2Member/s2Member Pro) **Auto-EOT System** Updated s2Member's Auto-EOT System. s2Member® now leaves an additional note behind after a demotion, which references the Paid Subscr. Gateway and Paid Subscr. ID values before the demotion occurred. This way there is a better reference left behind after an automatic demotion occurs.
483
+ * (s2Member/s2Member Pro) **Searching Users** Updating search function in list of Users (i.e., `Dashboard Users [Search Box]`) to include the Administrative Notes field when searching for Users. This allows references to old Paid Subscr. IDs in the Administrative Notes field to be considered when searching Users/Members.
484
  * (s2Member/s2Member Pro) **Last Login Time** Adding new User Option value (tracked by s2Member®). This option value tracks the last time each User/Member logged into your site. Ex: `get_user_option("s2member_last_login_time")`.
485
  * (s2Member/s2Member Pro) **Last Login Time** Adding new User data column to list of Users in the Dashboard: `Last Login Time`.
486
  * (s2Member/s2Member Pro) **Last Login Time** Adding new API Function: [`s2member_last_login_time()`](http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/).
487
  * (s2Member/s2Member Pro) **ezPHP** Updated all internal documentation references that pointed to Exec-PHP or the PHP Execution plugin as recommendations for developers that wish to integrate PHP tags into Posts/Pages/Widgets. These old references now point to the [ezPHP](http://www.s2member.com/kb/ezphp-plugin/) plugin by s2Member® Lead Developer: Jason Caldwell. s2Member® remains compatible with other PHP plugins, but we recommend [ezPHP](http://www.s2member.com/kb/ezphp-plugin/) for the best compatibility with both s2Member® and WordPress® itself.
488
+ * (s2Member/s2Member Pro) **Simple Shortcode Conditionals** Adding a [Simple Shortcode Conditionals](http://www.s2member.com/kb/simple-shortcode-conditionals/) section to `s2Member® Restriction Options` in the Dashboard. This way more site owners will be aware of this feature from the start.
489
+ * (s2Member/s2Member Pro) **Login/Registration Design** Login/Registration Design with s2Member® is now optional (e.g., this feature can be disabled now—if you prefer). See: `Dashboard s2Member® General Options Login/Registration Design`. This feature is enabled by default on all s2Member® installations.
490
  * (s2Member/s2Member Pro) **Inline Documentation** Adding more links to KB articles throughout the Dashboard area.
491
+ * (s2Member/s2Member Pro) **Inline Documentation** Updating all spaced parenthesis like `( something... )` to remove the grammatical errors—by removing the extra spaces inside these brackets.
492
+ * (s2Member/s2Member Pro) **Inline Documentation** Removing all references to PriMoThemes and/or primothemes.com within the application itself. PriMoThemes is now s2Member® (as of Jan 2012—it's been awhile; so time to remove these obviously).
493
  * (s2Member/s2Member Pro) **Inline Documentation** Adding link to "more updates..." in the Dashboard, pointing to the s2Member® KB. Increasing number of recent KB udpates from 3 up to 5. These are visible from any s2Member® page in the Dashboard (top of the right-hand column).
494
+ * (s2Member/s2Member Pro) **Inline Documentation** Adding [s2Member® Pro](http://www.s2member.com/pro/) (a recommended upgrade) to the Quick-Start Guide for s2Member®—in the Dashboard.
495
+ * (s2Member/s2Member Pro) **Inline Documentation** Adding Troubleshooting section to the Quick-Start Guide for s2Member®—in the Dashboard.
496
+ * (s2Member/s2Member Pro) **Inline Documentation** Adding Perfect Theme section to the Quick-Start Guide for s2Member®—in the Dashboard.
497
+ * (s2Member/s2Member Pro) **Inline Documentation** Adding video tutorial to the `Dashboard s2Member® API / Scripting Custom Capabilities` section.
498
+ * (s2Member/s2Member Pro) **Logging Functionality** Adding an s2Member® Log Viewer to the Dashboard for all site owners; and also for s2Member® Support Reps to use when running diagnostics. See: `Dashboard s2Member® Log Files (Debug)` for further details.
499
+ * (s2Member/s2Member Pro) **Logging Functionality** Logging routines are now enabled by default on all new installations of s2Member®. Existing installations of s2Member® are advised to enable logging, by visiting this section of your Dashboard. See: `s2Member® PayPal® Options (or Authorize.Net, ClickBank, etc) Account Details Logging`.
500
+ * (s2Member/s2Member Pro) **Logging Functionality** Additional logging routines that will track all s2Member® HTTP communication within WordPress® is now enabled by default. This new log file will be located inside `/wp-content/plugins/s2member-logs`. It is named: `s2-http-api-debug.log`. See: `Dashboard s2Member® Log Files (Debug)` for further details.
501
+ * (s2Member/s2Member Pro) **Logging Functionality** Additional logging routines that will track *all* HTTP communication within WordPress® are now possible (these are quite extensive). See: `Dashboard s2Member® Log Files (Debug) Logging Configuration` for further details. This more extensive logging is disabled by default; it must be enabled by a site owner. For debugging only—this should NEVER be enabled on a live site.
502
  * (s2Member/s2Member Pro) **Logging Functionality** Adding date/time to all log entries maintained by s2Member®.
503
+ * (s2Member/s2Member Pro) **GZIP Conflicts** Adding additional lines of defense against GZIP conflicts during file downloads, with calls to `@apache_setenv("no-gzip", "1")` in other areas—not just during public file downloads (e.g., also during User/Member exporations, log file downloads, etc).
504
  * (s2Member/s2Member Pro) **GZIP Conflicts** Adding an additional line of defense against GZIP conflicts during file downloads, with this line now appearing in the `.htaccess` file snippet added by the s2Member® software application: `RewriteCond %{QUERY_STRING} (^|\?|&)no-gzip\=1`.
505
  * (s2Member/s2Member Pro) **GZIP Conflicts** Adding an additional line of defense against GZIP conflicts during User/Member exporations, log file downloads, and other downloads that come straight from the Dashboard area to site owners via web browsers. s2Member® now sends `Content-Encoding: none` to prevent Apache's `mod_deflate` from interfering with s2Member® under these special scenarios. A `Content-Encoding: none` header value is technically invalid, but it's known to prevent issues with `mod_deflate`. Since a `Content-Encoding: none` header value is technically invalid, s2Member® does NOT implement this during public file downloads; where we need to provide wider support for a long list of devices that may choke on this incorrect value. This is only implemented for site owners in the administrative areas of WordPress; and only for file downloads related to CSV export files and logs.
506
  * (s2Member/s2Member Pro) **Bug Fix** Fixed incorrect `preg_split` limit against `$paypal['item_number']` in IPN handler for `subscr_payment` and `subscr_cancel` transaction types. Doesn't appear to have affected anything negatively, but it was wrong none the less. Fixed in this release.
507
  * (s2Member/s2Member Pro) **Bug Fix** Fixed incorrect handling of a single opt-in checkbox on BuddyPress registration forms, which was not being wrapped with s2Member's BuddyPress container divs at all times. A symptom of this bug was to see a checkbox on your BuddyPress registration that was out of alignment or out of position. Fixed in this release.
508
+ * (s2Member/s2Member Pro) **Compatibility** Updated all of s2Member's IPN handlers to accept `$_REQUEST` data for Proxy-related variables like `s2member_paypal_proxy_return_url`. This allows s2Member® itself to use `$_POST` variables for Proxy-related variables; and it further reduces the likelihood of 403 Forbidden errors caused by [paranoid Mod Security configurations](http://www.s2member.com/kb/mod-security-random-503-403-errors/). One issue this should help to correct, is a mysterious case where a `success=""` Shortcode Attribute is not working as you might expect. This can be caused by [paranoid Mod Security configurations](http://www.s2member.com/kb/mod-security-random-503-403-errors/) at places like HostGator®, because a URL is passing through a query string. This release will help to prevent this from becoming a problem, because `success=""` URLs will be passed through `$_POST` variables now in all Pro-Form integrations.
509
 
510
  = v130207 =
511
 
519
  = v130203 =
520
 
521
  * **(New Release) Please read this changelog for important details.**
522
+ * (s2Member Pro) **Remote Ops API (`create_user`)** s2Member® Pro's Remote Operations API, for the `create_user` Operation has been updated to support a new specification: `modify_if_login_exists`. For further details, please check your s2Member® Pro Dashboard here: `s2Member® API / Scripting Remote Operations API`.
523
+ * (s2Member Pro) **Remote Ops API (`modify_user`,`delete_user`)** s2Member® Pro's Remote Operations API has been updated to support two additional Operations: `update_user` and `delete_user`. For further details on these new Operations, please check your s2Member® Pro Dashboard here: `s2Member® API / Scripting Remote Operations API`.
524
  * (s2Member Pro) **Remote Ops API (`init` hook priority)** s2Member® Pro's Remote Operations API has been updated to prevent conflicts when running in concert with BuddyPress v1.6.4+. Hook priority now running at default value of `11`, right after BuddyPress v1.6.4 at hook priority `10`.
525
+ * (s2Member/s2Member Pro) **s2Stream Shortcode (#88)** s2Member® now supports JW Player® license keys (for the professional edition) using Shortcode Attribute `player_key=""` (or they can be specified sitewide via JavaScript provided by Longtail Video—optional). See [this discussion](http://www.s2member.com/forums/topic/jwplayer-shortcode-for-poster-not-working/#post-40435). See also: [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes).
526
  * (s2Member/s2Member Pro) **s2Stream Shortcode (#88)** Bug fix. The s2Stream Shortcode was not working properly (with respect to a specific Shortcode Attribute: `player_image=""`). Fixed in this release. See [this discussion](http://www.s2member.com/forums/topic/jwplayer-shortcode-for-poster-not-working/#post-40128). See also: [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes).
527
+ * (s2Member Pro) **User Exportation (#89)** s2Member® Pro's User Exportation now includes separate data columns for each Custom Registration/Profile Field that you've configured with s2Member®. Also, s2Member® Pro will now include ALL Custom Registration/Profile Fields (even if there is no value associated with certain Fields, for specific Users/Members—e.g., empty column values will now be included by s2Member® Pro). This provides a more consistent/readable CSV export file; a major improvement. Discussed in [this KB article](http://www.s2member.com/kb/importing-updating-users/#custom-registration-profile-fields).
528
+ * (s2Member Pro) **User Importation (#89)** s2Member® Pro's User/Member Import format changed in this release (with respect to Custom Registration/Profile Fields only). If you are importing Custom Registration/Profile Fields, please review [this KB article](http://www.s2member.com/kb/importing-updating-users/#custom-registration-profile-fields) before you import new Users/Members or mass update any existing Users/Members. ALSO NOTE: User/Member CSV Export Files generated by previous versions of s2Member® Pro (if they contained any Custom Registration/Profile Fields) will NOT be compatible with this latest release (e.g., you should NOT attempt to re-import those old files in an effort to mass update existing Users/Members). Please generate a new User/Member CSV Export File in the latest release of s2Member® Pro before attempting to edit and/or mass update existing Users/Members with applications like MS Excel or OpenOffice.
529
 
530
  = v130123 =
531
 
533
  * (s2Member/s2Member Pro) **s2Stream Shortcode (#78)** s2Member® now supports JW Player® license keys using Shortcode Attribute `player_key=""`. See [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/) please. Also discussed in [this thread](http://www.s2member.com/forums/topic/new-jw-player-6-s2-video-audio-shortcodes/#post-38768).
534
  * (s2Member/s2Member Pro) **s2Stream Shortcode (#79)** s2Member® now supports JW Player® [Advanced Option Blocks](http://www.longtailvideo.com/support/jw-player/28839/embedding-the-player) using Shortcode Attribute `player_option_blocks=""`. Example: `player_option_blocks="sharing:{}"`. See [this KB article](http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/) please. Also discussed in [this thread](http://www.s2member.com/forums/topic/new-jw-player-6-s2-video-audio-shortcodes/#post-38768).
535
  * (s2Member Pro) **User Exportation (#80)** s2Member® Pro User Exportation now occurs with MySQL `ORDER BY ID`, instead of no `ORDER BY` at all. This helps to prevent confusion and buggy behavior. Discussed in [this thread](http://www.s2member.com/forums/topic/user-export-not-working-properly/#post-39123).
536
+ * (s2Member Pro) **User Exportation (#81)** s2Member Pro's User Exportation now supports the exporation of up to `1000` User/Member table rows at once. Of course it remains possible to export ALL of your Users/Members with s2Member® Pro. All we've done here is bump the default limit from `250` up to `1000` at a time. In addition, there is a new Filter making it possible to extend this limit further on servers that can handle it. Use Filter: `ws_plugin__s2member_pro_export_users_limit` if you would like to export more Users all at once. See also: `Dashboard s2Member® Pro User/Member Exportation`.
537
  * (s2Member/s2Member Pro) **KB Articles** Inline documentation updated in some areas, with a few links pointing to helpful/related KB articles.
538
 
539
  = v130121 =
540
 
541
  * **(Maintenance Release) Upgrade immediately.**
542
+ * **New Feature** s2Member® now comes with a new Shortcode `[s2Stream file_download="video.mp4" player="jwplayer-v6" ... /]`, making it MUCH easier for site owners to implemement RTMP streams of audio/video content. For further details, please check your Dashboard under: `s2Member® Download Options JW Player® v6 and RTMP Protocol Examples`. See also: `s2Member® Download Options Shortcode Attributes (Explained)`.
543
  * **Compatibility (#75)** Updated s2Member's local file storage engine (for File Downloads via s2Member®), to support special characters in file names. Discussed in [this thread](http://www.s2member.com/forums/topic/problem-with-quotes-in-filename-downloads/#post-38395).
544
  * **Bug Fix (#71)** A bug first introduced in the previous release of v130116, where we added support for byte-range requests to s2Member's File Download functionality, was causing multiple byte-range requests (processed by s2Member) to count against each User/Member as multiple File Downloads. Fixed in this release.
545
+ * **Compatibility** Updated s2Member's integration with Amazon® S3 to extend the default 30 second connection timeout (which was too conservative for many integrations) up to 24 hours by default, making it match the same as s2Member's Amazon® CloudFront connection timeout. For further details, please check your Dashboard under: `s2Member® Download Options Amazon® S3/CDN Storage Dev Note w/Technical Details`. It is possible to modify this connection timeout through a Filter discussed there.
546
 
547
  = v130116 =
548
 
checksum.txt CHANGED
@@ -1 +1 @@
1
- 84638519eea37bbc521b44a83b66c496
1
+ 73638fc82a53a79d203ccd74db9b5416
images/trans-bg.png CHANGED
Binary file
includes/classes/access-cap-times.inc.php CHANGED
@@ -121,7 +121,7 @@ if(!class_exists('c_ws_plugin__s2member_access_cap_times'))
121
  foreach($caps as &$_caps_prev_now)
122
  {
123
  foreach(array_intersect(array_keys($_caps_prev_now), array_keys($role_objects)) as $_role)
124
- if($_caps_prev_now[$_role]) // If the cap (i.e. the role) is enabled; merge its caps.
125
  $_caps_prev_now = array_merge($_caps_prev_now, $role_objects[$_role]->capabilities);
126
 
127
  $_s2_caps_prev_now = array();
@@ -199,11 +199,11 @@ if(!class_exists('c_ws_plugin__s2member_access_cap_times'))
199
  * @param array $access_caps Optional. If not passed, this returns all times for all caps.
200
  * If passed, please pass an array of specific access capabilities to get the times for.
201
  * If removal times are desired, you should add a `-` prefix.
202
- * e.g. `array('ccap_music','level2','-ccap_video')`
203
  *
204
  * @return array An array of all access capability times.
205
  * Keys are UTC timestamps (w/ microtime precision), values are the capabilities (including `-` prefixed removals).
206
- * e.g. `array('1234567890.0001' => 'ccap_music', '1234567890.0002' => 'level2', '1234567890.0003' => '-ccap_video')`
207
  */
208
  public static function get_access_cap_times($user_id, $access_caps = array())
209
  {
121
  foreach($caps as &$_caps_prev_now)
122
  {
123
  foreach(array_intersect(array_keys($_caps_prev_now), array_keys($role_objects)) as $_role)
124
+ if($_caps_prev_now[$_role]) // If the cap (i.e., the role) is enabled; merge its caps.
125
  $_caps_prev_now = array_merge($_caps_prev_now, $role_objects[$_role]->capabilities);
126
 
127
  $_s2_caps_prev_now = array();
199
  * @param array $access_caps Optional. If not passed, this returns all times for all caps.
200
  * If passed, please pass an array of specific access capabilities to get the times for.
201
  * If removal times are desired, you should add a `-` prefix.
202
+ * e.g., `array('ccap_music','level2','-ccap_video')`
203
  *
204
  * @return array An array of all access capability times.
205
  * Keys are UTC timestamps (w/ microtime precision), values are the capabilities (including `-` prefixed removals).
206
+ * e.g., `array('1234567890.0001' => 'ccap_music', '1234567890.0002' => 'level2', '1234567890.0003' => '-ccap_video')`
207
  */
208
  public static function get_access_cap_times($user_id, $access_caps = array())
209
  {
includes/classes/admin-notices.inc.php CHANGED
@@ -33,7 +33,7 @@ if(!class_exists('c_ws_plugin__s2member_admin_notices'))
33
  * @package s2Member\Admin_Notices
34
  * @since 3.5
35
  *
36
- * @param string $notice String value of actual notice *(i.e. the message)*.
37
  * @param string|array $on_pages Optional. Defaults to any page. String or array of pages to display this notice on.
38
  * @param bool $error Optional. True if this notice is regarding an error. Defaults to false.
39
  * @param int $time Optional. Unix timestamp indicating when this notice will be displayed.
@@ -65,7 +65,7 @@ if(!class_exists('c_ws_plugin__s2member_admin_notices'))
65
  * @package s2Member\Admin_Notices
66
  * @since 3.5
67
  *
68
- * @param string $notice String value of actual notice *(i.e. the message)*.
69
  * @param bool $error Optional. True if this notice is regarding an error. Defaults to false.
70
  * @param bool $dismiss Optional. If true, the notice will be displayed with a dismissal link. Defaults to false.
71
  */
33
  * @package s2Member\Admin_Notices
34
  * @since 3.5
35
  *
36
+ * @param string $notice String value of actual notice *(i.e., the message)*.
37
  * @param string|array $on_pages Optional. Defaults to any page. String or array of pages to display this notice on.
38
  * @param bool $error Optional. True if this notice is regarding an error. Defaults to false.
39
  * @param int $time Optional. Unix timestamp indicating when this notice will be displayed.
65
  * @package s2Member\Admin_Notices
66
  * @since 3.5
67
  *
68
+ * @param string $notice String value of actual notice *(i.e., the message)*.
69
  * @param bool $error Optional. True if this notice is regarding an error. Defaults to false.
70
  * @param bool $dismiss Optional. If true, the notice will be displayed with a dismissal link. Defaults to false.
71
  */
includes/classes/auto-eots.inc.php CHANGED
@@ -176,7 +176,7 @@ if(!class_exists('c_ws_plugin__s2member_auto_eots'))
176
  c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Demoted by s2Member: '.date('D M j, Y g:i a T'));
177
 
178
  if($subscr_gateway && $subscr_id) // Also note the Paid Subscr. Gateway/ID so there is a reference left behind here.
179
- c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Paid Subscr. ID @ time of demotion: '.$subscr_gateway.' -› '.$subscr_id);
180
 
181
  if($GLOBALS['WS_PLUGIN__']['s2member']['o']['eot_del_notification_urls'] && is_array($cv = preg_split('/\|/', $custom)))
182
  {
176
  c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Demoted by s2Member: '.date('D M j, Y g:i a T'));
177
 
178
  if($subscr_gateway && $subscr_id) // Also note the Paid Subscr. Gateway/ID so there is a reference left behind here.
179
+ c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Paid Subscr. ID @ time of demotion: '.$subscr_gateway.' '.$subscr_id);
180
 
181
  if($GLOBALS['WS_PLUGIN__']['s2member']['o']['eot_del_notification_urls'] && is_array($cv = preg_split('/\|/', $custom)))
182
  {
includes/classes/aweber.inc.php CHANGED
@@ -44,7 +44,7 @@ if(!class_exists('c_ws_plugin__s2member_aweber'))
44
  include_once dirname(dirname(__FILE__)).'/externals/aweber/aweber_api.php';
45
 
46
  if(count($key_parts = explode('|', $GLOBALS['WS_PLUGIN__']['s2member']['o']['aweber_api_key'])) < 5)
47
- return NULL; // It's an invalid API key; i.e. authorization code.
48
 
49
  list($consumerKey, $consumerSecret, $requestToken, $tokenSecret, $verifier) = $key_parts;
50
  $internal_api_key_checksum = md5($consumerKey.$consumerSecret.$requestToken.$tokenSecret.$verifier);
@@ -185,7 +185,7 @@ if(!class_exists('c_ws_plugin__s2member_aweber'))
185
 
186
  $_aw['findSubscriber'] = array('email' => $args->email);
187
  if(self::count($_aw['foundSubscribers'] = $_aw['list']->subscribers->find($_aw['findSubscriber']))
188
- && $_aw['foundSubscribers'][0]->status !== 'unconfirmed' // i.e. `subscribed|unsubscribed`.
189
  ) // Cannot modify an `unconfirmed` subscriber.
190
  {
191
  /** @var AWeberEntry $_existing_subscriber */
44
  include_once dirname(dirname(__FILE__)).'/externals/aweber/aweber_api.php';
45
 
46
  if(count($key_parts = explode('|', $GLOBALS['WS_PLUGIN__']['s2member']['o']['aweber_api_key'])) < 5)
47
+ return NULL; // It's an invalid API key; i.e., authorization code.
48
 
49
  list($consumerKey, $consumerSecret, $requestToken, $tokenSecret, $verifier) = $key_parts;
50
  $internal_api_key_checksum = md5($consumerKey.$consumerSecret.$requestToken.$tokenSecret.$verifier);
185
 
186
  $_aw['findSubscriber'] = array('email' => $args->email);
187
  if(self::count($_aw['foundSubscribers'] = $_aw['list']->subscribers->find($_aw['findSubscriber']))
188
+ && $_aw['foundSubscribers'][0]->status !== 'unconfirmed' // i.e., `subscribed|unsubscribed`.
189
  ) // Cannot modify an `unconfirmed` subscriber.
190
  {
191
  /** @var AWeberEntry $_existing_subscriber */
includes/classes/constants.inc.php CHANGED
@@ -258,7 +258,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
258
  * The current User's Membership Access Label.
259
  *
260
  * As configured by the site owner. Each Membership Level is associated with a Membership Label
261
- * *(i.e. Bronze, Silver, Gold, Platinum)*, or whatever the site owner has configured.
262
  *
263
  * An empty string if NOT logged-in.
264
  *
@@ -1076,7 +1076,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1076
  *
1077
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1078
  *
1079
- * @see `Dashboard -› s2Member -› Download Options`
1080
  */
1081
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED'))
1082
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED', ($c[] = (int)$file_downloads['allowed']));
@@ -1085,7 +1085,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1085
  * Does the current User have access to unlimited File Downloads.
1086
  *
1087
  * A value of true means the current User's allowed downloads are >= `999999999`, and false means it is not.
1088
- * This is useful if you are allowing unlimited ( i.e. `999999999+` ) Downloads on some Membership Levels.
1089
  * You can display `Unlimited` instead of a numerical value.
1090
  *
1091
  * This will be false if NOT logged-in.
@@ -1131,7 +1131,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1131
  *
1132
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1133
  *
1134
- * @see `Dashboard -› s2Member -› Download Options`
1135
  */
1136
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_IS_UNLIMITED'))
1137
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_IS_UNLIMITED', ($c[] = (($file_downloads['allowed'] >= 999999999) ? TRUE : FALSE)));
@@ -1177,7 +1177,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1177
  *
1178
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1179
  *
1180
- * @see `Dashboard -› s2Member -› Download Options`
1181
  */
1182
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_CURRENTLY'))
1183
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_CURRENTLY', ($c[] = (int)$file_downloads['currently']));
@@ -1223,7 +1223,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1223
  *
1224
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1225
  *
1226
- * @see `Dashboard -› s2Member -› Download Options`
1227
  */
1228
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_DAYS'))
1229
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_DAYS', ($c[] = (int)$file_downloads['allowed_days']));
@@ -1279,7 +1279,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1279
  *
1280
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1281
  *
1282
- * @see `Dashboard -› s2Member -› Download Options`
1283
  */
1284
  if(!defined('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_ID'))
1285
  define ('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_ID', ($c[] = (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['file_download_limit_exceeded_page']));
@@ -1320,7 +1320,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1320
  *
1321
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1322
  *
1323
- * @see `Dashboard -› s2Member -› General Options -› Membership Options Page`
1324
  */
1325
  if(!defined('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_ID'))
1326
  define ('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_ID', ($c[] = (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['membership_options_page']));
@@ -1361,7 +1361,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1361
  *
1362
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1363
  *
1364
- * @see `Dashboard -› s2Member -› General Options -› Login Welcome Page`
1365
  */
1366
  if(!defined('S2MEMBER_LOGIN_WELCOME_PAGE_ID'))
1367
  define ('S2MEMBER_LOGIN_WELCOME_PAGE_ID', ($c[] = (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['login_welcome_page']));
@@ -1369,7 +1369,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1369
  /**
1370
  * A URL, which leads to the Stand-Alone Profile Modification Page.
1371
  *
1372
- * This is always a reference to `/?s2member_profile=1` *(i.e. the Stand-Alone version)*.
1373
  *
1374
  * ———— Quick PHP Code Sample ————
1375
  * ```
@@ -1401,7 +1401,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1401
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_ID
1402
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_URL
1403
  *
1404
- * @see `Dashboard -› s2Member -› General Options -› Profile Modifications`
1405
  */
1406
  if(!defined('S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL'))
1407
  define ('S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL', ($c[] = (string)home_url('/?s2member_profile=1')));
@@ -1457,7 +1457,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1457
  *
1458
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1459
  *
1460
- * @see `Dashboard -› s2Member -› Download Options`
1461
  */
1462
  if(!defined('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_URL'))
1463
  define ('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_URL', ($c[] = (string)$links['file_download_limit_exceeded_page']));
@@ -1498,7 +1498,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1498
  *
1499
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1500
  *
1501
- * @see `Dashboard -› s2Member -› General Options -› Membership Options Page`
1502
  */
1503
  if(!defined('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_URL'))
1504
  define ('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_URL', ($c[] = (string)$links['membership_options_page'])); // Signup page.
@@ -1539,7 +1539,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1539
  *
1540
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1541
  *
1542
- * @see `Dashboard -› s2Member -› General Options -› Login Welcome Page`
1543
  */
1544
  if(!defined('S2MEMBER_LOGIN_WELCOME_PAGE_URL'))
1545
  define ('S2MEMBER_LOGIN_WELCOME_PAGE_URL', ($c[] = (($login_redirection_url) ? (string)$login_redirection_url : (string)$links['login_welcome_page'])));
@@ -1664,7 +1664,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1664
  *
1665
  * @see http://codex.wordpress.org/Function_Reference/wp_get_current_user wp_get_current_user()
1666
  *
1667
- * @see `Dashboard -› s2Member -› General Options -› Membership Level (Labels)`
1668
  */
1669
  for($n = 0; $n <= $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels']; $n++)
1670
  {
@@ -1716,7 +1716,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1716
  *
1717
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1718
  *
1719
- * @see `Dashboard -› s2Member -› Download Options`
1720
  */
1721
  for($n = 0; $n <= $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels']; $n++)
1722
  {
@@ -1769,7 +1769,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1769
  *
1770
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1771
  *
1772
- * @see `Dashboard -› s2Member -› Download Options`
1773
  */
1774
  for($n = 0; $n <= $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels']; $n++)
1775
  {
@@ -1813,7 +1813,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1813
  * @see s2Member\API_Constants\S2MEMBER_LEVELn_FILE_DOWNLOADS_ALLOWED
1814
  * @see s2Member\API_Constants\S2MEMBER_LEVELn_FILE_DOWNLOADS_ALLOWED_DAYS
1815
  *
1816
- * @see `Dashboard -› s2Member -› Download Options`
1817
  */
1818
  if(!defined('S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS'))
1819
  define ('S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['file_download_inline_extensions']));
@@ -1841,7 +1841,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1841
  *
1842
  * @see s2Member\API_Constants\S2MEMBER_REG_EMAIL_FROM_EMAIL
1843
  *
1844
- * @see `Dashboard -› s2Member -› General Options`
1845
  */
1846
  if(!defined('S2MEMBER_REG_EMAIL_FROM_NAME'))
1847
  define ('S2MEMBER_REG_EMAIL_FROM_NAME', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['reg_email_from_name']));
@@ -1869,7 +1869,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1869
  *
1870
  * @see s2Member\API_Constants\S2MEMBER_REG_EMAIL_FROM_NAME
1871
  *
1872
- * @see `Dashboard -› s2Member -› General Options`
1873
  */
1874
  if(!defined('S2MEMBER_REG_EMAIL_FROM_EMAIL'))
1875
  define ('S2MEMBER_REG_EMAIL_FROM_EMAIL', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['reg_email_from_email']));
@@ -1898,7 +1898,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1898
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_RETURN_URL
1899
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN
1900
  *
1901
- * @see `Dashboard -› s2Member -› PayPal Options -› IPN Integration`
1902
  */
1903
  if(!defined('S2MEMBER_PAYPAL_NOTIFY_URL'))
1904
  define ('S2MEMBER_PAYPAL_NOTIFY_URL', ($c[] = (string)home_url('/?s2member_paypal_notify=1')));
@@ -1927,7 +1927,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1927
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_NOTIFY_URL
1928
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN
1929
  *
1930
- * @see `Dashboard -› s2Member -› PayPal Options -› Auto-Return/PDT Integration`
1931
  */
1932
  if(!defined('S2MEMBER_PAYPAL_RETURN_URL'))
1933
  define ('S2MEMBER_PAYPAL_RETURN_URL', ($c[] = (string)home_url('/?s2member_paypal_return=1')));
@@ -1957,7 +1957,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1957
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_PASSWORD
1958
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_SIGNATURE
1959
  *
1960
- * @see `Dashboard -› s2Member -› PayPal Options -› Account Details`
1961
  */
1962
  if(!defined('S2MEMBER_PAYPAL_BUSINESS'))
1963
  define ('S2MEMBER_PAYPAL_BUSINESS', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_business']));
@@ -1988,7 +1988,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
1988
  *
1989
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_ENDPOINT
1990
  *
1991
- * @see `Dashboard -› s2Member -› PayPal Options -› Account Details`
1992
  */
1993
  if(!defined('S2MEMBER_PAYPAL_ENDPOINT'))
1994
  define ('S2MEMBER_PAYPAL_ENDPOINT', ($c[] = (($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_sandbox']) ? 'www.sandbox.paypal.com' : 'www.paypal.com')));
@@ -2019,7 +2019,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2019
  *
2020
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_ENDPOINT
2021
  *
2022
- * @see `Dashboard -› s2Member -› PayPal Options -› Account Details`
2023
  */
2024
  if(!defined('S2MEMBER_PAYPAL_API_ENDPOINT'))
2025
  define ('S2MEMBER_PAYPAL_API_ENDPOINT', ($c[] = (($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_sandbox']) ? 'api-3t.sandbox.paypal.com' : 'api-3t.paypal.com')));
@@ -2048,7 +2048,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2048
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_PASSWORD
2049
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_SIGNATURE
2050
  *
2051
- * @see `Dashboard -› s2Member -› PayPal Options -› Account Details`
2052
  */
2053
  if(!defined('S2MEMBER_PAYPAL_API_USERNAME'))
2054
  define ('S2MEMBER_PAYPAL_API_USERNAME', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_username']));
@@ -2077,7 +2077,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2077
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_USERNAME
2078
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_SIGNATURE
2079
  *
2080
- * @see `Dashboard -› s2Member -› PayPal Options -› Account Details`
2081
  */
2082
  if(!defined('S2MEMBER_PAYPAL_API_PASSWORD'))
2083
  define ('S2MEMBER_PAYPAL_API_PASSWORD', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_password']));
@@ -2106,7 +2106,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2106
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_USERNAME
2107
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_PASSWORD
2108
  *
2109
- * @see `Dashboard -› s2Member -› PayPal Options -› Account Details`
2110
  */
2111
  if(!defined('S2MEMBER_PAYPAL_API_SIGNATURE'))
2112
  define ('S2MEMBER_PAYPAL_API_SIGNATURE', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_signature']));
@@ -2134,7 +2134,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2134
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_RETURN_URL
2135
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_NOTIFY_URL
2136
  *
2137
- * @see `Dashboard -› s2Member -› PayPal Options -› Auto-Return/PDT Integration`
2138
  */
2139
  if(!defined('S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN'))
2140
  define ('S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_identity_token']));
@@ -2146,7 +2146,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2146
  * However, in cases where multiple Buttons are displayed on the same page, the alternative {@link s2Member\API_Functions\s2member_value_for_pp_inv()} function should be used instead.
2147
  *
2148
  * Note. This API Constant is excluded from the ``$c[]`` hash calculation used in the generation of {@link s2Member\API_Constants\WS_PLUGIN__S2MEMBER_API_CONSTANTS_MD5}.
2149
- * It MUST be excluded, because the value of this particular API Constant will change too often *(i.e. it changes, depending on microtime)*.
2150
  * So, when including this API Constant in the JavaScript API as a Global, care must be taken to build an Invoice, using JavaScript
2151
  * to calculate the unique time-based code, with something like: `Math.round (new Date ().getTime ())`.
2152
  *
@@ -2195,7 +2195,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2195
  *
2196
  * @see s2Member\API_Functions\s2member_value_for_pp_inv()
2197
  *
2198
- * @see `Dashboard -› s2Member -› PayPal Buttons`
2199
  */
2200
  if(!defined('S2MEMBER_VALUE_FOR_PP_INV'))
2201
  define ('S2MEMBER_VALUE_FOR_PP_INV', uniqid().'~'.S2MEMBER_CURRENT_USER_IP);
@@ -2203,7 +2203,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2203
  /**
2204
  * PayPal value for Payment Buttons with input name: `on0`.
2205
  *
2206
- * Used in PayPal Modification Buttons *(i.e. upgrades/downgrades)*.
2207
  *
2208
  * This auto-fills the `on0` value in PayPal Button Codes. If a Button Code is presented to a logged-in Member,
2209
  * this will auto-fill the value for the `on0` input variable, with the string: "Referencing Customer ID".
@@ -2254,7 +2254,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2254
  *
2255
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS0
2256
  *
2257
- * @see `Dashboard -› s2Member -› PayPal Buttons`
2258
  */
2259
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON0'))
2260
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON0', ($c[] = ((S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID) ? 'Referencing Customer ID' : 'Originating Domain')));
@@ -2262,7 +2262,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2262
  /**
2263
  * PayPal value for Payment Buttons with input name: `os0`.
2264
  *
2265
- * Used in PayPal Modification Buttons *(i.e. upgrades/downgrades)*.
2266
  *
2267
  * This auto-fills the `os0` value in PayPal Button Codes. If a Button Code is presented to a logged-in Member,
2268
  * this will auto-fill the value for the `os0` input variable, with the value of {@link s2Member\API_Constants\S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID}.
@@ -2313,7 +2313,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2313
  *
2314
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON0
2315
  *
2316
- * @see `Dashboard -› s2Member -› PayPal Buttons`
2317
  */
2318
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS0'))
2319
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS0', ($c[] = ((S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID) ? S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID : (string)@$_SERVER['HTTP_HOST'])));
@@ -2369,7 +2369,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2369
  *
2370
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS1
2371
  *
2372
- * @see `Dashboard -› s2Member -› PayPal Buttons`
2373
  */
2374
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON1'))
2375
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON1', ($c[] = 'Customer IP Address' /* Via $_SERVER['REMOTE_ADDR'] below. */));
@@ -2425,7 +2425,7 @@ if(!class_exists('c_ws_plugin__s2member_constants'))
2425
  *
2426
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON1
2427
  *
2428
- * @see `Dashboard -› s2Member -› PayPal Buttons`
2429
  */
2430
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS1'))
2431
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS1', ($c[] = (string)@$_SERVER['REMOTE_ADDR']));
258
  * The current User's Membership Access Label.
259
  *
260
  * As configured by the site owner. Each Membership Level is associated with a Membership Label
261
+ * *(i.e., Bronze, Silver, Gold, Platinum)*, or whatever the site owner has configured.
262
  *
263
  * An empty string if NOT logged-in.
264
  *
1076
  *
1077
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1078
  *
1079
+ * @see `Dashboard s2Member Download Options`
1080
  */
1081
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED'))
1082
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED', ($c[] = (int)$file_downloads['allowed']));
1085
  * Does the current User have access to unlimited File Downloads.
1086
  *
1087
  * A value of true means the current User's allowed downloads are >= `999999999`, and false means it is not.
1088
+ * This is useful if you are allowing unlimited ( i.e., `999999999+` ) Downloads on some Membership Levels.
1089
  * You can display `Unlimited` instead of a numerical value.
1090
  *
1091
  * This will be false if NOT logged-in.
1131
  *
1132
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1133
  *
1134
+ * @see `Dashboard s2Member Download Options`
1135
  */
1136
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_IS_UNLIMITED'))
1137
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_IS_UNLIMITED', ($c[] = (($file_downloads['allowed'] >= 999999999) ? TRUE : FALSE)));
1177
  *
1178
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1179
  *
1180
+ * @see `Dashboard s2Member Download Options`
1181
  */
1182
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_CURRENTLY'))
1183
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_CURRENTLY', ($c[] = (int)$file_downloads['currently']));
1223
  *
1224
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1225
  *
1226
+ * @see `Dashboard s2Member Download Options`
1227
  */
1228
  if(!defined('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_DAYS'))
1229
  define ('S2MEMBER_CURRENT_USER_DOWNLOADS_ALLOWED_DAYS', ($c[] = (int)$file_downloads['allowed_days']));
1279
  *
1280
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1281
  *
1282
+ * @see `Dashboard s2Member Download Options`
1283
  */
1284
  if(!defined('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_ID'))
1285
  define ('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_ID', ($c[] = (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['file_download_limit_exceeded_page']));
1320
  *
1321
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1322
  *
1323
+ * @see `Dashboard s2Member General Options Membership Options Page`
1324
  */
1325
  if(!defined('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_ID'))
1326
  define ('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_ID', ($c[] = (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['membership_options_page']));
1361
  *
1362
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1363
  *
1364
+ * @see `Dashboard s2Member General Options Login Welcome Page`
1365
  */
1366
  if(!defined('S2MEMBER_LOGIN_WELCOME_PAGE_ID'))
1367
  define ('S2MEMBER_LOGIN_WELCOME_PAGE_ID', ($c[] = (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['login_welcome_page']));
1369
  /**
1370
  * A URL, which leads to the Stand-Alone Profile Modification Page.
1371
  *
1372
+ * This is always a reference to `/?s2member_profile=1` *(i.e., the Stand-Alone version)*.
1373
  *
1374
  * ———— Quick PHP Code Sample ————
1375
  * ```
1401
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_ID
1402
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_URL
1403
  *
1404
+ * @see `Dashboard s2Member General Options Profile Modifications`
1405
  */
1406
  if(!defined('S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL'))
1407
  define ('S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL', ($c[] = (string)home_url('/?s2member_profile=1')));
1457
  *
1458
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1459
  *
1460
+ * @see `Dashboard s2Member Download Options`
1461
  */
1462
  if(!defined('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_URL'))
1463
  define ('S2MEMBER_FILE_DOWNLOAD_LIMIT_EXCEEDED_PAGE_URL', ($c[] = (string)$links['file_download_limit_exceeded_page']));
1498
  *
1499
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1500
  *
1501
+ * @see `Dashboard s2Member General Options Membership Options Page`
1502
  */
1503
  if(!defined('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_URL'))
1504
  define ('S2MEMBER_MEMBERSHIP_OPTIONS_PAGE_URL', ($c[] = (string)$links['membership_options_page'])); // Signup page.
1539
  *
1540
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL
1541
  *
1542
+ * @see `Dashboard s2Member General Options Login Welcome Page`
1543
  */
1544
  if(!defined('S2MEMBER_LOGIN_WELCOME_PAGE_URL'))
1545
  define ('S2MEMBER_LOGIN_WELCOME_PAGE_URL', ($c[] = (($login_redirection_url) ? (string)$login_redirection_url : (string)$links['login_welcome_page'])));
1664
  *
1665
  * @see http://codex.wordpress.org/Function_Reference/wp_get_current_user wp_get_current_user()
1666
  *
1667
+ * @see `Dashboard s2Member General Options Membership Level (Labels)`
1668
  */
1669
  for($n = 0; $n <= $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels']; $n++)
1670
  {
1716
  *
1717
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1718
  *
1719
+ * @see `Dashboard s2Member Download Options`
1720
  */
1721
  for($n = 0; $n <= $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels']; $n++)
1722
  {
1769
  *
1770
  * @see s2Member\API_Constants\S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS
1771
  *
1772
+ * @see `Dashboard s2Member Download Options`
1773
  */
1774
  for($n = 0; $n <= $GLOBALS['WS_PLUGIN__']['s2member']['c']['levels']; $n++)
1775
  {
1813
  * @see s2Member\API_Constants\S2MEMBER_LEVELn_FILE_DOWNLOADS_ALLOWED
1814
  * @see s2Member\API_Constants\S2MEMBER_LEVELn_FILE_DOWNLOADS_ALLOWED_DAYS
1815
  *
1816
+ * @see `Dashboard s2Member Download Options`
1817
  */
1818
  if(!defined('S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS'))
1819
  define ('S2MEMBER_FILE_DOWNLOAD_INLINE_EXTENSIONS', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['file_download_inline_extensions']));
1841
  *
1842
  * @see s2Member\API_Constants\S2MEMBER_REG_EMAIL_FROM_EMAIL
1843
  *
1844
+ * @see `Dashboard s2Member General Options`
1845
  */
1846
  if(!defined('S2MEMBER_REG_EMAIL_FROM_NAME'))
1847
  define ('S2MEMBER_REG_EMAIL_FROM_NAME', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['reg_email_from_name']));
1869
  *
1870
  * @see s2Member\API_Constants\S2MEMBER_REG_EMAIL_FROM_NAME
1871
  *
1872
+ * @see `Dashboard s2Member General Options`
1873
  */
1874
  if(!defined('S2MEMBER_REG_EMAIL_FROM_EMAIL'))
1875
  define ('S2MEMBER_REG_EMAIL_FROM_EMAIL', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['reg_email_from_email']));
1898
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_RETURN_URL
1899
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN
1900
  *
1901
+ * @see `Dashboard s2Member PayPal Options IPN Integration`
1902
  */
1903
  if(!defined('S2MEMBER_PAYPAL_NOTIFY_URL'))
1904
  define ('S2MEMBER_PAYPAL_NOTIFY_URL', ($c[] = (string)home_url('/?s2member_paypal_notify=1')));
1927
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_NOTIFY_URL
1928
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN
1929
  *
1930
+ * @see `Dashboard s2Member PayPal Options Auto-Return/PDT Integration`
1931
  */
1932
  if(!defined('S2MEMBER_PAYPAL_RETURN_URL'))
1933
  define ('S2MEMBER_PAYPAL_RETURN_URL', ($c[] = (string)home_url('/?s2member_paypal_return=1')));
1957
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_PASSWORD
1958
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_SIGNATURE
1959
  *
1960
+ * @see `Dashboard s2Member PayPal Options Account Details`
1961
  */
1962
  if(!defined('S2MEMBER_PAYPAL_BUSINESS'))
1963
  define ('S2MEMBER_PAYPAL_BUSINESS', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_business']));
1988
  *
1989
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_ENDPOINT
1990
  *
1991
+ * @see `Dashboard s2Member PayPal Options Account Details`
1992
  */
1993
  if(!defined('S2MEMBER_PAYPAL_ENDPOINT'))
1994
  define ('S2MEMBER_PAYPAL_ENDPOINT', ($c[] = (($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_sandbox']) ? 'www.sandbox.paypal.com' : 'www.paypal.com')));
2019
  *
2020
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_ENDPOINT
2021
  *
2022
+ * @see `Dashboard s2Member PayPal Options Account Details`
2023
  */
2024
  if(!defined('S2MEMBER_PAYPAL_API_ENDPOINT'))
2025
  define ('S2MEMBER_PAYPAL_API_ENDPOINT', ($c[] = (($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_sandbox']) ? 'api-3t.sandbox.paypal.com' : 'api-3t.paypal.com')));
2048
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_PASSWORD
2049
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_SIGNATURE
2050
  *
2051
+ * @see `Dashboard s2Member PayPal Options Account Details`
2052
  */
2053
  if(!defined('S2MEMBER_PAYPAL_API_USERNAME'))
2054
  define ('S2MEMBER_PAYPAL_API_USERNAME', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_username']));
2077
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_USERNAME
2078
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_SIGNATURE
2079
  *
2080
+ * @see `Dashboard s2Member PayPal Options Account Details`
2081
  */
2082
  if(!defined('S2MEMBER_PAYPAL_API_PASSWORD'))
2083
  define ('S2MEMBER_PAYPAL_API_PASSWORD', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_password']));
2106
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_USERNAME
2107
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_API_PASSWORD
2108
  *
2109
+ * @see `Dashboard s2Member PayPal Options Account Details`
2110
  */
2111
  if(!defined('S2MEMBER_PAYPAL_API_SIGNATURE'))
2112
  define ('S2MEMBER_PAYPAL_API_SIGNATURE', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_signature']));
2134
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_RETURN_URL
2135
  * @see s2Member\API_Constants\S2MEMBER_PAYPAL_NOTIFY_URL
2136
  *
2137
+ * @see `Dashboard s2Member PayPal Options Auto-Return/PDT Integration`
2138
  */
2139
  if(!defined('S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN'))
2140
  define ('S2MEMBER_PAYPAL_PDT_IDENTITY_TOKEN', ($c[] = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_identity_token']));
2146
  * However, in cases where multiple Buttons are displayed on the same page, the alternative {@link s2Member\API_Functions\s2member_value_for_pp_inv()} function should be used instead.
2147
  *
2148
  * Note. This API Constant is excluded from the ``$c[]`` hash calculation used in the generation of {@link s2Member\API_Constants\WS_PLUGIN__S2MEMBER_API_CONSTANTS_MD5}.
2149
+ * It MUST be excluded, because the value of this particular API Constant will change too often *(i.e., it changes, depending on microtime)*.
2150
  * So, when including this API Constant in the JavaScript API as a Global, care must be taken to build an Invoice, using JavaScript
2151
  * to calculate the unique time-based code, with something like: `Math.round (new Date ().getTime ())`.
2152
  *
2195
  *
2196
  * @see s2Member\API_Functions\s2member_value_for_pp_inv()
2197
  *
2198
+ * @see `Dashboard s2Member PayPal Buttons`
2199
  */
2200
  if(!defined('S2MEMBER_VALUE_FOR_PP_INV'))
2201
  define ('S2MEMBER_VALUE_FOR_PP_INV', uniqid().'~'.S2MEMBER_CURRENT_USER_IP);
2203
  /**
2204
  * PayPal value for Payment Buttons with input name: `on0`.
2205
  *
2206
+ * Used in PayPal Modification Buttons *(i.e., upgrades/downgrades)*.
2207
  *
2208
  * This auto-fills the `on0` value in PayPal Button Codes. If a Button Code is presented to a logged-in Member,
2209
  * this will auto-fill the value for the `on0` input variable, with the string: "Referencing Customer ID".
2254
  *
2255
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS0
2256
  *
2257
+ * @see `Dashboard s2Member PayPal Buttons`
2258
  */
2259
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON0'))
2260
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON0', ($c[] = ((S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID) ? 'Referencing Customer ID' : 'Originating Domain')));
2262
  /**
2263
  * PayPal value for Payment Buttons with input name: `os0`.
2264
  *
2265
+ * Used in PayPal Modification Buttons *(i.e., upgrades/downgrades)*.
2266
  *
2267
  * This auto-fills the `os0` value in PayPal Button Codes. If a Button Code is presented to a logged-in Member,
2268
  * this will auto-fill the value for the `os0` input variable, with the value of {@link s2Member\API_Constants\S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID}.
2313
  *
2314
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON0
2315
  *
2316
+ * @see `Dashboard s2Member PayPal Buttons`
2317
  */
2318
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS0'))
2319
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS0', ($c[] = ((S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID) ? S2MEMBER_CURRENT_USER_SUBSCR_OR_WP_ID : (string)@$_SERVER['HTTP_HOST'])));
2369
  *
2370
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS1
2371
  *
2372
+ * @see `Dashboard s2Member PayPal Buttons`
2373
  */
2374
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON1'))
2375
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON1', ($c[] = 'Customer IP Address' /* Via $_SERVER['REMOTE_ADDR'] below. */));
2425
  *
2426
  * @see s2Member\API_Constants\S2MEMBER_CURRENT_USER_VALUE_FOR_PP_ON1
2427
  *
2428
+ * @see `Dashboard s2Member PayPal Buttons`
2429
  */
2430
  if(!defined('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS1'))
2431
  define ('S2MEMBER_CURRENT_USER_VALUE_FOR_PP_OS1', ($c[] = (string)@$_SERVER['REMOTE_ADDR']));
includes/classes/files-in.inc.php CHANGED
@@ -141,7 +141,7 @@ if(!class_exists('c_ws_plugin__s2member_files_in'))
141
  status_header(503);
142
  header('Content-Type: text/html; charset=UTF-8');
143
  while(@ob_end_clean()) ; // Clean any existing output buffers.
144
- exit(_x('<strong>503: Basic File Downloads are NOT enabled yet.</strong> Please contact Support for assistance. If you are the site owner, please configure: <code>s2Member -› General Options -› Membership Options Page</code>.', 's2member-front', 's2member'));
145
  }
146
  return FALSE; // Else return false.
147
  }
@@ -152,7 +152,7 @@ if(!class_exists('c_ws_plugin__s2member_files_in'))
152
  status_header(503);
153
  header('Content-Type: text/html; charset=UTF-8');
154
  while(@ob_end_clean()) ; // Clean any existing output buffers.
155
- exit(_x('<strong>503: Basic File Downloads are NOT enabled yet.</strong> Please contact Support for assistance. If you are the site owner, please configure: <code>s2Member -› Download Options -› Basic Download Restrictions</code>.', 's2member-front', 's2member'));
156
  }
157
  return FALSE; // Else return false.
158
  }
@@ -510,7 +510,7 @@ if(!class_exists('c_ws_plugin__s2member_files_in'))
510
  *
511
  * @param array $config Required. This is an array of configuration options associated with permissions being checked against the current User/Member; and also the actual URL generated by this routine.
512
  * Possible ``$config`` array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `check_user`.
513
- * @param bool $get_streamer_array Optional. Defaults to `false`. If `true`, this function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member -› Download Options -› JW Player & RTMP Protocol Examples`.
514
  *
515
  * @return string A File Download URL string on success; or an array on success, with elements `streamer`, `file`, `url` when/if ``$get_streamer_array`` is true; else false on any type of failure.
516
  *
@@ -684,10 +684,10 @@ if(!class_exists('c_ws_plugin__s2member_files_in'))
684
  * @package s2Member\Files
685
  * @since 150108
686
  *
687
- * @param string $s3_date The date header; e.g. `YYYYMMDD'T'HHMMSS'Z'`.
688
- * @param string $s3_domain The API endpoint domain; e.g. `[bucket].s3.amazonaws.com`.
689
- * @param string $s3_location The API endpoint URI; e.g. `/?acl`.
690
- * @param string $s3_method The request method; e.g. `GET`, `PUT`, `POST`, etc.
691
  * @param array $s3_headers An associative array of all headers.
692
  * @param string $s3_body Any input data sent with the request.
693
  * @param boolean $sig_only Return signature only?
141
  status_header(503);
142
  header('Content-Type: text/html; charset=UTF-8');
143
  while(@ob_end_clean()) ; // Clean any existing output buffers.
144
+ exit(_x('<strong>503: Basic File Downloads are NOT enabled yet.</strong> Please contact Support for assistance. If you are the site owner, please configure: <strong>s2Member General Options Membership Options Page</strong>.', 's2member-front', 's2member'));
145
  }
146
  return FALSE; // Else return false.
147
  }
152
  status_header(503);
153
  header('Content-Type: text/html; charset=UTF-8');
154
  while(@ob_end_clean()) ; // Clean any existing output buffers.
155
+ exit(_x('<strong>503: Basic File Downloads are NOT enabled yet.</strong> Please contact Support for assistance. If you are the site owner, please configure: <strong>s2Member Download Options Basic Download Restrictions</strong>.', 's2member-front', 's2member'));
156
  }
157
  return FALSE; // Else return false.
158
  }
510
  *
511
  * @param array $config Required. This is an array of configuration options associated with permissions being checked against the current User/Member; and also the actual URL generated by this routine.
512
  * Possible ``$config`` array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `check_user`.
513
+ * @param bool $get_streamer_array Optional. Defaults to `false`. If `true`, this function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member Download Options JW Player & RTMP Protocol Examples`.
514
  *
515
  * @return string A File Download URL string on success; or an array on success, with elements `streamer`, `file`, `url` when/if ``$get_streamer_array`` is true; else false on any type of failure.
516
  *
684
  * @package s2Member\Files
685
  * @since 150108
686
  *
687
+ * @param string $s3_date The date header; e.g., `YYYYMMDD'T'HHMMSS'Z'`.
688
+ * @param string $s3_domain The API endpoint domain; e.g., `[bucket].s3.amazonaws.com`.
689
+ * @param string $s3_location The API endpoint URI; e.g., `/?acl`.
690
+ * @param string $s3_method The request method; e.g., `GET`, `PUT`, `POST`, etc.
691
  * @param array $s3_headers An associative array of all headers.
692
  * @param string $s3_body Any input data sent with the request.
693
  * @param boolean $sig_only Return signature only?
includes/classes/files.inc.php CHANGED
@@ -57,7 +57,7 @@ if(!class_exists('c_ws_plugin__s2member_files'))
57
  *
58
  * @param array $config Required. This is an array of configuration options associated with permissions being checked against the current User/Member; and also the actual URL generated by this routine.
59
  * Possible ``$config`` array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `check_user`.
60
- * @param bool $get_streamer_array Optional. Defaults to `false`. If `true`, this function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member -› Download Options -› JW Player & RTMP Protocol Examples`.
61
  *
62
  * @return string A File Download URL string on success; or an array on success, with elements `streamer`, `file`, `url` when/if ``$get_streamer_array`` is true; else false on any type of failure.
63
  *
@@ -149,7 +149,7 @@ if(!class_exists('c_ws_plugin__s2member_files'))
149
  * @since 3.5
150
  *
151
  * @param string $file Location of your protected file, relative to the `/s2member-files/` directory.
152
- * In other words, just the name of the file *(i.e. `file.zip` )*.
153
  * @param string $directive Optional. One of `ip-forever|universal|cache-compatible`.
154
  * `ip-forever` = a Download Key that never expires, tied only to a specific file and IP address.
155
  * `universal` and/or `cache-compatible` = a Download Key which never expires, and is NOT tied to any specific User. Use at your own risk.
@@ -187,8 +187,8 @@ if(!class_exists('c_ws_plugin__s2member_files'))
187
  * @param object $user Optional. A `WP_User` object. Defaults to the current User's object.
188
  * @param string $not_counting_this_particular_file Optional. If you want to exclude a particular file,
189
  * relative to the `/s2member-files/` directory, or relative to the root of your Amazon S3 Bucket *(when applicable)*.
190
- * @param array $user_log Optional. Prevents another database connection *(i.e. the User's log does not need to be pulled again)*.
191
- * @param array $user_arc Optional. Prevents another database connection *(i.e. the User's archive does not need to be pulled again)*.
192
  *
193
  * @return array An array with the following elements... File Downloads allowed for this User: (int)`allowed`, Download Period for this User in days: (int)`allowed_days`, Files downloaded by this User in the current Period: (int)`currently`, log of all Files downloaded in the current Period, with file names/dates: (array)`log`, archive of all Files downloaded in prior Periods, with file names/dates: (array)`archive`.
194
  *
@@ -301,7 +301,7 @@ if(!class_exists('c_ws_plugin__s2member_files'))
301
  foreach($la_entries as $la_entry /* Go through all of the entries in each result ``$r``; collecting `counter` values. */)
302
  if(!empty($la_entry['file']) && $la_entry['file'] === $file && (!empty($la_entry['counter']) || ($la_entry['counter'] = 1)))
303
  {
304
- $total = (!empty($total)) ? $total + 1 : 1; // Only count `1` here (i.e. unique downloads).
305
  break; // Break now. No need to continue looping; ``$file`` found in these entries.
306
  }
307
  }
57
  *
58
  * @param array $config Required. This is an array of configuration options associated with permissions being checked against the current User/Member; and also the actual URL generated by this routine.
59
  * Possible ``$config`` array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `check_user`.
60
+ * @param bool $get_streamer_array Optional. Defaults to `false`. If `true`, this function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member Download Options JW Player & RTMP Protocol Examples`.
61
  *
62
  * @return string A File Download URL string on success; or an array on success, with elements `streamer`, `file`, `url` when/if ``$get_streamer_array`` is true; else false on any type of failure.
63
  *
149
  * @since 3.5
150
  *
151
  * @param string $file Location of your protected file, relative to the `/s2member-files/` directory.
152
+ * In other words, just the name of the file *(i.e., `file.zip` )*.
153
  * @param string $directive Optional. One of `ip-forever|universal|cache-compatible`.
154
  * `ip-forever` = a Download Key that never expires, tied only to a specific file and IP address.
155
  * `universal` and/or `cache-compatible` = a Download Key which never expires, and is NOT tied to any specific User. Use at your own risk.
187
  * @param object $user Optional. A `WP_User` object. Defaults to the current User's object.
188
  * @param string $not_counting_this_particular_file Optional. If you want to exclude a particular file,
189
  * relative to the `/s2member-files/` directory, or relative to the root of your Amazon S3 Bucket *(when applicable)*.
190
+ * @param array $user_log Optional. Prevents another database connection *(i.e., the User's log does not need to be pulled again)*.
191
+ * @param array $user_arc Optional. Prevents another database connection *(i.e., the User's archive does not need to be pulled again)*.
192
  *
193
  * @return array An array with the following elements... File Downloads allowed for this User: (int)`allowed`, Download Period for this User in days: (int)`allowed_days`, Files downloaded by this User in the current Period: (int)`currently`, log of all Files downloaded in the current Period, with file names/dates: (array)`log`, archive of all Files downloaded in prior Periods, with file names/dates: (array)`archive`.
194
  *
301
  foreach($la_entries as $la_entry /* Go through all of the entries in each result ``$r``; collecting `counter` values. */)
302
  if(!empty($la_entry['file']) && $la_entry['file'] === $file && (!empty($la_entry['counter']) || ($la_entry['counter'] = 1)))
303
  {
304
+ $total = (!empty($total)) ? $total + 1 : 1; // Only count `1` here (i.e., unique downloads).
305
  break; // Break now. No need to continue looping; ``$file`` found in these entries.
306
  }
307
  }
includes/classes/getresponse.inc.php CHANGED
@@ -92,7 +92,7 @@ if(!class_exists('c_ws_plugin__s2member_getresponse'))
92
  $_gr['api_success'] = $success = TRUE; // Flag this as `TRUE`; assists with return value below.
93
  }
94
  }
95
- else // Create a new contact; i.e. they do not exist yet.
96
  {
97
  $_gr['api_method'] = 'add_contact'; // Add new contact.
98
  $_gr['api_headers'] = array('Content-Type' => 'application/json');
92
  $_gr['api_success'] = $success = TRUE; // Flag this as `TRUE`; assists with return value below.
93
  }
94
  }
95
+ else // Create a new contact; i.e., they do not exist yet.
96
  {
97
  $_gr['api_method'] = 'add_contact'; // Add new contact.
98
  $_gr['api_headers'] = array('Content-Type' => 'application/json');
includes/classes/installation.inc.php CHANGED
@@ -92,17 +92,17 @@ if(!class_exists('c_ws_plugin__s2member_installation'))
92
  if(!$v || !version_compare($v, '110912', '>=') && $GLOBALS['WS_PLUGIN__']['s2member']['o']['filter_wp_query'] === array('all'))
93
  // s2Member v110912 changed the way the 'all' option for Alternative Views was handled.
94
  {
95
- $notice = '<strong>IMPORTANT:</strong> This version of s2Member changes the way your <code>Alternative View Protections</code> work. Please review your options under: <code>s2Member -› Restriction Options -› Alternative View Protections</code>.';
96
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
97
  }
98
  if($v && version_compare($v, '130316', '<=')) // This version disables logging by default.
99
  {
100
  c_ws_plugin__s2member_menu_pages::update_all_options(array('ws_plugin__s2member_gateway_debug_logs' => '0', 'ws_plugin__s2member_gateway_debug_logs_extensive' => '0'), TRUE, FALSE, FALSE, FALSE, FALSE);
101
 
102
- $notice = '<strong>IMPORTANT:</strong> This version of s2Member disables s2Member\'s debug logging by default (for added security). Please see: <a href="'.esc_attr(admin_url('/admin.php?page=ws-plugin--s2member-logs')).'">s2Member -› Log Files (Debug) -› Configuration</a> for further details.';
103
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
104
 
105
- $notice = '<strong>IMPORTANT / Regarding s2Member Security Badges:</strong> If debug logging is enabled, your site will no longer qualify for an s2Member Security Badge until you disable logging (and you MUST also download, and then delete any existing log files from the past). Please see KB Article: <a href="http://www.s2member.com/kb/security-badges/" target="_blank" rel="external">s2Member Security Badges</a> for further details. If you have existing s2Member log files, you will need to delete those files from the server before your s2Member Security Badge can be re-enabled. s2Member stores log files here: <code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'])).'</code>. See also: <a href="'.esc_attr(admin_url('/admin.php?page=ws-plugin--s2member-logs')).'">s2Member -› Log Files (Debug) -› Configuration</a> for further details.';
106
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
107
  }
108
  if($v && version_compare($v, '140128', '<=')) // This version introduces support for partial refunds.
@@ -114,7 +114,7 @@ if(!class_exists('c_ws_plugin__s2member_installation'))
114
  $notice .= 'You now have version '.esc_html(WS_PLUGIN__S2MEMBER_VERSION).'. Your existing configuration remains.';
115
 
116
  if(!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) // No Changelog on a Multisite Blog Farm.
117
- $notice .= '<br />Have fun, <a href="'.esc_attr(admin_url('/admin.php?page=ws-plugin--s2member-info#rm-changelog')).'">read the Changelog</a>, and make some money! :-)';
118
 
119
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
120
  }
@@ -136,7 +136,7 @@ if(!class_exists('c_ws_plugin__s2member_installation'))
136
  $notice = '<strong>Multisite Network</strong> updated automatically by <strong>s2Member</strong> v'.esc_html(WS_PLUGIN__S2MEMBER_VERSION).'.<br />';
137
  $notice .= 'You\'ll want to configure s2Member\'s Multisite options now.<br />';
138
  $notice .= 'In the Dashboard for your Main Site, see:<br />';
139
- $notice .= '<code>s2Member -› Multisite (Config)</code>.';
140
 
141
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
142
 
92
  if(!$v || !version_compare($v, '110912', '>=') && $GLOBALS['WS_PLUGIN__']['s2member']['o']['filter_wp_query'] === array('all'))
93
  // s2Member v110912 changed the way the 'all' option for Alternative Views was handled.
94
  {
95
+ $notice = '<strong>IMPORTANT:</strong> This version of s2Member changes the way your <code>Alternative View Protections</code> work. Please review your options under: <strong>s2Member Restriction Options Alternative View Protections</strong>.';
96
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
97
  }
98
  if($v && version_compare($v, '130316', '<=')) // This version disables logging by default.
99
  {
100
  c_ws_plugin__s2member_menu_pages::update_all_options(array('ws_plugin__s2member_gateway_debug_logs' => '0', 'ws_plugin__s2member_gateway_debug_logs_extensive' => '0'), TRUE, FALSE, FALSE, FALSE, FALSE);
101
 
102
+ $notice = '<strong>IMPORTANT:</strong> This version of s2Member disables s2Member\'s debug logging by default (for added security). Please see: <a href="'.esc_attr(admin_url('/admin.php?page=ws-plugin--s2member-logs')).'">s2Member Log Files (Debug) Configuration</a> for further details.';
103
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
104
 
105
+ $notice = '<strong>IMPORTANT / Regarding s2Member Security Badges:</strong> If debug logging is enabled, your site will no longer qualify for an s2Member Security Badge until you disable logging (and you must also download, and then delete any existing log files from the past). Please see KB Article: <a href="http://www.s2member.com/kb/security-badges/" target="_blank" rel="external">s2Member Security Badges</a> for further details. If you have existing s2Member log files, you will need to delete those files from the server before your s2Member Security Badge can be re-enabled. s2Member stores log files here: <code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'])).'</code>. See also: <a href="'.esc_attr(admin_url('/admin.php?page=ws-plugin--s2member-logs')).'">s2Member Log Files (Debug) Configuration</a> for further details.';
106
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
107
  }
108
  if($v && version_compare($v, '140128', '<=')) // This version introduces support for partial refunds.
114
  $notice .= 'You now have version '.esc_html(WS_PLUGIN__S2MEMBER_VERSION).'. Your existing configuration remains.';
115
 
116
  if(!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) // No Changelog on a Multisite Blog Farm.
117
+ $notice .= '<br />Have fun, <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value('Changelog URI')).'" target="_blank">read the Changelog</a>, and make some money! :-)';
118
 
119
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
120
  }
136
  $notice = '<strong>Multisite Network</strong> updated automatically by <strong>s2Member</strong> v'.esc_html(WS_PLUGIN__S2MEMBER_VERSION).'.<br />';
137
  $notice .= 'You\'ll want to configure s2Member\'s Multisite options now.<br />';
138
  $notice .= 'In the Dashboard for your Main Site, see:<br />';
139
+ $notice .= '<strong>s2Member Multisite (Config)</strong>.';
140
 
141
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, array('blog|network:plugins.php', 'blog|network:ws-plugin--s2member-start', 'blog|network:ws-plugin--s2member-mms-ops', 'blog|network:ws-plugin--s2member-gen-ops', 'blog|network:ws-plugin--s2member-res-ops'));
142
 
includes/classes/list-servers.inc.php CHANGED
@@ -83,7 +83,7 @@ if(!class_exists('c_ws_plugin__s2member_list_servers'))
83
  *
84
  * @param bool $opt_in Defaults to `FALSE`; must be set to `TRUE`.
85
  * @param bool $double_opt_in Defaults to `TRUE`. Use at your own risk.
86
- * @param bool $clean_user_cache Defaults to `TRUE`; i.e. we start from a fresh copy of the current user.
87
  *
88
  * @return bool True if at least one list server is processed successfully.
89
  */
@@ -166,7 +166,7 @@ if(!class_exists('c_ws_plugin__s2member_list_servers'))
166
  * @package s2Member\List_Servers
167
  *
168
  * @param bool $opt_out Defaults to `FALSE`; must be set to `TRUE`.
169
- * @param bool $clean_user_cache Defaults to `TRUE`; i.e. we start from a fresh copy of the current user.
170
  *
171
  * @return bool True if at least one list server removal is processed successfully.
172
  */
83
  *
84
  * @param bool $opt_in Defaults to `FALSE`; must be set to `TRUE`.
85
  * @param bool $double_opt_in Defaults to `TRUE`. Use at your own risk.
86
+ * @param bool $clean_user_cache Defaults to `TRUE`; i.e., we start from a fresh copy of the current user.
87
  *
88
  * @return bool True if at least one list server is processed successfully.
89
  */
166
  * @package s2Member\List_Servers
167
  *
168
  * @param bool $opt_out Defaults to `FALSE`; must be set to `TRUE`.
169
+ * @param bool $clean_user_cache Defaults to `TRUE`; i.e., we start from a fresh copy of the current user.
170
  *
171
  * @return bool True if at least one list server removal is processed successfully.
172
  */
includes/classes/login-checks.inc.php CHANGED
@@ -140,7 +140,7 @@ if(!class_exists('c_ws_plugin__s2member_login_checks'))
140
  * @package s2Member\Login_Checks
141
  * @since 131025
142
  *
143
- * @param string $username Expects a username (e.g. a `user_login` value).
144
  *
145
  * @return integer Current number of simultaneous logins.
146
  */
@@ -156,7 +156,7 @@ if(!class_exists('c_ws_plugin__s2member_login_checks'))
156
  $transient_entries = $prefix.md5('s2member_simultaneous_login_entries_for_'.strtolower((string)$username));
157
 
158
  $timeout = $GLOBALS['WS_PLUGIN__']['s2member']['o']['max_simultaneous_logins_timeout'];
159
- $timeout_ago = strtotime('-'.$timeout); // e.g. 30 minutes ago.
160
 
161
  $entries = (is_array($entries = get_transient($transient_entries))) ? $entries : array();
162
  foreach($entries as $_entry => $_time /* Auto-expire entries, based on time. */)
@@ -173,7 +173,7 @@ if(!class_exists('c_ws_plugin__s2member_login_checks'))
173
  *
174
  * @attaches-to ``add_action('wp_login');``
175
  *
176
- * @param string $username Expects a username (e.g. a `user_login` value).
177
  * @param WP_User|null $user When fired against `wp_login` this receives a WP_User object also.
178
  * @param string $action Default action is to increment the counter. This can be set to `decrement` or NULL to do nothing.
179
  */
@@ -189,7 +189,7 @@ if(!class_exists('c_ws_plugin__s2member_login_checks'))
189
  $transient_entries = $prefix.md5('s2member_simultaneous_login_entries_for_'.strtolower((string)$username));
190
 
191
  $timeout = $GLOBALS['WS_PLUGIN__']['s2member']['o']['max_simultaneous_logins_timeout'];
192
- $timeout_ago = strtotime('-'.$timeout); // e.g. 30 minutes ago.
193
 
194
  $entries = (is_array($entries = get_transient($transient_entries))) ? $entries : array();
195
  foreach($entries as $_entry => $_time /* Auto-expire entries, based on time. */)
140
  * @package s2Member\Login_Checks
141
  * @since 131025
142
  *
143
+ * @param string $username Expects a username (e.g., a `user_login` value).
144
  *
145
  * @return integer Current number of simultaneous logins.
146
  */
156
  $transient_entries = $prefix.md5('s2member_simultaneous_login_entries_for_'.strtolower((string)$username));
157
 
158
  $timeout = $GLOBALS['WS_PLUGIN__']['s2member']['o']['max_simultaneous_logins_timeout'];
159
+ $timeout_ago = strtotime('-'.$timeout); // e.g., 30 minutes ago.
160
 
161
  $entries = (is_array($entries = get_transient($transient_entries))) ? $entries : array();
162
  foreach($entries as $_entry => $_time /* Auto-expire entries, based on time. */)
173
  *
174
  * @attaches-to ``add_action('wp_login');``
175
  *
176
+ * @param string $username Expects a username (e.g., a `user_login` value).
177
  * @param WP_User|null $user When fired against `wp_login` this receives a WP_User object also.
178
  * @param string $action Default action is to increment the counter. This can be set to `decrement` or NULL to do nothing.
179
  */
189
  $transient_entries = $prefix.md5('s2member_simultaneous_login_entries_for_'.strtolower((string)$username));
190
 
191
  $timeout = $GLOBALS['WS_PLUGIN__']['s2member']['o']['max_simultaneous_logins_timeout'];
192
+ $timeout_ago = strtotime('-'.$timeout); // e.g., 30 minutes ago.
193
 
194
  $entries = (is_array($entries = get_transient($transient_entries))) ? $entries : array();
195
  foreach($entries as $_entry => $_time /* Auto-expire entries, based on time. */)
includes/classes/login-customizations.inc.php CHANGED
@@ -76,7 +76,7 @@ if(!class_exists('c_ws_plugin__s2member_login_customizations'))
76
  }
77
 
78
  /**
79
- * Styles login/registration *( i.e. `/wp-login.php` )*.
80
  *
81
  * @package s2Member\Login_Customizations
82
  * @since 3.5
76
  }
77
 
78
  /**
79
+ * Styles login/registration *( i.e., `/wp-login.php` )*.
80
  *
81
  * @package s2Member\Login_Customizations
82
  * @since 3.5
includes/classes/login-redirects.inc.php CHANGED
@@ -209,10 +209,10 @@ if(!class_exists('c_ws_plugin__s2member_login_redirects'))
209
  $url = preg_replace('/%%current_user_logins%%/i', c_ws_plugin__s2member_utils_strings::esc_refs(urlencode($user_logins)), $url);
210
 
211
  if($url !== $orig_url && (!($parse = c_ws_plugin__s2member_utils_urls::parse_url($url, -1, FALSE)) || (!empty($parse['path']) && strpos($parse['path'], '//') !== FALSE)))
212
- $url = home_url('/'); // Defaults to Home Page. We don't return invalid URLs produced by empty Replacement Codes ( i.e. with `//` ).
213
 
214
  if($root_returns_false && c_ws_plugin__s2member_utils_conds::is_site_root($url)) // Used by s2Member's security gate.
215
- $url = FALSE; // In case we need to return false on root URLs (i.e. don't protect the Home Page inadvertently).
216
 
217
  return apply_filters('ws_plugin__s2member_fill_login_redirect_rc_vars', $url, get_defined_vars());
218
  }
209
  $url = preg_replace('/%%current_user_logins%%/i', c_ws_plugin__s2member_utils_strings::esc_refs(urlencode($user_logins)), $url);
210
 
211
  if($url !== $orig_url && (!($parse = c_ws_plugin__s2member_utils_urls::parse_url($url, -1, FALSE)) || (!empty($parse['path']) && strpos($parse['path'], '//') !== FALSE)))
212
+ $url = home_url('/'); // Defaults to Home Page. We don't return invalid URLs produced by empty Replacement Codes ( i.e., with `//` ).
213
 
214
  if($root_returns_false && c_ws_plugin__s2member_utils_conds::is_site_root($url)) // Used by s2Member's security gate.
215
+ $url = FALSE; // In case we need to return false on root URLs (i.e., don't protect the Home Page inadvertently).
216
 
217
  return apply_filters('ws_plugin__s2member_fill_login_redirect_rc_vars', $url, get_defined_vars());
218
  }
includes/classes/mailchimp-o.inc.php CHANGED
@@ -105,7 +105,7 @@ if(!class_exists('c_ws_plugin__s2member_mailchimp_o'))
105
 
106
  if($_mc['api_response'] = $mc_api->{$_mc['api_method']}($_mc['list_id'], $args->email, // See: `http://apidocs.mailchimp.com/` for full details.
107
  ($_mc['api_merge_array'] = apply_filters('ws_plugin__s2member_mailchimp_merge_array', $_mc['merge_array'], get_defined_vars())), // Configured merge array above.
108
- ($_mc['api_email_type'] = apply_filters('ws_plugin__s2member_mailchimp_email_type', 'html', get_defined_vars())), // Type of email to receive (i.e. html,text,mobile).
109
  ($_mc['api_double_optin'] = apply_filters('ws_plugin__s2member_mailchimp_double_optin', $args->double_opt_in, get_defined_vars())), // Abuse of this may cause account suspension.
110
  ($_mc['api_update_existing'] = apply_filters('ws_plugin__s2member_mailchimp_update_existing', TRUE, get_defined_vars())), // Existing subscribers should be updated with this?
111
  ($_mc['api_replace_interests'] = apply_filters('ws_plugin__s2member_mailchimp_replace_interests', TRUE, get_defined_vars())), // Replace interest groups? (only if provided).
105
 
106
  if($_mc['api_response'] = $mc_api->{$_mc['api_method']}($_mc['list_id'], $args->email, // See: `http://apidocs.mailchimp.com/` for full details.
107
  ($_mc['api_merge_array'] = apply_filters('ws_plugin__s2member_mailchimp_merge_array', $_mc['merge_array'], get_defined_vars())), // Configured merge array above.
108
+ ($_mc['api_email_type'] = apply_filters('ws_plugin__s2member_mailchimp_email_type', 'html', get_defined_vars())), // Type of email to receive (i.e., html,text,mobile).
109
  ($_mc['api_double_optin'] = apply_filters('ws_plugin__s2member_mailchimp_double_optin', $args->double_opt_in, get_defined_vars())), // Abuse of this may cause account suspension.
110
  ($_mc['api_update_existing'] = apply_filters('ws_plugin__s2member_mailchimp_update_existing', TRUE, get_defined_vars())), // Existing subscribers should be updated with this?
111
  ($_mc['api_replace_interests'] = apply_filters('ws_plugin__s2member_mailchimp_replace_interests', TRUE, get_defined_vars())), // Replace interest groups? (only if provided).
includes/classes/mailchimp.inc.php CHANGED
@@ -107,7 +107,7 @@ if(!class_exists('c_ws_plugin__s2member_mailchimp'))
107
  {
108
  if(($_mc['api_response'] = $mc_api->lists->subscribe($_mc['list_id'], array('email' => $args->email), // See: `http://apidocs.mailchimp.com/` for full details.
109
  ($_mc['api_merge_array'] = apply_filters('ws_plugin__s2member_mailchimp_merge_array', $_mc['merge_array'], get_defined_vars())), // Configured merge array above.
110
- ($_mc['api_email_type'] = apply_filters('ws_plugin__s2member_mailchimp_email_type', 'html', get_defined_vars())), // Type of email to receive (i.e. html,text,mobile).
111
  ($_mc['api_double_optin'] = apply_filters('ws_plugin__s2member_mailchimp_double_optin', $args->double_opt_in, get_defined_vars())), // Abuse of this may cause account suspension.
112
  ($_mc['api_update_existing'] = apply_filters('ws_plugin__s2member_mailchimp_update_existing', TRUE, get_defined_vars())), // Existing subscribers should be updated with this?
113
  ($_mc['api_replace_interests'] = apply_filters('ws_plugin__s2member_mailchimp_replace_interests', TRUE, get_defined_vars())), // Replace interest groups? (only if provided).
107
  {
108
  if(($_mc['api_response'] = $mc_api->lists->subscribe($_mc['list_id'], array('email' => $args->email), // See: `http://apidocs.mailchimp.com/` for full details.
109
  ($_mc['api_merge_array'] = apply_filters('ws_plugin__s2member_mailchimp_merge_array', $_mc['merge_array'], get_defined_vars())), // Configured merge array above.
110
+ ($_mc['api_email_type'] = apply_filters('ws_plugin__s2member_mailchimp_email_type', 'html', get_defined_vars())), // Type of email to receive (i.e., html,text,mobile).
111
  ($_mc['api_double_optin'] = apply_filters('ws_plugin__s2member_mailchimp_double_optin', $args->double_opt_in, get_defined_vars())), // Abuse of this may cause account suspension.
112
  ($_mc['api_update_existing'] = apply_filters('ws_plugin__s2member_mailchimp_update_existing', TRUE, get_defined_vars())), // Existing subscribers should be updated with this?
113
  ($_mc['api_replace_interests'] = apply_filters('ws_plugin__s2member_mailchimp_replace_interests', TRUE, get_defined_vars())), // Replace interest groups? (only if provided).
includes/classes/menu-pages-rs.inc.php CHANGED
@@ -50,7 +50,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_pages_rs"))
50
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["menu_pages"]["upsell-pro"])
51
  {
52
  echo '<div class="ws-menu-page-others">' . "\n";
53
- echo '<a href="' . esc_attr (c_ws_plugin__s2member_readmes::parse_readme_value ("Pro Module / Prices")) . '" target="_blank"><img src="' . esc_attr ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]) . '/images/brand-upsell-pro.png" alt="." /></a>' . "\n";
54
  echo '</div>' . "\n";
55
  }
56
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["menu_pages"]["installation"])
50
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["menu_pages"]["upsell-pro"])
51
  {
52
  echo '<div class="ws-menu-page-others">' . "\n";
53
+ echo '<a href="' . esc_attr (c_ws_plugin__s2member_readmes::parse_readme_value ("Pro Add-on / Prices")) . '" target="_blank"><img src="' . esc_attr ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]) . '/images/brand-upsell-pro.png" alt="." /></a>' . "\n";
54
  echo '</div>' . "\n";
55
  }
56
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["menu_pages"]["installation"])
includes/classes/menu-pages-tb.inc.php CHANGED
@@ -47,7 +47,7 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages_tb'))
47
  }
48
  if($GLOBALS['WS_PLUGIN__']['s2member']['c']['menu_pages']['upsell-pro'])
49
  {
50
- echo '<a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value('Pro Module / Prices')).'" target="_blank" style="font-size:120%; font-weight:bold;"><i class="fa fa-money"></i> s2Member® Pro (Upgrade)</a>'."\n";
51
  }
52
  if($GLOBALS['WS_PLUGIN__']['s2member']['c']['menu_pages']['installation'])
53
  {
47
  }
48
  if($GLOBALS['WS_PLUGIN__']['s2member']['c']['menu_pages']['upsell-pro'])
49
  {
50
+ echo '<a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value('Pro Add-on / Prices')).'" target="_blank" style="font-size:120%; font-weight:bold;"><i class="fa fa-money"></i> s2Member® Pro (Upgrade)</a>'."\n";
51
  }
52
  if($GLOBALS['WS_PLUGIN__']['s2member']['c']['menu_pages']['installation'])
53
  {
includes/classes/menu-pages.inc.php CHANGED
@@ -96,25 +96,25 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
96
 
97
  if(empty($_GET['page']) || $_GET['page'] !== 'ws-plugin--s2member-mms-ops') // Do NOT display page-conflict-warnings on the Main Multisite Configuration panel.
98
  {
99
- if(!$options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>NOTE:</strong> s2Member security restrictions will NOT be enforced until you\'ve configured a Membership Options Page. See: <code>s2Member -› General Options -› Membership Options Page</code>.'))
100
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
101
 
102
- if($options['login_welcome_page'] && $options['login_welcome_page'] === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is the same as your Membership Options Page. Please correct this. See: <code>s2Member -› General Options -› Login Welcome Page</code>.'))
103
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
104
 
105
- if($options['membership_options_page'] && (string)get_option('page_on_front') === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Membership Options Page is currently configured as your Home Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this. See: <code>WordPress -› Reading Options</code>. Or change: <code>s2Member -› General Options -› Membership Options Page</code>.'))
106
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
107
 
108
- if($options['login_welcome_page'] && (string)get_option('page_on_front') === $options['login_welcome_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is currently configured as your Home Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this. See: <code>WordPress -› Reading Options</code>. Or change: <code>s2Member -› General Options -› Login Welcome Page</code>.'))
109
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
110
 
111
- if($options['membership_options_page'] && (string)get_option('page_for_posts') === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Membership Options Page is currently configured as your Posts Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this. See: <code>WordPress -› Reading Options</code>. Or change: <code>s2Member -› General Options -› Membership Options Page</code>.'))
112
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
113
 
114
- if($options['login_welcome_page'] && (string)get_option('page_for_posts') === $options['login_welcome_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is currently configured as your Posts Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this. See: <code>WordPress -› Reading Options</code>. Or change: <code>s2Member -› General Options -› Login Welcome Page</code>.'))
115
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
116
 
117
- if($options['file_download_limit_exceeded_page'] && $options['file_download_limit_exceeded_page'] === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Download Limit Exceeded Page is the same as your Membership Options Page. Please correct this. See: <code>s2Member -› Download Options</code>.'))
118
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
119
  }
120
  $updated_all_options = TRUE; // Flag indicating this routine was processed successfully; and that all s2Member options have been updated successfully.
@@ -205,12 +205,6 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
205
  if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_logs_page', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
206
  add_submenu_page($menu, 's2Member Logs', 'Log Files (Debug)', 'create_users', 'ws-plugin--s2member-logs', 'c_ws_plugin__s2member_menu_pages::logs_page');
207
 
208
- if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_divider_6', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
209
- add_submenu_page($menu, '', '<span style="display:block; margin:1px 0 1px -5px; padding:0; height:1px; line-height:1px; background:#CCCCCC;"></span>', 'create_users', '#');
210
-
211
- if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_info_page', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
212
- add_submenu_page($menu, 's2Member Information', 's2Member Info', 'create_users', 'ws-plugin--s2member-info', 'c_ws_plugin__s2member_menu_pages::info_page');
213
-
214
  do_action('ws_plugin__s2member_during_add_admin_options_additional_pages', get_defined_vars());
215
  }
216
  do_action('ws_plugin__s2member_after_add_admin_options', get_defined_vars());
@@ -239,9 +233,6 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
239
 
240
  add_submenu_page($menu, 's2Member Multisite (Configuration)', 'Multisite (Config)', 'create_users', 'ws-plugin--s2member-mms-ops', 'c_ws_plugin__s2member_menu_pages::mms_ops_page');
241
 
242
- if(apply_filters('ws_plugin__s2member_during_add_network_admin_options_add_info_page', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
243
- add_submenu_page($menu, 's2Member Information', 's2Member Info', 'create_users', 'ws-plugin--s2member-info', 'c_ws_plugin__s2member_menu_pages::info_page');
244
-
245
  do_action('ws_plugin__s2member_during_add_network_admin_options_additional_pages', get_defined_vars());
246
  }
247
  do_action('ws_plugin__s2member_after_add_network_admin_options', get_defined_vars());
@@ -626,7 +617,8 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
626
  c_ws_plugin__s2member_menu_pages::update_all_options();
627
 
628
  if(!empty($_REQUEST['ws_plugin__s2member_cf_options_reset'])
629
- && wp_verify_nonce($_REQUEST['ws_plugin__s2member_cf_options_reset'], 'ws-plugin--s2member-cf-options-reset'))
 
630
  {
631
  c_ws_plugin__s2member_files_in::reset_aws_cf_config_values(); // A full CloudFront reset.
632
  c_ws_plugin__s2member_admin_notices::display_admin_notice('Amazon CloudFront configuration reset successfully.');
@@ -662,7 +654,7 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
662
 
663
  if(!empty($_POST['ws_plugin__s2member_amazon_cf_files_auto_configure_distros']) && ($nonce = $_POST['ws_plugin__s2member_amazon_cf_files_auto_configure_distros']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-amazon-cf-files-auto-configure-distros'))
664
  if(($amazon_cf_auto_configure_distros = c_ws_plugin__s2member_files_in::amazon_cf_auto_configure_distros()) && $amazon_cf_auto_configure_distros['success'])
665
- c_ws_plugin__s2member_admin_notices::display_admin_notice('Amazon CloudFront Distributions auto-configured successfully. Please allow 30 minutes for initial propagation. <strong>Tip:</strong> If you try to stream over the RTMP protocol using something like the <code>[s2Stream /]</code> shortcode, and you keep getting an "ID Not Found" error while using JW Player; please note that it can <em>sometimes</em> take a full 24 hours for RTMP (i.e. streaming distributions) to begin working properly. This is because there are a few initialization routines that must complete on the AWS side when you first integrate with CloudFront. Please be patient.'.(($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_cname']) ? '<br /><em>Downloads Distribution CNAME: <code>'.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_cname']).' &mdash;&raquo; '.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_dname']).'</code></em>' : '').(($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_cname']) ? '<br /><em>Streaming Distribution CNAME: <code>'.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_cname']).' &mdash;&raquo; '.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_dname']).'</code></em>' : ''));
666
  else // Else there was an error. We need to report this back to the site owner so they can understand what's going on.
667
  (c_ws_plugin__s2member_menu_pages::$pre_display_errors['cf_files_auto_configure_distros'] = TRUE).c_ws_plugin__s2member_admin_notices::display_admin_notice('Unable to auto-configure Amazon CloudFront Distributions.<br />Error code: <code>'.esc_html($amazon_cf_auto_configure_distros['code']).'</code>. Error Message: <code>'.esc_html($amazon_cf_auto_configure_distros['message']).'</code>', TRUE);
668
 
@@ -677,7 +669,6 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
677
  do_action('ws_plugin__s2member_after_down_ops_page', get_defined_vars());
678
  }
679
 
680
-
681
  /**
682
  * Builds and handles the API Tracking options page.
683
  *
@@ -740,7 +731,7 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
740
  do_action('ws_plugin__s2member_before_paypal_buttons_page', get_defined_vars());
741
 
742
  if(!$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_business'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_merchant_id'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_username'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_password'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_signature'])
743
- c_ws_plugin__s2member_admin_notices::display_admin_notice('Please configure <code>s2Member -› PayPal Options</code> first. Once all of your PayPal Options are configured; including your Email Address, Merchant ID, API Username, Password, and Signature; return to this page &amp; generate your PayPal Button(s).', TRUE);
744
 
745
  include_once dirname(dirname(__FILE__)).'/menu-pages/paypal-buttons.inc.php';
746
 
@@ -823,20 +814,5 @@ if(!class_exists('c_ws_plugin__s2member_menu_pages'))
823
 
824
  do_action('ws_plugin__s2member_after_logs_page', get_defined_vars());
825
  }
826
-
827
- /**
828
- * Builds and handles the s2Member Info page.
829
- *
830
- * @package s2Member\Menu_Pages
831
- * @since 3.5
832
- */
833
- public static function info_page()
834
- {
835
- do_action('ws_plugin__s2member_before_info_page', get_defined_vars());
836
-
837
- include_once dirname(dirname(__FILE__)).'/menu-pages/info.inc.php';
838
-
839
- do_action('ws_plugin__s2member_after_info_page', get_defined_vars());
840
- }
841
  }
842
  }
96
 
97
  if(empty($_GET['page']) || $_GET['page'] !== 'ws-plugin--s2member-mms-ops') // Do NOT display page-conflict-warnings on the Main Multisite Configuration panel.
98
  {
99
+ if(!$options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>NOTE:</strong> s2Member security restrictions will NOT be enforced until you\'ve configured a Membership Options Page. See: <strong>s2Member General Options Membership Options Page</strong>.'))
100
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
101
 
102
+ if($options['login_welcome_page'] && $options['login_welcome_page'] === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is the same as your Membership Options Page. Please correct this. See: <strong>s2Member General Options Login Welcome Page</strong>.'))
103
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
104
 
105
+ if($options['membership_options_page'] && (string)get_option('page_on_front') === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Membership Options Page is currently configured as your Home Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this. See: <strong>WordPress Reading Options</strong>. Or change: <strong>s2Member General Options Membership Options Page</strong>.'))
106
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
107
 
108
+ if($options['login_welcome_page'] && (string)get_option('page_on_front') === $options['login_welcome_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is currently configured as your Home Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this. See: <strong>WordPress Reading Options</strong>. Or change: <strong>s2Member General Options Login Welcome Page</strong>.'))
109
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
110
 
111
+ if($options['membership_options_page'] && (string)get_option('page_for_posts') === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Membership Options Page is currently configured as your Posts Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this. See: <strong>WordPress Reading Options</strong>. Or change: <strong>s2Member General Options Membership Options Page</strong>.'))
112
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
113
 
114
+ if($options['login_welcome_page'] && (string)get_option('page_for_posts') === $options['login_welcome_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is currently configured as your Posts Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this. See: <strong>WordPress Reading Options</strong>. Or change: <strong>s2Member General Options Login Welcome Page</strong>.'))
115
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
116
 
117
+ if($options['file_download_limit_exceeded_page'] && $options['file_download_limit_exceeded_page'] === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Download Limit Exceeded Page is the same as your Membership Options Page. Please correct this. See: <strong>s2Member Download Options</strong>.'))
118
  ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
119
  }
120
  $updated_all_options = TRUE; // Flag indicating this routine was processed successfully; and that all s2Member options have been updated successfully.
205
  if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_logs_page', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
206
  add_submenu_page($menu, 's2Member Logs', 'Log Files (Debug)', 'create_users', 'ws-plugin--s2member-logs', 'c_ws_plugin__s2member_menu_pages::logs_page');
207
 
 
 
 
 
 
 
208
  do_action('ws_plugin__s2member_during_add_admin_options_additional_pages', get_defined_vars());
209
  }
210
  do_action('ws_plugin__s2member_after_add_admin_options', get_defined_vars());
233
 
234
  add_submenu_page($menu, 's2Member Multisite (Configuration)', 'Multisite (Config)', 'create_users', 'ws-plugin--s2member-mms-ops', 'c_ws_plugin__s2member_menu_pages::mms_ops_page');
235
 
 
 
 
236
  do_action('ws_plugin__s2member_during_add_network_admin_options_additional_pages', get_defined_vars());
237
  }
238
  do_action('ws_plugin__s2member_after_add_network_admin_options', get_defined_vars());
617
  c_ws_plugin__s2member_menu_pages::update_all_options();
618
 
619
  if(!empty($_REQUEST['ws_plugin__s2member_cf_options_reset'])
620
+ && wp_verify_nonce($_REQUEST['ws_plugin__s2member_cf_options_reset'], 'ws-plugin--s2member-cf-options-reset')
621
+ )
622
  {
623
  c_ws_plugin__s2member_files_in::reset_aws_cf_config_values(); // A full CloudFront reset.
624
  c_ws_plugin__s2member_admin_notices::display_admin_notice('Amazon CloudFront configuration reset successfully.');
654
 
655
  if(!empty($_POST['ws_plugin__s2member_amazon_cf_files_auto_configure_distros']) && ($nonce = $_POST['ws_plugin__s2member_amazon_cf_files_auto_configure_distros']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-amazon-cf-files-auto-configure-distros'))
656
  if(($amazon_cf_auto_configure_distros = c_ws_plugin__s2member_files_in::amazon_cf_auto_configure_distros()) && $amazon_cf_auto_configure_distros['success'])
657
+ c_ws_plugin__s2member_admin_notices::display_admin_notice('Amazon CloudFront Distributions auto-configured successfully. Please allow 30 minutes for initial propagation. <strong>Tip:</strong> If you try to stream over the RTMP protocol using something like the <code>[s2Stream /]</code> shortcode, and you keep getting an "ID Not Found" error while using JW Player; please note that it can <em>sometimes</em> take a full 24 hours for RTMP (i.e., streaming distributions) to begin working properly. This is because there are a few initialization routines that must complete on the AWS side when you first integrate with CloudFront. Please be patient.'.(($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_cname']) ? '<br /><em>Downloads Distribution CNAME: <code>'.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_cname']).' &mdash;&raquo; '.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_dname']).'</code></em>' : '').(($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_cname']) ? '<br /><em>Streaming Distribution CNAME: <code>'.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_cname']).' &mdash;&raquo; '.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_dname']).'</code></em>' : ''));
658
  else // Else there was an error. We need to report this back to the site owner so they can understand what's going on.
659
  (c_ws_plugin__s2member_menu_pages::$pre_display_errors['cf_files_auto_configure_distros'] = TRUE).c_ws_plugin__s2member_admin_notices::display_admin_notice('Unable to auto-configure Amazon CloudFront Distributions.<br />Error code: <code>'.esc_html($amazon_cf_auto_configure_distros['code']).'</code>. Error Message: <code>'.esc_html($amazon_cf_auto_configure_distros['message']).'</code>', TRUE);
660
 
669
  do_action('ws_plugin__s2member_after_down_ops_page', get_defined_vars());
670
  }
671
 
 
672
  /**
673
  * Builds and handles the API Tracking options page.
674
  *
731
  do_action('ws_plugin__s2member_before_paypal_buttons_page', get_defined_vars());
732
 
733
  if(!$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_business'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_merchant_id'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_username'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_password'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_signature'])
734
+ c_ws_plugin__s2member_admin_notices::display_admin_notice('Please configure <strong>s2Member PayPal Options</strong> first. Once all of your PayPal Options are configured; including your Email Address, Merchant ID, API Username, Password, and Signature; return to this page &amp; generate your PayPal Button(s).', TRUE);
735
 
736
  include_once dirname(dirname(__FILE__)).'/menu-pages/paypal-buttons.inc.php';
737
 
814
 
815
  do_action('ws_plugin__s2member_after_logs_page', get_defined_vars());
816
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817
  }
818
  }
includes/classes/meta-box-security.inc.php CHANGED
@@ -69,7 +69,7 @@ if(!class_exists("c_ws_plugin__s2member_meta_box_security"))
69
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Posts of this type)</option>'."\n")
70
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Pages)</option>'."\n";
71
 
72
- echo '</select><br /><small>* see: <code>Restriction Options -› Pages</code></small>'."\n";
73
 
74
  if(!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site())
75
  // ^ Will change once Custom Capabilities are compatible with a Blog Farm.
@@ -77,7 +77,7 @@ if(!class_exists("c_ws_plugin__s2member_meta_box_security"))
77
  echo '<p style="margin-top:15px; margin-left:2px;"><strong>Require Custom Capabilities?</strong></p>'."\n";
78
  echo '<label class="screen-reader-text" for="ws-plugin--s2member-security-meta-box-ccaps">Custom Capabilities?</label>'."\n";
79
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_security_meta_box_ccaps" id="ws-plugin--s2member-security-meta-box-ccaps" value="'.format_to_edit(trim(implode(",", (array)get_post_meta($page_id, "s2member_ccaps_req", true)))).'" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" style="width:99%;" />'."\n";
80
- echo '<br /><small>* see: <code>API Scripting -› Custom Capabilities</code></small>'."\n";
81
  }
82
  }
83
 
@@ -115,7 +115,7 @@ if(!class_exists("c_ws_plugin__s2member_meta_box_security"))
115
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Posts of this type)</option>'."\n")
116
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Posts)</option>'."\n";
117
 
118
- echo '</select><br /><small>* see: <code>Restriction Options -› Posts</code></small>'."\n";
119
 
120
  if(!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site())
121
  // ^ Will change once Custom Capabilities are compatible with a Blog Farm.
@@ -123,7 +123,7 @@ if(!class_exists("c_ws_plugin__s2member_meta_box_security"))
123
  echo '<p style="margin-top:15px; margin-left:2px;"><strong>Require Custom Capabilities?</strong></p>'."\n";
124
  echo '<label class="screen-reader-text" for="ws-plugin--s2member-security-meta-box-ccaps">Custom Capabilities?</label>'."\n";
125
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_security_meta_box_ccaps" id="ws-plugin--s2member-security-meta-box-ccaps" value="'.format_to_edit(trim(implode(",", (array)get_post_meta($post_id, "s2member_ccaps_req", true)))).'" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" style="width:99%;" />'."\n";
126
- echo '<br /><small>* see: <code>API Scripting -› Custom Capabilities</code></small>'."\n";
127
  }
128
  }
129
 
69
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Posts of this type)</option>'."\n")
70
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Pages)</option>'."\n";
71
 
72
+ echo '</select><br /><small>* see: <strong>Restriction Options Pages</strong></small>'."\n";
73
 
74
  if(!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site())
75
  // ^ Will change once Custom Capabilities are compatible with a Blog Farm.
77
  echo '<p style="margin-top:15px; margin-left:2px;"><strong>Require Custom Capabilities?</strong></p>'."\n";
78
  echo '<label class="screen-reader-text" for="ws-plugin--s2member-security-meta-box-ccaps">Custom Capabilities?</label>'."\n";
79
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_security_meta_box_ccaps" id="ws-plugin--s2member-security-meta-box-ccaps" value="'.format_to_edit(trim(implode(",", (array)get_post_meta($page_id, "s2member_ccaps_req", true)))).'" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" style="width:99%;" />'."\n";
80
+ echo '<br /><small>* see: <strong>API Scripting Custom Capabilities</strong></small>'."\n";
81
  }
82
  }
83
 
115
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Posts of this type)</option>'."\n")
116
  : '<option value="" disabled="disabled">Level #'.$n.' (already protects "all" Posts)</option>'."\n";
117
 
118
+ echo '</select><br /><small>* see: <strong>Restriction Options Posts</strong></small>'."\n";
119
 
120
  if(!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site())
121
  // ^ Will change once Custom Capabilities are compatible with a Blog Farm.
123
  echo '<p style="margin-top:15px; margin-left:2px;"><strong>Require Custom Capabilities?</strong></p>'."\n";
124
  echo '<label class="screen-reader-text" for="ws-plugin--s2member-security-meta-box-ccaps">Custom Capabilities?</label>'."\n";
125
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_security_meta_box_ccaps" id="ws-plugin--s2member-security-meta-box-ccaps" value="'.format_to_edit(trim(implode(",", (array)get_post_meta($post_id, "s2member_ccaps_req", true)))).'" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" style="width:99%;" />'."\n";
126
+ echo '<br /><small>* see: <strong>API Scripting Custom Capabilities</strong></small>'."\n";
127
  }
128
  }
129
 
includes/classes/meta-boxes.inc.php CHANGED
@@ -38,7 +38,7 @@ if (!class_exists ("c_ws_plugin__s2member_meta_boxes"))
38
  *
39
  * @attaches-to ``add_action("add_meta_boxes");``
40
  *
41
- * @param string $type String indicating type of Post, or another classification *( i.e. `nav_menu_item` )*.
42
  * @return null
43
  */
44
  public static function add_meta_boxes ($type = FALSE)
38
  *
39
  * @attaches-to ``add_action("add_meta_boxes");``
40
  *
41
+ * @param string $type String indicating type of Post, or another classification *( i.e., `nav_menu_item` )*.
42
  * @return null
43
  */
44
  public static function add_meta_boxes ($type = FALSE)
includes/classes/mo-page-in.inc.php CHANGED
@@ -120,7 +120,7 @@ if(!class_exists("c_ws_plugin__s2member_mo_page_in"))
120
  /*
121
  * s2Member's MOP Vars are now a double-dot (`..`) delimited list of six values.
122
  *
123
- * e.g. .../membership-options-page/
124
  * ?_s2member_vars=[restriction type]..[requirement type]..[requirement type value]..
125
  * [seeking type]..[seeking type value]..[seeking URI base 64 encoded]
126
  */
120
  /*
121
  * s2Member's MOP Vars are now a double-dot (`..`) delimited list of six values.
122
  *
123
+ * e.g., .../membership-options-page/
124
  * ?_s2member_vars=[restriction type]..[requirement type]..[requirement type value]..
125
  * [seeking type]..[seeking type value]..[seeking URI base 64 encoded]
126
  */
includes/classes/mo-page.inc.php CHANGED
@@ -73,7 +73,7 @@ if(!class_exists("c_ws_plugin__s2member_mo_page"))
73
  /*
74
  * s2Member's MOP Vars are now a double-dot (`..`) delimited list of six values.
75
  *
76
- * e.g. .../membership-options-page/
77
  * ?_s2member_vars=[restriction type]..[requirement type]..[requirement type value]..
78
  * [seeking type]..[seeking type value]..[seeking URI base 64 encoded]
79
  */
73
  /*
74
  * s2Member's MOP Vars are now a double-dot (`..`) delimited list of six values.
75
  *
76
+ * e.g., .../membership-options-page/
77
  * ?_s2member_vars=[restriction type]..[requirement type]..[requirement type value]..
78
  * [seeking type]..[seeking type value]..[seeking URI base 64 encoded]
79
  */
includes/classes/op-notices.inc.php CHANGED
@@ -45,7 +45,7 @@ if (!class_exists ("c_ws_plugin__s2member_op_notices"))
45
 
46
  if (is_blog_admin () && $pagenow === "options-general.php" && !isset ($_GET["page"]) && !is_multisite ()) // Multisite does NOT provide these options.
47
  {
48
- $notice = "<em>* Note: The s2Member plugin has control over two options on this page.<br /><code>Anyone Can Register = " . esc_html (get_option ("users_can_register")) . "</code>, and <code>New User Default Role = " . esc_html (get_option ("default_role")) . "</code>.<br />For further details, see: <code>s2Member -› General Options -› Open Registration</code>.";
49
 
50
  $js = '<script type="text/javascript">';
51
  $js .= "jQuery(document).ready(function(\$){ \$('input#users_can_register, select#default_role').attr('disabled', 'disabled'); });";
@@ -78,7 +78,7 @@ if (!class_exists ("c_ws_plugin__s2member_op_notices"))
78
 
79
  if (is_multisite () && is_network_admin () && in_array($pagenow, array("settings.php")) && !isset ($_GET["page"]))
80
  {
81
- $notice = "<em>* Note: The s2Member plugin has control over two options on this page.<br /><code>Allow Open Registration = " . esc_html (get_site_option ("registration")) . "</code> and <code>Add New Users = " . esc_html (get_site_option ("add_new_users")) . "</code>.<br />Please check: <code>s2Member -› Multisite (Config)</code>.";
82
 
83
  $js = '<script type="text/javascript">';
84
  $js .= "jQuery(document).ready(function(\$){ \$('input[name=registration], input#add_new_users').attr('disabled', 'disabled'); });";
@@ -114,19 +114,19 @@ if (!class_exists ("c_ws_plugin__s2member_op_notices"))
114
  do_action("ws_plugin__s2member_during_reading_ops_notice", get_defined_vars ()); // Now check for conflicts.
115
 
116
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"] && (string)get_option ("page_on_front") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"]
117
- && ($notice = '<strong>NOTE:</strong> Your Membership Options Page for s2Member is currently configured as your Home Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this.'))
118
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
119
 
120
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"] && (string)get_option ("page_on_front") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"]
121
- && ($notice = '<strong>NOTE:</strong> Your Login Welcome Page for s2Member is currently configured as your Home Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this.'))
122
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
123
 
124
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"] && (string)get_option ("page_for_posts") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"]
125
- && ($notice = '<strong>NOTE:</strong> Your Membership Options Page for s2Member is currently configured as your Posts Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this.'))
126
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
127
 
128
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"] && (string)get_option ("page_for_posts") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"]
129
- && ($notice = '<strong>NOTE:</strong> Your Login Welcome Page for s2Member is currently configured as your Posts Page (i.e. static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this.'))
130
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
131
  }
132
 
45
 
46
  if (is_blog_admin () && $pagenow === "options-general.php" && !isset ($_GET["page"]) && !is_multisite ()) // Multisite does NOT provide these options.
47
  {
48
+ $notice = "<em>* Note: The s2Member plugin has control over two options on this page.<br /><code>Anyone Can Register = " . esc_html (get_option ("users_can_register")) . "</code>, and <code>New User Default Role = " . esc_html (get_option ("default_role")) . "</code>.<br />For further details, see: <strong>s2Member General Options Open Registration</strong>.";
49
 
50
  $js = '<script type="text/javascript">';
51
  $js .= "jQuery(document).ready(function(\$){ \$('input#users_can_register, select#default_role').attr('disabled', 'disabled'); });";
78
 
79
  if (is_multisite () && is_network_admin () && in_array($pagenow, array("settings.php")) && !isset ($_GET["page"]))
80
  {
81
+ $notice = "<em>* Note: The s2Member plugin has control over two options on this page.<br /><code>Allow Open Registration = " . esc_html (get_site_option ("registration")) . "</code> and <code>Add New Users = " . esc_html (get_site_option ("add_new_users")) . "</code>.<br />Please check: <strong>s2Member Multisite (Config)</strong>.";
82
 
83
  $js = '<script type="text/javascript">';
84
  $js .= "jQuery(document).ready(function(\$){ \$('input[name=registration], input#add_new_users').attr('disabled', 'disabled'); });";
114
  do_action("ws_plugin__s2member_during_reading_ops_notice", get_defined_vars ()); // Now check for conflicts.
115
 
116
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"] && (string)get_option ("page_on_front") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"]
117
+ && ($notice = '<strong>NOTE:</strong> Your Membership Options Page for s2Member is currently configured as your Home Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this.'))
118
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
119
 
120
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"] && (string)get_option ("page_on_front") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"]
121
+ && ($notice = '<strong>NOTE:</strong> Your Login Welcome Page for s2Member is currently configured as your Home Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this.'))
122
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
123
 
124
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"] && (string)get_option ("page_for_posts") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"]
125
+ && ($notice = '<strong>NOTE:</strong> Your Membership Options Page for s2Member is currently configured as your Posts Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this.'))
126
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
127
 
128
  if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"] && (string)get_option ("page_for_posts") === $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"]
129
+ && ($notice = '<strong>NOTE:</strong> Your Login Welcome Page for s2Member is currently configured as your Posts Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this.'))
130
  c_ws_plugin__s2member_admin_notices::enqueue_admin_notice ($notice, "blog:" . $pagenow, true);
131
  }
132
 
includes/classes/paypal-notify-in-subscr-modify-w-level.inc.php CHANGED
@@ -136,9 +136,9 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in_subscr_modify_w_level')
136
 
137
  $paypal['s2member_log'][] = 's2Member Level/Capabilities updated on Subscription modification.';
138
 
139
- $sbj = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_subject']; // The same for standard and w/ Pro Forms.
140
- $msg = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_message']; // The same for standard and w/ Pro Forms.
141
- $rec = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_recipients']; // The same for standard and w/ Pro Forms.
142
 
143
  if(($rec = preg_replace('/%%cv([0-9]+)%%/ei', 'trim(@$cv[$1])', $rec)) && ($rec = preg_replace('/%%subscr_id%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_id']), $rec)))
144
  if(($rec = preg_replace('/%%subscr_baid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_baid']), $rec)) && ($rec = preg_replace('/%%subscr_cid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_cid']), $rec)))
136
 
137
  $paypal['s2member_log'][] = 's2Member Level/Capabilities updated on Subscription modification.';
138
 
139
+ $sbj = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_subject']; // The same for standard and w/ Pro-Forms.
140
+ $msg = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_message']; // The same for standard and w/ Pro-Forms.
141
+ $rec = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_recipients']; // The same for standard and w/ Pro-Forms.
142
 
143
  if(($rec = preg_replace('/%%cv([0-9]+)%%/ei', 'trim(@$cv[$1])', $rec)) && ($rec = preg_replace('/%%subscr_id%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_id']), $rec)))
144
  if(($rec = preg_replace('/%%subscr_baid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_baid']), $rec)) && ($rec = preg_replace('/%%subscr_cid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_cid']), $rec)))
includes/classes/paypal-notify-in-subscr-or-rp-eots-w-level.inc.php CHANGED
@@ -74,7 +74,7 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in_subscr_or_rp_eots_w_lev
74
  $paypal['s2member_log'][] = 's2Member `txn_type` identified as '.($identified_as = '( `[empty or irrelevant]` ) w/ `payment_status` ( `refunded|reversed|reversal` ) - or - `new_case` w/ `case_type` ( `chargeback` )').'.';
75
  else $paypal['s2member_log'][] = 's2Member `txn_type` identified as '.($identified_as = '( `subscr_eot|recurring_payment_expired|recurring_payment_suspended_due_to_max_failed_payment` ) - or - `recurring_payment_profile_cancel` w/ `initial_payment_status` ( `failed` )').'.';
76
 
77
- if(empty($_REQUEST['s2member_paypal_proxy'])) // Only on true PayPal IPNs; e.g. we can bypass this on proxied IPNs.
78
  {
79
  $paypal['s2member_log'][] = 'Sleeping for 10 seconds. Waiting for a possible ( `subscr_signup|subscr_modify|recurring_payment_profile_created` ).';
80
  sleep(10); // Sleep here for a moment. PayPal sometimes sends a subscr_eot before the subscr_signup, subscr_modify.
@@ -145,7 +145,7 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in_subscr_or_rp_eots_w_lev
145
  delete_user_option($user_id, 's2member_file_download_access_log');
146
 
147
  c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Demoted by s2Member: '.date('D M j, Y g:i a T'));
148
- c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Paid Subscr. ID @ time of demotion: '.$paypal['subscr_gateway'].' -› '.$paypal['subscr_id']);
149
 
150
  $paypal['s2member_log'][] = 'Member Level/Capabilities demoted to: '.ucwords(preg_replace('/_/', ' ', $demotion_role)).'.';
151
 
@@ -287,7 +287,7 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in_subscr_or_rp_eots_w_lev
287
 
288
  $auto_eot_time = c_ws_plugin__s2member_utils_time::auto_eot_time($user_id, $paypal['period1'], $paypal['period3'], '', time());
289
  /* We assume the last payment was today, because this is how newer PayPal accounts function with respect to EOT handling.
290
- Newer PayPal accounts ( i.e. Subscription IDs starting with `I-`, will have their EOT triggered upon the last payment. */
291
  update_user_option($user_id, 's2member_auto_eot_time', $auto_eot_time); // s2Member will follow-up on this later.
292
 
293
  $paypal['s2member_log'][] = 'Auto-EOT Time for this account (delayed), set to: '.date('D M j, Y g:i a T', $auto_eot_time);
74
  $paypal['s2member_log'][] = 's2Member `txn_type` identified as '.($identified_as = '( `[empty or irrelevant]` ) w/ `payment_status` ( `refunded|reversed|reversal` ) - or - `new_case` w/ `case_type` ( `chargeback` )').'.';
75
  else $paypal['s2member_log'][] = 's2Member `txn_type` identified as '.($identified_as = '( `subscr_eot|recurring_payment_expired|recurring_payment_suspended_due_to_max_failed_payment` ) - or - `recurring_payment_profile_cancel` w/ `initial_payment_status` ( `failed` )').'.';
76
 
77
+ if(empty($_REQUEST['s2member_paypal_proxy'])) // Only on true PayPal IPNs; e.g., we can bypass this on proxied IPNs.
78
  {
79
  $paypal['s2member_log'][] = 'Sleeping for 10 seconds. Waiting for a possible ( `subscr_signup|subscr_modify|recurring_payment_profile_created` ).';
80
  sleep(10); // Sleep here for a moment. PayPal sometimes sends a subscr_eot before the subscr_signup, subscr_modify.
145
  delete_user_option($user_id, 's2member_file_download_access_log');
146
 
147
  c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Demoted by s2Member: '.date('D M j, Y g:i a T'));
148
+ c_ws_plugin__s2member_user_notes::append_user_notes($user_id, 'Paid Subscr. ID @ time of demotion: '.$paypal['subscr_gateway'].' '.$paypal['subscr_id']);
149
 
150
  $paypal['s2member_log'][] = 'Member Level/Capabilities demoted to: '.ucwords(preg_replace('/_/', ' ', $demotion_role)).'.';
151
 
287
 
288
  $auto_eot_time = c_ws_plugin__s2member_utils_time::auto_eot_time($user_id, $paypal['period1'], $paypal['period3'], '', time());
289
  /* We assume the last payment was today, because this is how newer PayPal accounts function with respect to EOT handling.
290
+ Newer PayPal accounts ( i.e., Subscription IDs starting with `I-`, will have their EOT triggered upon the last payment. */
291
  update_user_option($user_id, 's2member_auto_eot_time', $auto_eot_time); // s2Member will follow-up on this later.
292
 
293
  $paypal['s2member_log'][] = 'Auto-EOT Time for this account (delayed), set to: '.date('D M j, Y g:i a T', $auto_eot_time);
includes/classes/paypal-notify-in-subscr-or-rp-payment-w-level.inc.php CHANGED
@@ -60,7 +60,7 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in_subscr_or_rp_payment_w_
60
  {
61
  $paypal['s2member_log'][] = 's2Member `txn_type` identified as '.($identified_as = '( `subscr_payment|recurring_payment` )').'.';
62
 
63
- if(empty($_REQUEST['s2member_paypal_proxy'])) // Only on true PayPal IPNs; e.g. we can bypass this on proxied IPNs.
64
  {
65
  $paypal['s2member_log'][] = 'Sleeping for 5 seconds. Waiting for a possible ( `subscr_signup|subscr_modify|recurring_payment_profile_created` ).';
66
  sleep(5); // Sleep here for a moment. PayPal sometimes sends a subscr_payment before the subscr_signup, subscr_modify.
60
  {
61
  $paypal['s2member_log'][] = 's2Member `txn_type` identified as '.($identified_as = '( `subscr_payment|recurring_payment` )').'.';
62
 
63
+ if(empty($_REQUEST['s2member_paypal_proxy'])) // Only on true PayPal IPNs; e.g., we can bypass this on proxied IPNs.
64
  {
65
  $paypal['s2member_log'][] = 'Sleeping for 5 seconds. Waiting for a possible ( `subscr_signup|subscr_modify|recurring_payment_profile_created` ).';
66
  sleep(5); // Sleep here for a moment. PayPal sometimes sends a subscr_payment before the subscr_signup, subscr_modify.
includes/classes/paypal-notify-in-subscr-or-wa-w-level.inc.php CHANGED
@@ -173,9 +173,9 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in_subscr_or_wa_w_level'))
173
 
174
  $paypal['s2member_log'][] = 's2Member Level/Capabilities updated w/ advanced update routines.';
175
 
176
- $sbj = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_subject']; // The same for standard and w/ Pro Forms.
177
- $msg = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_message']; // The same for standard and w/ Pro Forms.
178
- $rec = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_recipients']; // The same for standard and w/ Pro Forms.
179
 
180
  if(($rec = preg_replace('/%%cv([0-9]+)%%/ei', 'trim(@$cv[$1])', $rec)) && ($rec = preg_replace('/%%subscr_id%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_id']), $rec)))
181
  if(($rec = preg_replace('/%%subscr_baid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_baid']), $rec)) && ($rec = preg_replace('/%%subscr_cid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_cid']), $rec)))
173
 
174
  $paypal['s2member_log'][] = 's2Member Level/Capabilities updated w/ advanced update routines.';
175
 
176
+ $sbj = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_subject']; // The same for standard and w/ Pro-Forms.
177
+ $msg = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_message']; // The same for standard and w/ Pro-Forms.
178
+ $rec = $GLOBALS['WS_PLUGIN__']['s2member']['o']['modification_email_recipients']; // The same for standard and w/ Pro-Forms.
179
 
180
  if(($rec = preg_replace('/%%cv([0-9]+)%%/ei', 'trim(@$cv[$1])', $rec)) && ($rec = preg_replace('/%%subscr_id%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_id']), $rec)))
181
  if(($rec = preg_replace('/%%subscr_baid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_baid']), $rec)) && ($rec = preg_replace('/%%subscr_cid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['subscr_cid']), $rec)))
includes/classes/paypal-notify-in-wa-ccaps-wo-level.inc.php CHANGED
@@ -112,9 +112,9 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in_wa_ccaps_wo_level'))
112
  }
113
  $paypal['s2member_log'][] = 's2Member Custom Capabilities updated w/ advanced update routines.';
114
 
115
- $sbj = $GLOBALS['WS_PLUGIN__']['s2member']['o']['ccap_email_subject']; // The same for standard and w/ Pro Forms.
116
- $msg = $GLOBALS['WS_PLUGIN__']['s2member']['o']['ccap_email_message']; // The same for standard and w/ Pro Forms.
117
- $rec = $GLOBALS['WS_PLUGIN__']['s2member']['o']['ccap_email_recipients']; // The same for standard and w/ Pro Forms.
118
 
119
  if(($rec = preg_replace('/%%cv([0-9]+)%%/ei', 'trim(@$cv[$1])', $rec)) && ($rec = preg_replace('/%%(?:subscr|txn)_id%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['txn_id']), $rec)))
120
  if(($rec = preg_replace('/%%(?:subscr|txn)_baid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['txn_baid']), $rec)) && ($rec = preg_replace('/%%(?:subscr|txn)_cid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['txn_cid']), $rec)))
112
  }
113
  $paypal['s2member_log'][] = 's2Member Custom Capabilities updated w/ advanced update routines.';
114
 
115
+ $sbj = $GLOBALS['WS_PLUGIN__']['s2member']['o']['ccap_email_subject']; // The same for standard and w/ Pro-Forms.
116
+ $msg = $GLOBALS['WS_PLUGIN__']['s2member']['o']['ccap_email_message']; // The same for standard and w/ Pro-Forms.
117
+ $rec = $GLOBALS['WS_PLUGIN__']['s2member']['o']['ccap_email_recipients']; // The same for standard and w/ Pro-Forms.
118
 
119
  if(($rec = preg_replace('/%%cv([0-9]+)%%/ei', 'trim(@$cv[$1])', $rec)) && ($rec = preg_replace('/%%(?:subscr|txn)_id%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['txn_id']), $rec)))
120
  if(($rec = preg_replace('/%%(?:subscr|txn)_baid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['txn_baid']), $rec)) && ($rec = preg_replace('/%%(?:subscr|txn)_cid%%/i', c_ws_plugin__s2member_utils_strings::esc_refs($paypal['txn_cid']), $rec)))
includes/classes/paypal-notify-in.inc.php CHANGED
@@ -78,7 +78,7 @@ if(!class_exists('c_ws_plugin__s2member_paypal_notify_in'))
78
 
79
  if(!empty($paypal['txn_type']) && $paypal['txn_type'] === 'merch_pmt')
80
  // This is mostly irrelevant, but it helps to keep the logs cleaner.
81
- sleep(5); // Wait for Pro Form procesing to complete.
82
 
83
  if(empty($paypal['custom']) && !empty($paypal['recurring_payment_id'])) // Recurring Profile ID.
84
  $paypal['custom'] = c_ws_plugin__s2member_utils_users::get_user_custom_with($paypal['recurring_payment_id']);
78
 
79
  if(!empty($paypal['txn_type']) && $paypal['txn_type'] === 'merch_pmt')
80
  // This is mostly irrelevant, but it helps to keep the logs cleaner.
81
+ sleep(5); // Wait for Pro-Form procesing to complete.
82
 
83
  if(empty($paypal['custom']) && !empty($paypal['recurring_payment_id'])) // Recurring Profile ID.
84
  $paypal['custom'] = c_ws_plugin__s2member_utils_users::get_user_custom_with($paypal['recurring_payment_id']);
includes/classes/paypal-return-in-no-tx-data.inc.php CHANGED
@@ -45,7 +45,7 @@ if (!class_exists ("c_ws_plugin__s2member_paypal_return_in_no_tx_data"))
45
  unset($__refs, $__v);
46
 
47
  $paypal["s2member_log"][] = "No Return-Data. Customer MUST wait for Email Confirmation.";
48
- $paypal["s2member_log"][] = "Note. This can sometimes happen when/if you are offering an Initial/Trial Period. There are times when a Payment Gateway will NOT supply s2Member with any data immediately after checkout. When/if this happens, s2Member must process the transaction via IPN only (i.e. behind-the-scene), and the Customer must wait for Email Confirmation in these cases.";
49
  $paypal["s2member_log"][] = /* Recording _POST + _GET vars for analysis and debugging. */ var_export ($_REQUEST, true);
50
 
51
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
@@ -63,7 +63,7 @@ if (!class_exists ("c_ws_plugin__s2member_paypal_return_in_no_tx_data"))
63
  $paypal["s2member_log"][] = "Redirecting Customer to the Home Page (after asking Customer to check their email).";
64
 
65
  echo c_ws_plugin__s2member_return_templates::return_template ($paypal["subscr_gateway"],
66
- _x ('<strong>Thank you! (you MUST check your email before proceeding).</strong><br /><br />* Note: It can take <em>(up to 15 minutes)</em> for Email Confirmation with important details. If you don\'t receive email confirmation in the next 15 minutes, please contact Support.', "s2member-front", "s2member") . (($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"] || (c_ws_plugin__s2member_utils_conds::pro_is_installed () && !empty($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["pro_" . $paypal["subscr_gateway"] . "_sandbox"]))) ? '<br /><br />' . _x ('<strong>** Sandbox Mode **</strong> You may NOT receive this Email in Sandbox Mode. Sandbox addresses are usually bogus (for testing).', "s2member-front", "s2member") : ''),
67
  _x ("Back To Home Page", "s2member-front", "s2member"), home_url ("/"));
68
  }
69
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
45
  unset($__refs, $__v);
46
 
47
  $paypal["s2member_log"][] = "No Return-Data. Customer MUST wait for Email Confirmation.";
48
+ $paypal["s2member_log"][] = "Note. This can sometimes happen when/if you are offering an Initial/Trial Period. There are times when a Payment Gateway will NOT supply s2Member with any data immediately after checkout. When/if this happens, s2Member must process the transaction via IPN only (i.e., behind-the-scene), and the Customer must wait for Email Confirmation in these cases.";
49
  $paypal["s2member_log"][] = /* Recording _POST + _GET vars for analysis and debugging. */ var_export ($_REQUEST, true);
50
 
51
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
63
  $paypal["s2member_log"][] = "Redirecting Customer to the Home Page (after asking Customer to check their email).";
64
 
65
  echo c_ws_plugin__s2member_return_templates::return_template ($paypal["subscr_gateway"],
66
+ _x ('<strong>Thank you! (you MUST check your email before proceeding).</strong><br /><br />* Note: It can take <em>(up to 15 minutes)</em> for Email Confirmation with important details. If you don\'t receive email confirmation in the next 15 minutes, please contact Support.', "s2member-front", "s2member") . (($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"] || (c_ws_plugin__s2member_utils_conds::pro_is_installed () && !empty($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["pro_" . $paypal["subscr_gateway"] . "_sandbox"]))) ? '<br /><br />' . _x ('<strong>* Sandbox Mode *:</strong> You may NOT receive this Email in Sandbox Mode. Sandbox addresses are usually bogus (for testing).', "s2member-front", "s2member") : ''),
67
  _x ("Back To Home Page", "s2member-front", "s2member"), home_url ("/"));
68
  }
69
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
includes/classes/paypal-return-in-proxy-ty-email.inc.php CHANGED
@@ -44,7 +44,7 @@ if (!class_exists ("c_ws_plugin__s2member_paypal_return_in_proxy_ty_email"))
44
  do_action("ws_plugin__s2member_during_paypal_return_before_explicit_ty_email", get_defined_vars ());
45
  unset($__refs, $__v);
46
 
47
- $paypal["s2member_log"][] = "Customer MUST wait for Email Confirmation — `proxy_use`: ( `ty-email` ).";
48
 
49
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
50
  do_action("ws_plugin__s2member_during_paypal_return_during_explicit_ty_email", get_defined_vars ());
@@ -61,7 +61,7 @@ if (!class_exists ("c_ws_plugin__s2member_paypal_return_in_proxy_ty_email"))
61
  $paypal["s2member_log"][] = "Redirecting Customer to the Home Page (after asking Customer to check their email).";
62
 
63
  echo c_ws_plugin__s2member_return_templates::return_template ($paypal["subscr_gateway"],
64
- _x ('<strong>Thank you! (you MUST check your email before proceeding).</strong><br /><br />* Note: It can take <em>(up to 15 minutes)</em> for Email Confirmation with important details. If you don\'t receive email confirmation in the next 15 minutes, please contact Support.', "s2member-front", "s2member") . (($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"] || (c_ws_plugin__s2member_utils_conds::pro_is_installed () && !empty($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["pro_" . $paypal["subscr_gateway"] . "_sandbox"]))) ? '<br /><br />' . _x ('<strong>** Sandbox Mode **</strong> You may NOT receive this Email in Sandbox Mode. Sandbox addresses are usually bogus (for testing).', "s2member-front", "s2member") : ''),
65
  _x ("Back To Home Page", "s2member-front", "s2member"), home_url ("/"));
66
  }
67
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
44
  do_action("ws_plugin__s2member_during_paypal_return_before_explicit_ty_email", get_defined_vars ());
45
  unset($__refs, $__v);
46
 
47
+ $paypal["s2member_log"][] = "Customer MUST wait for Email Confirmation—`proxy_use`: ( `ty-email` ).";
48
 
49
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
50
  do_action("ws_plugin__s2member_during_paypal_return_during_explicit_ty_email", get_defined_vars ());
61
  $paypal["s2member_log"][] = "Redirecting Customer to the Home Page (after asking Customer to check their email).";
62
 
63
  echo c_ws_plugin__s2member_return_templates::return_template ($paypal["subscr_gateway"],
64
+ _x ('<strong>Thank you! (you MUST check your email before proceeding).</strong><br /><br />* Note: It can take <em>(up to 15 minutes)</em> for Email Confirmation with important details. If you don\'t receive email confirmation in the next 15 minutes, please contact Support.', "s2member-front", "s2member") . (($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"] || (c_ws_plugin__s2member_utils_conds::pro_is_installed () && !empty($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["pro_" . $paypal["subscr_gateway"] . "_sandbox"]))) ? '<br /><br />' . _x ('<strong>* Sandbox Mode *:</strong> You may NOT receive this Email in Sandbox Mode. Sandbox addresses are usually bogus (for testing).', "s2member-front", "s2member") : ''),
65
  _x ("Back To Home Page", "s2member-front", "s2member"), home_url ("/"));
66
  }
67
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
includes/classes/paypal-return-in-proxy-x-preview.inc.php CHANGED
@@ -61,7 +61,7 @@ if (!class_exists ("c_ws_plugin__s2member_paypal_return_in_proxy_x_preview"))
61
  $paypal["s2member_log"][] = "Redirecting Customer to the Home Page (after displaying preview information).";
62
 
63
  echo c_ws_plugin__s2member_return_templates::return_template ($paypal["subscr_gateway"],
64
- sprintf (_x ('<strong>Thank you! (this is a preview, no action necessary).</strong><br /><br />* Note: each of your Customers are returned back to your site immediately after they complete checkout. This Return Page displays a message and instructions for the Customer. s2Member may change the message and instructions dynamically, based on what the Customer is actually doing <em>(i.e. based on the type of transaction that is taking place)</em>.<br /><br /><em>* With <a href="%s" target="_blank">s2Member Pro</a> installed, it is possible to customize this Return Page in various ways.</em>', "s2member-front", "s2member"), esc_attr (c_ws_plugin__s2member_readmes::parse_readme_value ("Pro Module / Prices"))),
65
  _x ("Continue (Click Here)", "s2member-front", "s2member"), home_url ("/"));
66
  }
67
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
61
  $paypal["s2member_log"][] = "Redirecting Customer to the Home Page (after displaying preview information).";
62
 
63
  echo c_ws_plugin__s2member_return_templates::return_template ($paypal["subscr_gateway"],
64
+ sprintf (_x ('<strong>Thank you! (this is a preview, no action necessary).</strong><br /><br />* Note: each of your Customers are returned back to your site immediately after they complete checkout. This Return Page displays a message and instructions for the Customer. s2Member may change the message and instructions dynamically, based on what the Customer is actually doing <em>(i.e., based on the type of transaction that is taking place)</em>.<br /><br /><em>* With <a href="%s" target="_blank">s2Member Pro</a> installed, it is possible to customize this Return Page in various ways.</em>', "s2member-front", "s2member"), esc_attr (c_ws_plugin__s2member_readmes::parse_readme_value ("Pro Add-on / Prices"))),
65
  _x ("Continue (Click Here)", "s2member-front", "s2member"), home_url ("/"));
66
  }
67
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
includes/classes/paypal-return-in.inc.php CHANGED
@@ -84,7 +84,7 @@ if (!class_exists ("c_ws_plugin__s2member_paypal_return_in"))
84
  else if (($_paypal_cp = c_ws_plugin__s2member_paypal_return_in_subscr_modify_w_level::cp (get_defined_vars ())))
85
  $paypal = $_paypal_cp;
86
 
87
- else // Else we have an unexpected scenario ( i.e. an unexpected `txn_type/status` ).
88
  {
89
  $paypal["s2member_log"][] = "Unexpected `txn_type/status`. The `txn_type/status` did not match a required action.";
90
 
@@ -123,10 +123,10 @@ if (!class_exists ("c_ws_plugin__s2member_paypal_return_in"))
123
  else // Extensive log reporting here. This is an area where many site owners find trouble. Depending on server configuration; remote HTTPS connections may fail.
124
  {
125
  if /* Enqueue an admin notice if the site owner is missing the PDT Identity Key. */ (!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_identity_token"])
126
- c_ws_plugin__s2member_admin_notices::enqueue_admin_notice("<strong>s2Member:</strong> You have no PayPal PDT Identity Token configured. PayPal Auto-Return handling failed. Please update your PayPal PDT Identity Key. See: <code>s2Member -› PayPal Options -› PayPal PDT/Auto-Return Integration</code>. Thank you!", "*:*", true);
127
 
128
  $paypal["s2member_log"][] = "Unable to verify \$_POST vars. This is most likely related to an invalid configuration of s2Member, or a problem with server compatibility.";
129
- $paypal["s2member_log"][] = "Please make sure that you configure a PayPal PDT Identity Token for your installation of s2Member. See: `s2Member -› PayPal Options -› PayPal PDT/Auto-Return Integration`.";
130
  $paypal["s2member_log"][] = "See also, this KB article: `http://www.s2member.com/kb/server-scanner/`. We suggest that you run the s2Member Server Scanner.";
131
  $paypal["s2member_log"][] = /* Recording _POST + _GET vars for analysis and debugging. */ var_export ($_REQUEST, true);
132
 
84
  else if (($_paypal_cp = c_ws_plugin__s2member_paypal_return_in_subscr_modify_w_level::cp (get_defined_vars ())))
85
  $paypal = $_paypal_cp;
86
 
87
+ else // Else we have an unexpected scenario ( i.e., an unexpected `txn_type/status` ).
88
  {
89
  $paypal["s2member_log"][] = "Unexpected `txn_type/status`. The `txn_type/status` did not match a required action.";
90
 
123
  else // Extensive log reporting here. This is an area where many site owners find trouble. Depending on server configuration; remote HTTPS connections may fail.
124
  {
125
  if /* Enqueue an admin notice if the site owner is missing the PDT Identity Key. */ (!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_identity_token"])
126
+ c_ws_plugin__s2member_admin_notices::enqueue_admin_notice("<strong>s2Member:</strong> You have no PayPal PDT Identity Token configured. PayPal Auto-Return handling failed. Please update your PayPal PDT Identity Key. See: <strong>s2Member PayPal Options PayPal PDT/Auto-Return Integration</strong>. Thank you!", "*:*", true);
127
 
128
  $paypal["s2member_log"][] = "Unable to verify \$_POST vars. This is most likely related to an invalid configuration of s2Member, or a problem with server compatibility.";
129
+ $paypal["s2member_log"][] = "Please make sure that you configure a PayPal PDT Identity Token for your installation of s2Member. See: `s2Member PayPal Options PayPal PDT/Auto-Return Integration`.";
130
  $paypal["s2member_log"][] = "See also, this KB article: `http://www.s2member.com/kb/server-scanner/`. We suggest that you run the s2Member Server Scanner.";
131
  $paypal["s2member_log"][] = /* Recording _POST + _GET vars for analysis and debugging. */ var_export ($_REQUEST, true);
132
 
includes/classes/paypal-utilities.inc.php CHANGED
@@ -400,7 +400,7 @@ if(!class_exists("c_ws_plugin__s2member_paypal_utilities"))
400
  * @since 3.5
401
  *
402
  * @param string $term Expects one of `D|W|M|Y`.
403
- * @return bool|str A full singular description of the term *( i.e. `Day|Week|Month|Year` )*, else false.
404
  */
405
  public static function paypal_pro_term($term = FALSE)
406
  {
@@ -422,7 +422,7 @@ if(!class_exists("c_ws_plugin__s2member_paypal_utilities"))
422
  *
423
  * @param string $term Expects one of `D|W|M|Y`.
424
  * @param string $period Expects a numeric value.
425
- * @return bool|str A full singular description of the term *( i.e. `DAY|WEEK|BIWK|MONT|QTER|SMYR|YEAR` )*, else false.
426
  *
427
  * @note Payflow unfortunately does NOT support daily and/or bi-monthly billing.
428
  */
@@ -454,7 +454,7 @@ if(!class_exists("c_ws_plugin__s2member_paypal_utilities"))
454
  * @since 3.5
455
  *
456
  * @param string $term Expects one of `Day|Week|Month|Year`.
457
- * @return bool|str A term code *( i.e. `D|W|M|Y` )*, else false.
458
  */
459
  public static function paypal_std_term($term = FALSE)
460
  {
400
  * @since 3.5
401
  *
402
  * @param string $term Expects one of `D|W|M|Y`.
403
+ * @return bool|str A full singular description of the term *( i.e., `Day|Week|Month|Year` )*, else false.
404
  */
405
  public static function paypal_pro_term($term = FALSE)
406
  {
422
  *
423
  * @param string $term Expects one of `D|W|M|Y`.
424
  * @param string $period Expects a numeric value.
425
+ * @return bool|str A full singular description of the term *( i.e., `DAY|WEEK|BIWK|MONT|QTER|SMYR|YEAR` )*, else false.
426
  *
427
  * @note Payflow unfortunately does NOT support daily and/or bi-monthly billing.
428
  */
454
  * @since 3.5
455
  *
456
  * @param string $term Expects one of `Day|Week|Month|Year`.
457
+ * @return bool|str A term code *( i.e., `D|W|M|Y` )*, else false.
458
  */
459
  public static function paypal_std_term($term = FALSE)
460
  {
includes/classes/profile-in.inc.php CHANGED
@@ -51,7 +51,7 @@ if(!class_exists('c_ws_plugin__s2member_profile_in'))
51
 
52
  echo '<body style="'.esc_attr(apply_filters('ws_plugin__s2member_profile_body_styles', "background:#FFFFFF; color:#333333; font-family:'Verdana', sans-serif; font-size:13px;", get_defined_vars())).'">'."\n";
53
 
54
- echo '<form method="post" name="ws_plugin__s2member_profile" id="ws-plugin--s2member-profile" action="'.esc_attr(home_url('/')).'">'."\n";
55
 
56
  foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
57
  do_action('ws_plugin__s2member_during_profile_before_table', get_defined_vars());
51
 
52
  echo '<body style="'.esc_attr(apply_filters('ws_plugin__s2member_profile_body_styles', "background:#FFFFFF; color:#333333; font-family:'Verdana', sans-serif; font-size:13px;", get_defined_vars())).'">'."\n";
53
 
54
+ echo '<form method="post" name="ws_plugin__s2member_profile" id="ws-plugin--s2member-profile" action="'.esc_attr(home_url('/')).'" autocomplete="off">'."\n";
55
 
56
  foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
57
  do_action('ws_plugin__s2member_during_profile_before_table', get_defined_vars());
includes/classes/querys.inc.php CHANGED
@@ -89,7 +89,7 @@ if(!class_exists('c_ws_plugin__s2member_querys'))
89
  public static function query_level_access(&$wp_query = NULL, $force = FALSE)
90
  {
91
  global $wpdb; // Global DB object reference.
92
- static $initial_query = TRUE; // Tracks the initial query.
93
  c_ws_plugin__s2member_querys::$current_wp_query = &$wp_query;
94
 
95
  foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
@@ -108,8 +108,9 @@ if(!class_exists('c_ws_plugin__s2member_querys'))
108
  if((!is_admin() || c_ws_plugin__s2member_querys::_is_admin_ajax_search($wp_query)) && !$wp_query->get('___s2_is_bbp_has_replies') /* See: <http://bit.ly/1obLpv4> */)
109
  {
110
  $suppressing_filters = $wp_query->get('suppress_filters'); // Filter suppression on?
111
- if(!$suppressing_filters && $force // Forcing this routine bypasses all of these other conditionals. Works with API function ``attach_s2member_query_filters()``.
112
- || (!$suppressing_filters && in_array('all', $o) && !($initial_query && $wp_query->is_singular())) // Don't create 404 errors. Allow Security Gate to handle these.
 
113
  || (!$suppressing_filters && (in_array('all', $o) || in_array('searches', $o)) && $wp_query->is_search()) // Or, is this a search results query, possibly via AJAX: `admin-ajax.php`?
114
  || (!$suppressing_filters && (in_array('all', $o) || in_array('feeds', $o)) && $wp_query->is_feed() && !$wp_query->is_comment_feed()) // Or, is this a feed; and it's NOT for comments?
115
  || (!$suppressing_filters && (in_array('all', $o) || in_array('comment-feeds', $o)) && $wp_query->is_feed() && $wp_query->is_comment_feed()) // Or, is this a feed; and it IS indeed for comments?
@@ -250,8 +251,6 @@ if(!class_exists('c_ws_plugin__s2member_querys'))
250
  foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
251
  do_action('ws_plugin__s2member_after_query_level_access', get_defined_vars());
252
  unset($__refs, $__v); // Housekeeping.
253
-
254
- $initial_query = FALSE; // No longer.
255
  }
256
 
257
  /**
89
  public static function query_level_access(&$wp_query = NULL, $force = FALSE)
90
  {
91
  global $wpdb; // Global DB object reference.
92
+
93
  c_ws_plugin__s2member_querys::$current_wp_query = &$wp_query;
94
 
95
  foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
108
  if((!is_admin() || c_ws_plugin__s2member_querys::_is_admin_ajax_search($wp_query)) && !$wp_query->get('___s2_is_bbp_has_replies') /* See: <http://bit.ly/1obLpv4> */)
109
  {
110
  $suppressing_filters = $wp_query->get('suppress_filters'); // Filter suppression on?
111
+
112
+ if((!$suppressing_filters && $force) // Forcing this routine bypasses all of these other conditionals. Works with API function ``attach_s2member_query_filters()``.
113
+ || (!$suppressing_filters && in_array('all', $o) && !($wp_query->is_main_query() && $wp_query->is_singular())) // Don't create 404 errors. Allow Security Gate to handle these.
114
  || (!$suppressing_filters && (in_array('all', $o) || in_array('searches', $o)) && $wp_query->is_search()) // Or, is this a search results query, possibly via AJAX: `admin-ajax.php`?
115
  || (!$suppressing_filters && (in_array('all', $o) || in_array('feeds', $o)) && $wp_query->is_feed() && !$wp_query->is_comment_feed()) // Or, is this a feed; and it's NOT for comments?
116
  || (!$suppressing_filters && (in_array('all', $o) || in_array('comment-feeds', $o)) && $wp_query->is_feed() && $wp_query->is_comment_feed()) // Or, is this a feed; and it IS indeed for comments?
251
  foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
252
  do_action('ws_plugin__s2member_after_query_level_access', get_defined_vars());
253
  unset($__refs, $__v); // Housekeeping.
 
 
254
  }
255
 
256
  /**
includes/classes/readmes.inc.php CHANGED
@@ -176,7 +176,7 @@ if (!class_exists ("c_ws_plugin__s2member_readmes"))
176
  * @package s2Member\Readmes
177
  * @since 3.5
178
  *
179
- * @param string $str A string *(i.e. the specs section)*.
180
  * @return string Parsed specs. With HTML markup for list item display.
181
  */
182
  public static function _parse_readme_specs ($str = FALSE)
@@ -198,9 +198,9 @@ if (!class_exists ("c_ws_plugin__s2member_readmes"))
198
  * @param string $key A key *(within the specs section)*.
199
  * @param string $specific_path Optional. Path to a specific readme file to parse. Defaults to that of the software itself.
200
  * When/if a readme-dev.txt file is available, that will be used instead of the default readme.txt.
201
- * @return str|bool The value of the key, else false if not found.
202
  */
203
- public static function parse_readme_value ($key = FALSE, $specific_path = FALSE)
204
  {
205
  static $readme = array(); // For repeated lookups.
206
 
@@ -210,7 +210,6 @@ if (!class_exists ("c_ws_plugin__s2member_readmes"))
210
  $dev_path = dirname (dirname (dirname (__FILE__))) . "/readme-dev.txt";
211
  $path = (file_exists ($dev_path)) ? $dev_path : $path;
212
  }
213
-
214
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
215
  do_action("ws_plugin__s2member_before_parse_readme_value", get_defined_vars ());
216
  unset($__refs, $__v);
@@ -228,9 +227,7 @@ if (!class_exists ("c_ws_plugin__s2member_readmes"))
228
 
229
  return apply_filters("ws_plugin__s2member_parse_readme_value", ((isset ($m[5]) && strlen ($m[5] = trim ($m[5]))) ? $m[5] : false), get_defined_vars ());
230
  }
231
- else // Nope.
232
  return false;
233
  }
234
  }
235
- }
236
- ?>
176
  * @package s2Member\Readmes
177
  * @since 3.5
178
  *
179
+ * @param string $str A string *(i.e., the specs section)*.
180
  * @return string Parsed specs. With HTML markup for list item display.
181
  */
182
  public static function _parse_readme_specs ($str = FALSE)
198
  * @param string $key A key *(within the specs section)*.
199
  * @param string $specific_path Optional. Path to a specific readme file to parse. Defaults to that of the software itself.
200
  * When/if a readme-dev.txt file is available, that will be used instead of the default readme.txt.
201
+ * @return string|bool The value of the key, else false if not found.
202
  */
203
+ public static function parse_readme_value ($key = '', $specific_path = '')
204
  {
205
  static $readme = array(); // For repeated lookups.
206
 
210
  $dev_path = dirname (dirname (dirname (__FILE__))) . "/readme-dev.txt";
211
  $path = (file_exists ($dev_path)) ? $dev_path : $path;
212
  }
 
213
  foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;
214
  do_action("ws_plugin__s2member_before_parse_readme_value", get_defined_vars ());
215
  unset($__refs, $__v);
227
 
228
  return apply_filters("ws_plugin__s2member_parse_readme_value", ((isset ($m[5]) && strlen ($m[5] = trim ($m[5]))) ? $m[5] : false), get_defined_vars ());
229
  }
 
230
  return false;
231
  }
232
  }
233
+ }
 
includes/classes/register-access.inc.php CHANGED
@@ -36,7 +36,7 @@ if(!class_exists('c_ws_plugin__s2member_register_access'))
36
  * @param string $subscr_gateway Payment Gateway associated with a Customer.
37
  * @param string $subscr_id Unique Subscr. ID associated with Payment Gateway; associated with a Customer.
38
  * @param string $custom Custom String value *(as supplied in Shortcode)*; must start with installation domain name.
39
- * @param int|string $item_number An s2Member-generated `item_number` *( i.e. `1` for Level 1, or `level|ccaps|fixed-term`, or `sp|ids|expiration` )*.
40
  * @param bool $shrink Optional. Defaults to true. If false, the raw registration link will NOT be reduced in size through the tinyURL API.
41
  *
42
  * @return string|bool A Registration Access Link on success, else false on failure.
36
  * @param string $subscr_gateway Payment Gateway associated with a Customer.
37
  * @param string $subscr_id Unique Subscr. ID associated with Payment Gateway; associated with a Customer.
38
  * @param string $custom Custom String value *(as supplied in Shortcode)*; must start with installation domain name.
39
+ * @param int|string $item_number An s2Member-generated `item_number` *( i.e., `1` for Level 1, or `level|ccaps|fixed-term`, or `sp|ids|expiration` )*.
40
  * @param bool $shrink Optional. Defaults to true. If false, the raw registration link will NOT be reduced in size through the tinyURL API.
41
  *
42
  * @return string|bool A Registration Access Link on success, else false on failure.
includes/classes/registrations.inc.php CHANGED
@@ -586,7 +586,7 @@ if(!class_exists('c_ws_plugin__s2member_registrations'))
586
  if(!is_admin() && (!c_ws_plugin__s2member_utils_conds::pro_is_installed() || !c_ws_plugin__s2member_pro_remote_ops::is_remote_op('create_user')) && ($reg_cookies = c_ws_plugin__s2member_register_access::reg_cookies_ok()) && extract($reg_cookies))
587
  { /* This routine could be processed through `/wp-login.php?action=register`, `/wp-activate.php`, or `/activate` via BuddyPress`.
588
  This may also be processed through a standard BuddyPress installation, or another plugin calling `user_register`.
589
- If processed through `/wp-activate.php`, it could've originated inside the admin via `/user-new.php`. */
590
  /**
591
  * @var $subscr_gateway string Reference for IDEs.
592
  * @var $subscr_id string Reference for IDEs.
@@ -939,11 +939,11 @@ if(!class_exists('c_ws_plugin__s2member_registrations'))
939
 
940
  $opt_in = (!empty($_pmr['ws_plugin__s2member_custom_reg_field_opt_in'])) ? TRUE : FALSE;
941
 
942
- if(!($fname = $user->first_name)) // `Users -› Add New`.
943
  if(!empty($_pmr['ws_plugin__s2member_custom_reg_field_first_name']))
944
  $fname = (string)$_pmr['ws_plugin__s2member_custom_reg_field_first_name'];
945
 
946
- if(!($lname = $user->last_name)) // `Users -› Add New`.
947
  if(!empty($_pmr['ws_plugin__s2member_custom_reg_field_last_name']))
948
  $lname = (string)$_pmr['ws_plugin__s2member_custom_reg_field_last_name'];
949
 
@@ -958,7 +958,7 @@ if(!class_exists('c_ws_plugin__s2member_registrations'))
958
  if(!empty($GLOBALS['ws_plugin__s2member_generate_password_return']))
959
  $pass = (string)$GLOBALS['ws_plugin__s2member_generate_password_return'];
960
 
961
- if(!$pass) // Also try the `Users -› Add New` form.
962
  if(!empty($_pmr['pass1'])) // Field in `/user-new.php`.
963
  $pass = (string)$_pmr['pass1'];
964
 
586
  if(!is_admin() && (!c_ws_plugin__s2member_utils_conds::pro_is_installed() || !c_ws_plugin__s2member_pro_remote_ops::is_remote_op('create_user')) && ($reg_cookies = c_ws_plugin__s2member_register_access::reg_cookies_ok()) && extract($reg_cookies))
587
  { /* This routine could be processed through `/wp-login.php?action=register`, `/wp-activate.php`, or `/activate` via BuddyPress`.
588
  This may also be processed through a standard BuddyPress installation, or another plugin calling `user_register`.
589
+ If processed through `/wp-activate.php`, it could've originated inside the admin—via `/user-new.php`. */
590
  /**
591
  * @var $subscr_gateway string Reference for IDEs.
592
  * @var $subscr_id string Reference for IDEs.
939
 
940
  $opt_in = (!empty($_pmr['ws_plugin__s2member_custom_reg_field_opt_in'])) ? TRUE : FALSE;
941
 
942
+ if(!($fname = $user->first_name)) // `Users Add New`.
943
  if(!empty($_pmr['ws_plugin__s2member_custom_reg_field_first_name']))
944
  $fname = (string)$_pmr['ws_plugin__s2member_custom_reg_field_first_name'];
945
 
946
+ if(!($lname = $user->last_name)) // `Users Add New`.
947
  if(!empty($_pmr['ws_plugin__s2member_custom_reg_field_last_name']))
948
  $lname = (string)$_pmr['ws_plugin__s2member_custom_reg_field_last_name'];
949
 
958
  if(!empty($GLOBALS['ws_plugin__s2member_generate_password_return']))
959
  $pass = (string)$GLOBALS['ws_plugin__s2member_generate_password_return'];
960
 
961
+ if(!$pass) // Also try the `Users Add New` form.
962
  if(!empty($_pmr['pass1'])) // Field in `/user-new.php`.
963
  $pass = (string)$_pmr['pass1'];
964
 
includes/classes/sc-files-in.inc.php CHANGED
@@ -154,7 +154,7 @@ if(!class_exists('c_ws_plugin__s2member_sc_files_in'))
154
  {
155
  $file_download_extension = strtolower(ltrim((string)strrchr(basename($config['file_download']), '.'), '.'));
156
  $file_download_resolution_wo_extension = substr($config['file_download'], 0, -(strlen($file_download_extension) + 1) /* For the dot. */);
157
- $file_download_wo_resolution_extension = preg_replace('/\-r[0-9]+([^.]*)$/i', '', $file_download_resolution_wo_extension); // e.g. `r720p-HD` is removed here.
158
 
159
  $file_download_resolutions = array(); // Initialize the array of resolutions.
160
  foreach(preg_split('/[,;\s]+/', $attr['player_resolutions'], NULL, PREG_SPLIT_NO_EMPTY) as $_player_resolution)
@@ -245,7 +245,7 @@ if(!class_exists('c_ws_plugin__s2member_sc_files_in'))
245
  if($_is_first_file_download_url) $player_sources .= ",'default': 'true'";
246
  $player_sources .= '}'; // Close this source.
247
  }
248
- $_file_download_url['smil']['height'] = (integer)$_file_download_url_label; // e.g. `720p-HD` becomes `720`.
249
  if(!$_file_download_url['smil']['height']) $_file_download_url['smil']['height'] = 720; // Use a default height if invalid.
250
  $_file_download_url['smil']['width'] = ceil(($_file_download_url['smil']['height'] / $player_resolution_aspect_ratio_h) * $player_resolution_aspect_ratio_w);
251
 
@@ -258,7 +258,7 @@ if(!class_exists('c_ws_plugin__s2member_sc_files_in'))
258
  ' height="'.esc_attr($_file_download_url['smil']['height']).'"'.
259
  ' system-bitrate="'.esc_attr($_file_download_url['smil']['system-bitrate']).'" />';
260
  }
261
- else // Build them inline; i.e. don't create a SMIL file in this case; not necessary.
262
  {
263
  $player_sources .= ',{'; // Open this source; JSON object properties.
264
  $player_sources .= "'file': '".c_ws_plugin__s2member_utils_strings::esc_js_sq($_file_download_url['streamer'].'/'.$_file_download_url['prefix'].$_file_download_url['file'])."'";
154
  {
155
  $file_download_extension = strtolower(ltrim((string)strrchr(basename($config['file_download']), '.'), '.'));
156
  $file_download_resolution_wo_extension = substr($config['file_download'], 0, -(strlen($file_download_extension) + 1) /* For the dot. */);
157
+ $file_download_wo_resolution_extension = preg_replace('/\-r[0-9]+([^.]*)$/i', '', $file_download_resolution_wo_extension); // e.g., `r720p-HD` is removed here.
158
 
159
  $file_download_resolutions = array(); // Initialize the array of resolutions.
160
  foreach(preg_split('/[,;\s]+/', $attr['player_resolutions'], NULL, PREG_SPLIT_NO_EMPTY) as $_player_resolution)
245
  if($_is_first_file_download_url) $player_sources .= ",'default': 'true'";
246
  $player_sources .= '}'; // Close this source.
247
  }
248
+ $_file_download_url['smil']['height'] = (integer)$_file_download_url_label; // e.g., `720p-HD` becomes `720`.
249
  if(!$_file_download_url['smil']['height']) $_file_download_url['smil']['height'] = 720; // Use a default height if invalid.
250
  $_file_download_url['smil']['width'] = ceil(($_file_download_url['smil']['height'] / $player_resolution_aspect_ratio_h) * $player_resolution_aspect_ratio_w);
251
 
258
  ' height="'.esc_attr($_file_download_url['smil']['height']).'"'.
259
  ' system-bitrate="'.esc_attr($_file_download_url['smil']['system-bitrate']).'" />';
260
  }
261
+ else // Build them inline; i.e., don't create a SMIL file in this case; not necessary.
262
  {
263
  $player_sources .= ',{'; // Open this source; JSON object properties.
264
  $player_sources .= "'file': '".c_ws_plugin__s2member_utils_strings::esc_js_sq($_file_download_url['streamer'].'/'.$_file_download_url['prefix'].$_file_download_url['file'])."'";
includes/classes/sc-if-conds-in.inc.php CHANGED
@@ -83,7 +83,7 @@ if(!class_exists('c_ws_plugin__s2member_sc_if_conds_in'))
83
 
84
  $content_if = $content_else = NULL; // Initialize.
85
  $shortcode_depth = strspn($shortcode, '_'); // Based on a zero index.
86
- $else_tag = '['.str_repeat('_', $shortcode_depth).'else]'; // e.g. [else], [_else], [__else]
87
 
88
  if(strpos($content, $else_tag) !== FALSE && $pro_is_installed)
89
  list($content_if, $content_else) = explode($else_tag, $content, 2);
83
 
84
  $content_if = $content_else = NULL; // Initialize.
85
  $shortcode_depth = strspn($shortcode, '_'); // Based on a zero index.
86
+ $else_tag = '['.str_repeat('_', $shortcode_depth).'else]'; // e.g., [else], [_else], [__else]
87
 
88
  if(strpos($content, $else_tag) !== FALSE && $pro_is_installed)
89
  list($content_if, $content_else) = explode($else_tag, $content, 2);
includes/classes/sc-paypal-button-e.inc.php CHANGED
@@ -50,7 +50,7 @@ if (!class_exists ("c_ws_plugin__s2member_sc_paypal_button_e"))
50
  {
51
  $cache = /* Are we caching? */ apply_filters("ws_plugin__s2member_sc_paypal_button_encryption_cache", true, get_defined_vars ());
52
 
53
- $_code = $vars["_code"]; $attr = $vars["attr"]; // Let's unpack (i.e. use shorter references) to these two important vars.
54
 
55
  if ($cache && ($transient = "s2m_btn_" . md5 ($code . c_ws_plugin__s2member_utilities::ver_checksum ())) && ($cache = get_transient ($transient)))
56
  $code = /* Great, so we can use the cached version here to save processing time. The MD5 hash uses ``$code`` and NOT ``$_code``. */ $cache;
50
  {
51
  $cache = /* Are we caching? */ apply_filters("ws_plugin__s2member_sc_paypal_button_encryption_cache", true, get_defined_vars ());
52
 
53
+ $_code = $vars["_code"]; $attr = $vars["attr"]; // Let's unpack (i.e., use shorter references) to these two important vars.
54
 
55
  if ($cache && ($transient = "s2m_btn_" . md5 ($code . c_ws_plugin__s2member_utilities::ver_checksum ())) && ($cache = get_transient ($transient)))
56
  $code = /* Great, so we can use the cached version here to save processing time. The MD5 hash uses ``$code`` and NOT ``$_code``. */ $cache;
includes/classes/sc-profile-in.inc.php CHANGED
@@ -63,7 +63,7 @@ if(!class_exists('c_ws_plugin__s2member_sc_profile_in'))
63
 
64
  ob_start(); // Start buffering. Allows Hooks to echo output like: `/?s2member_profile=1`.
65
 
66
- echo '<form method="post" name="ws_plugin__s2member_profile" id="ws-plugin--s2member-profile">'."\n";
67
 
68
  if(!empty($GLOBALS['ws_plugin__s2member_profile_saved'])) // Respond to successful updates.
69
  {
63
 
64
  ob_start(); // Start buffering. Allows Hooks to echo output like: `/?s2member_profile=1`.
65
 
66
+ echo '<form method="post" name="ws_plugin__s2member_profile" id="ws-plugin--s2member-profile" autocomplete="off">'."\n";
67
 
68
  if(!empty($GLOBALS['ws_plugin__s2member_profile_saved'])) // Respond to successful updates.
69
  {
includes/classes/user-new-in.inc.php CHANGED
@@ -154,7 +154,7 @@ if (!class_exists ("c_ws_plugin__s2member_user_new_in"))
154
  unset($__refs, $__v);
155
 
156
  $unfs .= '<tr>' . "\n";
157
- $unfs .= '<th><label for="ws-plugin--s2member-user-new-s2member-ccaps">Custom Capabilities:</label> <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member -› API Scripting -› Custom Capabilities.' . ((is_multisite ()) ? '\\n\\nCustom Capabilities are assigned on a per-Blog basis. So having a set of Custom Capabilities for one Blog, and having NO Custom Capabilities on another Blog - is very common. This is how permissions are designed to work.' : '') . '\'); return false;" tabindex="-1">[?]</a></th>' . "\n";
158
  $unfs .= '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_user_new_s2member_ccaps" id="ws-plugin--s2member-user-new-s2member-ccaps" value="' . format_to_edit ($_p["ws_plugin__s2member_user_new_s2member_ccaps"]) . '" class="regular-text" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" /></td>' . "\n";
159
  $unfs .= '</tr>' . "\n";
160
 
@@ -168,7 +168,7 @@ if (!class_exists ("c_ws_plugin__s2member_user_new_in"))
168
  unset($__refs, $__v);
169
 
170
  $unfs .= '<tr>' . "\n";
171
- $unfs .= '<th><label for="ws-plugin--s2member-user-new-auto-eot-time">Automatic EOT Time:</label> <a href="#" onclick="alert(\'EOT = End Of Term. ( i.e. Account Expiration / Termination. ).\\n\\nIf you leave this empty, s2Member will configure an EOT Time automatically, based on the paid Subscription associated with this account. In other words, if a paid Subscription expires, is cancelled, terminated, refunded, reversed, or charged back to you; s2Member will deal with the EOT automatically.\\n\\nThat being said, if you would rather take control over this, you can. If you type in a date manually, s2Member will obey the Auto-EOT Time that you\\\'ve given, no matter what. In other words, you can force certain Members to expire automatically, at a time that you specify. s2Member will obey.\\n\\nValid formats for Automatic EOT Time:\\n\\nmm/dd/yyyy\\nyyyy-mm-dd\\n+1 year\\n+2 weeks\\n+2 months\\n+10 minutes\\nnext thursday\\ntomorrow\\ntoday\\n\\n* anything compatible with PHP\\\'s strtotime() function.\'); return false;" tabindex="-1">[?]</a>' . (($auto_eot_time) ? '<br /><small>(<a href="http://www.world-time-zones.org/zones/greenwich-mean-time.htm" target="_blank" rel="external">Universal Time / GMT</a>)</small>' : '') . '</th>' . "\n";
172
  $unfs .= '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_user_new_s2member_auto_eot_time" id="ws-plugin--s2member-user-new-auto-eot-time" value="' . format_to_edit ($_p["ws_plugin__s2member_user_new_s2member_auto_eot_time"]) . '" class="regular-text" /></td>' . "\n";
173
  $unfs .= '</tr>' . "\n";
174
 
154
  unset($__refs, $__v);
155
 
156
  $unfs .= '<tr>' . "\n";
157
+ $unfs .= '<th><label for="ws-plugin--s2member-user-new-s2member-ccaps">Custom Capabilities:</label> <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member API Scripting Custom Capabilities.' . ((is_multisite ()) ? '\\n\\nCustom Capabilities are assigned on a per-Blog basis. So having a set of Custom Capabilities for one Blog, and having NO Custom Capabilities on another Blog - is very common. This is how permissions are designed to work.' : '') . '\'); return false;" tabindex="-1">[?]</a></th>' . "\n";
158
  $unfs .= '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_user_new_s2member_ccaps" id="ws-plugin--s2member-user-new-s2member-ccaps" value="' . format_to_edit ($_p["ws_plugin__s2member_user_new_s2member_ccaps"]) . '" class="regular-text" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" /></td>' . "\n";
159
  $unfs .= '</tr>' . "\n";
160
 
168
  unset($__refs, $__v);
169
 
170
  $unfs .= '<tr>' . "\n";
171
+ $unfs .= '<th><label for="ws-plugin--s2member-user-new-auto-eot-time">Automatic EOT Time:</label> <a href="#" onclick="alert(\'EOT = End Of Term. ( i.e., Account Expiration / Termination. ).\\n\\nIf you leave this empty, s2Member will configure an EOT Time automatically, based on the paid Subscription associated with this account. In other words, if a paid Subscription expires, is cancelled, terminated, refunded, reversed, or charged back to you; s2Member will deal with the EOT automatically.\\n\\nThat being said, if you would rather take control over this, you can. If you type in a date manually, s2Member will obey the Auto-EOT Time that you\\\'ve given, no matter what. In other words, you can force certain Members to expire automatically, at a time that you specify. s2Member will obey.\\n\\nValid formats for Automatic EOT Time:\\n\\nmm/dd/yyyy\\nyyyy-mm-dd\\n+1 year\\n+2 weeks\\n+2 months\\n+10 minutes\\nnext thursday\\ntomorrow\\ntoday\\n\\n* anything compatible with PHP\\\'s strtotime() function.\'); return false;" tabindex="-1">[?]</a>' . (($auto_eot_time) ? '<br /><small>(<a href="http://www.world-time-zones.org/zones/greenwich-mean-time.htm" target="_blank" rel="external">Universal Time / GMT</a>)</small>' : '') . '</th>' . "\n";
172
  $unfs .= '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_user_new_s2member_auto_eot_time" id="ws-plugin--s2member-user-new-auto-eot-time" value="' . format_to_edit ($_p["ws_plugin__s2member_user_new_s2member_auto_eot_time"]) . '" class="regular-text" /></td>' . "\n";
173
  $unfs .= '</tr>' . "\n";
174
 
includes/classes/user-securities.inc.php CHANGED
@@ -38,14 +38,26 @@ if(!class_exists('c_ws_plugin__s2member_user_securities'))
38
  * @since 3.5
39
  *
40
  * @attaches-to ``add_action('init');``
41
- *
42
- * @return null
43
  */
44
  public static function initialize() // Initializes the Filter for `user_has_cap`.
45
  {
46
  add_filter('user_has_cap', 'c_ws_plugin__s2member_user_securities::user_capabilities', 10, 3);
47
  }
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  /**
50
  * Alters `WP_User->has_cap()` in special cases for Administrators.
51
  *
@@ -55,7 +67,7 @@ if(!class_exists('c_ws_plugin__s2member_user_securities'))
55
  * @attaches-to ``add_filter('user_has_cap');``
56
  *
57
  * @param array $capabilities Expects an array of Capabilities passed in by the Filter.
58
- * This array contains all of the Capabilities that the User has *( i.e. ``$user->allcaps`` )*.
59
  * @param array $caps_map An array of Capabilities mapped out by the ``map_meta_cap`` function.
60
  * @param array $args Array of arguments originally passed through the ``has_cap()`` function.
61
  * However, WordPress modifies this array of arguments in the following way.
@@ -116,7 +128,7 @@ if(!class_exists('c_ws_plugin__s2member_user_securities'))
116
  *
117
  * Demo accounts *( where the Username MUST be 'demo' )*, will NOT be allowed to change their Password.
118
  * Any other restrictions you need to impose must be done through custom programming, using s2Member's Conditionals.
119
- * See `s2Member -› API Scripting`.
120
  *
121
  * @package s2Member\User_Securities
122
  * @since 3.5
38
  * @since 3.5
39
  *
40
  * @attaches-to ``add_action('init');``
 
 
41
  */
42
  public static function initialize() // Initializes the Filter for `user_has_cap`.
43
  {
44
  add_filter('user_has_cap', 'c_ws_plugin__s2member_user_securities::user_capabilities', 10, 3);
45
  }
46
 
47
+ /**
48
+ * Deals w/ bbPress-specific issues on a Multisite Network.
49
+ *
50
+ * @package s2Member\User_Securities
51
+ * @since 150224
52
+ *
53
+ * @attaches-to ``add_action('after_setup_theme');``
54
+ */
55
+ public static function set_current_user()
56
+ {
57
+ if(is_multisite() && is_user_logged_in() && !current_user_can('read'))
58
+ remove_action('bbp_setup_current_user', 'bbp_set_current_user_default_role');
59
+ }
60
+
61
  /**
62
  * Alters `WP_User->has_cap()` in special cases for Administrators.
63
  *
67
  * @attaches-to ``add_filter('user_has_cap');``
68
  *
69
  * @param array $capabilities Expects an array of Capabilities passed in by the Filter.
70
+ * This array contains all of the Capabilities that the User has *( i.e., ``$user->allcaps`` )*.
71
  * @param array $caps_map An array of Capabilities mapped out by the ``map_meta_cap`` function.
72
  * @param array $args Array of arguments originally passed through the ``has_cap()`` function.
73
  * However, WordPress modifies this array of arguments in the following way.
128
  *
129
  * Demo accounts *( where the Username MUST be 'demo' )*, will NOT be allowed to change their Password.
130
  * Any other restrictions you need to impose must be done through custom programming, using s2Member's Conditionals.
131
+ * See `s2Member API Scripting`.
132
  *
133
  * @package s2Member\User_Securities
134
  * @since 3.5
includes/classes/users-list-in.inc.php CHANGED
@@ -118,7 +118,7 @@ if(!class_exists("c_ws_plugin__s2member_users_list_in"))
118
  unset($__refs, $__v);
119
 
120
  echo '<tr>'."\n";
121
- echo '<th><label for="ws-plugin--s2member-profile-s2member-subscr-cid">Paid Subscr. CID:</label> <a href="#" onclick="alert(\'A Paid Subscription CID; i.e. a Customer\\\'s ID. Applicable only with Stripe integration. s2Member fills this in automatically. This is the Customer\\\'s ID in Stripe, which remains constant throughout any & all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID.\\n\\nIn all other cases, the Paid Subscr. CID is simply set to the Paid Subscr. ID value; i.e. it is a duplicate of Paid Subscr. ID when running anything other than Stripe.\\n\\nThis field will be empty for Free Subscribers, and/or anyone who is NOT paying you. This field is only editable for Customer Service purposes; just in case you ever need to update the Paid Subscr. Gateway/ID/CID manually.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
122
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_subscr_cid" id="ws-plugin--s2member-profile-s2member-subscr-cid" value="'.format_to_edit(get_user_option("s2member_subscr_cid", $user_id)).'" class="regular-text" /></td>'."\n";
123
  echo '</tr>'."\n";
124
 
@@ -146,7 +146,7 @@ if(!class_exists("c_ws_plugin__s2member_users_list_in"))
146
  unset($__refs, $__v);
147
 
148
  echo '<tr>'."\n";
149
- echo '<th><label for="ws-plugin--s2member-profile-s2member-coupon-codes">Coupon Code(s):</label> <a href="#" onclick="alert(\'This is a comma-delimited list of the Coupon Codes associated with this user; i.e. the Coupon Code(s) that have been used to complete checkout. s2Member updates this list automatically. This field is only editable for Customer Service purposes; i.e. just in case you ever need to update the list manually.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
150
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_coupon_codes" id="ws-plugin--s2member-profile-s2member-coupon-codes" value="'.format_to_edit(implode(',', is_array($_user_coupon_codes = get_user_option("s2member_coupon_codes", $user_id)) ? $_user_coupon_codes : array())).'" class="regular-text" /></td>'."\n";
151
  echo '</tr>'."\n";
152
 
@@ -178,7 +178,7 @@ if(!class_exists("c_ws_plugin__s2member_users_list_in"))
178
  unset($__refs, $__v);
179
 
180
  echo '<tr>'."\n";
181
- echo '<th><label for="ws-plugin--s2member-profile-s2member-ccaps">Custom Capabilities:</label> <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member -› API Scripting -› Custom Capabilities.'.((is_multisite()) ? '\\n\\nCustom Capabilities are assigned on a per-Blog basis. So having a set of Custom Capabilities for one Blog, and having NO Custom Capabilities on another Blog - is very common. This is how permissions are designed to work.' : '').'\'); return false;" tabindex="-1">[?]</a>'.((is_multisite()) ? '<br /><small>(for this Blog)</small>' : '').'</th>'."\n";
182
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_ccaps" id="ws-plugin--s2member-profile-s2member-ccaps" value="'.format_to_edit(((!empty($ccaps)) ? implode(",", $ccaps) : "")).'" class="regular-text" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" /></td>'."\n";
183
  echo '</tr>'."\n";
184
 
@@ -195,7 +195,7 @@ if(!class_exists("c_ws_plugin__s2member_users_list_in"))
195
  echo '<tr>'."\n";
196
  $auto_eot_time = get_user_option("s2member_auto_eot_time", $user_id);
197
  $auto_eot_time = ($auto_eot_time) ? date("D M j, Y g:i a T", $auto_eot_time) : "";
198
- echo '<th><label for="ws-plugin--s2member-profile-s2member-auto-eot-time">Automatic EOT Time:</label> <a href="#" onclick="alert(\'EOT = End Of Term. ( i.e. Account Expiration / Termination. ).\\n\\nIf you leave this empty, s2Member will configure an EOT Time automatically, based on the paid Subscription associated with this account. In other words, if a paid Subscription expires, is cancelled, terminated, refunded, reversed, or charged back to you; s2Member will deal with the EOT automatically.\\n\\nThat being said, if you would rather take control over this, you can. If you type in a date manually, s2Member will obey the Auto-EOT Time that you\\\'ve given, no matter what. In other words, you can force certain Members to expire automatically, at a time that you specify. s2Member will obey.\\n\\nValid formats for Automatic EOT Time:\\n\\nmm/dd/yyyy\\nyyyy-mm-dd\\n+1 year\\n+2 weeks\\n+2 months\\n+10 minutes\\nnext thursday\\ntomorrow\\ntoday\\n\\n* anything compatible with PHP\\\'s strtotime() function.\'); return false;" tabindex="-1">[?]</a>'.(($auto_eot_time) ? '<br /><small>(<a href="http://en.wikipedia.org/wiki/Coordinated_Universal_Time" target="_blank" rel="external">Universal Time / GMT</a>)</small>' : '').'</th>'."\n";
199
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_auto_eot_time" id="ws-plugin--s2member-profile-s2member-auto-eot-time" value="'.format_to_edit($auto_eot_time).'" class="regular-text" /></td>'."\n";
200
  echo '</tr>'."\n";
201
 
@@ -210,7 +210,7 @@ if(!class_exists("c_ws_plugin__s2member_users_list_in"))
210
  unset($__refs, $__v);
211
 
212
  echo '<tr>'."\n";
213
- echo '<th><label for="ws-plugin--s2member-profile-reset-pass-resend">Reset Password &amp; Resend Welcome Email Message:</label> <a href="#" onclick="alert(\'Checking this box will tell s2Member to reset this User\\\'s password and then reprocess the New User Email Notification message against this User\\\'s account. This way they\\\'ll get an email message with their Username/Password.\\n\\nThis can be helpful in cases where a User/Member missed the original email message for some reason.\\n\\nThe User\\\'s password is reset to a new auto-generated password by default. However, you can provide a custom password by entering a new password of your choosing in the field above provided by WordPress itself.\\n\\nIt is also possible to customize the New User Email Notification message with s2Member. Please see: `Dashboard -› s2Member -› General Options -› Email Configuration -› New User Notifications`.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
214
  echo '<td><label><input type="checkbox" name="ws_plugin__s2member_profile_reset_pass_resend" id="ws-plugin--s2member-profile-reset-pass-resend" value="1" /> Yes, reset password &amp; resend welcome email message to this User.</label></td>'."\n";
215
  echo '</tr>'."\n";
216
 
@@ -257,7 +257,7 @@ if(!class_exists("c_ws_plugin__s2member_users_list_in"))
257
  unset($__refs, $__v);
258
 
259
  echo '<tr>'."\n";
260
- echo '<th><label for="ws-plugin--s2member-profile-ip-restrictions">Reset IP Restrictions:</label> <a href="#" onclick="alert(\'A single Username is only valid for a certain number of unique IP addresses (as configured in your s2Member -› General Options). Once that limit is reached, s2Member assumes there has been a security breach. At that time, s2Member will place a temporary ban (preventing access).\\n\\nIf you have spoken to a legitimate Customer that is receiving an error upon logging in (ex: 503 / too many IP addresses), you can remove this temporary ban by checking the box below. If the abusive behavior continues, s2Member will automatically re-instate IP Restrictions in the future. If you would like to gain further control over IP Restrictions, please check your General Options panel for s2Member.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
261
  echo '<td><label><input type="checkbox" name="ws_plugin__s2member_profile_ip_restrictions" id="ws-plugin--s2member-profile-ip-restrictions" value="1" /> Yes, delete/reset IP Restrictions associated with this Username.</label>'.((c_ws_plugin__s2member_ip_restrictions::specific_ip_restriction_at_or_above_max(strtolower($user->user_login)) || c_ws_plugin__s2member_ip_restrictions::specific_ip_restriction_breached_security(strtolower($user->user_login))) ? '<br /><em>*WARNING* this User is at (or above) max allowable IP addresses (based on your IP Restrictions).</em>' : '<br /><em>*Note* this User is NOT currently banned by any of your IP Restrictions.</em>').'</td>'."\n";
262
  echo '</tr>'."\n";
263
 
118
  unset($__refs, $__v);
119
 
120
  echo '<tr>'."\n";
121
+ echo '<th><label for="ws-plugin--s2member-profile-s2member-subscr-cid">Paid Subscr. CID:</label> <a href="#" onclick="alert(\'A Paid Subscription CID; i.e., a Customer\\\'s ID. Applicable only with Stripe integration. s2Member fills this in automatically. This is the Customer\\\'s ID in Stripe, which remains constant throughout any & all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID.\\n\\nIn all other cases, the Paid Subscr. CID is simply set to the Paid Subscr. ID value; i.e., it is a duplicate of Paid Subscr. ID when running anything other than Stripe.\\n\\nThis field will be empty for Free Subscribers, and/or anyone who is NOT paying you. This field is only editable for Customer Service purposes; just in case you ever need to update the Paid Subscr. Gateway/ID/CID manually.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
122
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_subscr_cid" id="ws-plugin--s2member-profile-s2member-subscr-cid" value="'.format_to_edit(get_user_option("s2member_subscr_cid", $user_id)).'" class="regular-text" /></td>'."\n";
123
  echo '</tr>'."\n";
124
 
146
  unset($__refs, $__v);
147
 
148
  echo '<tr>'."\n";
149
+ echo '<th><label for="ws-plugin--s2member-profile-s2member-coupon-codes">Coupon Code(s):</label> <a href="#" onclick="alert(\'This is a comma-delimited list of the Coupon Codes associated with this user; i.e., the Coupon Code(s) that have been used to complete checkout. s2Member updates this list automatically. This field is only editable for Customer Service purposes; i.e., just in case you ever need to update the list manually.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
150
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_coupon_codes" id="ws-plugin--s2member-profile-s2member-coupon-codes" value="'.format_to_edit(implode(',', is_array($_user_coupon_codes = get_user_option("s2member_coupon_codes", $user_id)) ? $_user_coupon_codes : array())).'" class="regular-text" /></td>'."\n";
151
  echo '</tr>'."\n";
152
 
178
  unset($__refs, $__v);
179
 
180
  echo '<tr>'."\n";
181
+ echo '<th><label for="ws-plugin--s2member-profile-s2member-ccaps">Custom Capabilities:</label> <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member API Scripting Custom Capabilities.'.((is_multisite()) ? '\\n\\nCustom Capabilities are assigned on a per-Blog basis. So having a set of Custom Capabilities for one Blog, and having NO Custom Capabilities on another Blog - is very common. This is how permissions are designed to work.' : '').'\'); return false;" tabindex="-1">[?]</a>'.((is_multisite()) ? '<br /><small>(for this Blog)</small>' : '').'</th>'."\n";
182
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_ccaps" id="ws-plugin--s2member-profile-s2member-ccaps" value="'.format_to_edit(((!empty($ccaps)) ? implode(",", $ccaps) : "")).'" class="regular-text" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" /></td>'."\n";
183
  echo '</tr>'."\n";
184
 
195
  echo '<tr>'."\n";
196
  $auto_eot_time = get_user_option("s2member_auto_eot_time", $user_id);
197
  $auto_eot_time = ($auto_eot_time) ? date("D M j, Y g:i a T", $auto_eot_time) : "";
198
+ echo '<th><label for="ws-plugin--s2member-profile-s2member-auto-eot-time">Automatic EOT Time:</label> <a href="#" onclick="alert(\'EOT = End Of Term. ( i.e., Account Expiration / Termination. ).\\n\\nIf you leave this empty, s2Member will configure an EOT Time automatically, based on the paid Subscription associated with this account. In other words, if a paid Subscription expires, is cancelled, terminated, refunded, reversed, or charged back to you; s2Member will deal with the EOT automatically.\\n\\nThat being said, if you would rather take control over this, you can. If you type in a date manually, s2Member will obey the Auto-EOT Time that you\\\'ve given, no matter what. In other words, you can force certain Members to expire automatically, at a time that you specify. s2Member will obey.\\n\\nValid formats for Automatic EOT Time:\\n\\nmm/dd/yyyy\\nyyyy-mm-dd\\n+1 year\\n+2 weeks\\n+2 months\\n+10 minutes\\nnext thursday\\ntomorrow\\ntoday\\n\\n* anything compatible with PHP\\\'s strtotime() function.\'); return false;" tabindex="-1">[?]</a>'.(($auto_eot_time) ? '<br /><small>(<a href="http://en.wikipedia.org/wiki/Coordinated_Universal_Time" target="_blank" rel="external">Universal Time / GMT</a>)</small>' : '').'</th>'."\n";
199
  echo '<td><input type="text" autocomplete="off" name="ws_plugin__s2member_profile_s2member_auto_eot_time" id="ws-plugin--s2member-profile-s2member-auto-eot-time" value="'.format_to_edit($auto_eot_time).'" class="regular-text" /></td>'."\n";
200
  echo '</tr>'."\n";
201
 
210
  unset($__refs, $__v);
211
 
212
  echo '<tr>'."\n";
213
+ echo '<th><label for="ws-plugin--s2member-profile-reset-pass-resend">Reset Password &amp; Resend Welcome Email Message:</label> <a href="#" onclick="alert(\'Checking this box will tell s2Member to reset this User\\\'s password and then reprocess the New User Email Notification message against this User\\\'s account. This way they\\\'ll get an email message with their Username/Password.\\n\\nThis can be helpful in cases where a User/Member missed the original email message for some reason.\\n\\nThe User\\\'s password is reset to a new auto-generated password by default. However, you can provide a custom password by entering a new password of your choosing in the field above—provided by WordPress itself.\\n\\nIt is also possible to customize the New User Email Notification message with s2Member. Please see: `Dashboard s2Member General Options Email Configuration New User Notifications`.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
214
  echo '<td><label><input type="checkbox" name="ws_plugin__s2member_profile_reset_pass_resend" id="ws-plugin--s2member-profile-reset-pass-resend" value="1" /> Yes, reset password &amp; resend welcome email message to this User.</label></td>'."\n";
215
  echo '</tr>'."\n";
216
 
257
  unset($__refs, $__v);
258
 
259
  echo '<tr>'."\n";
260
+ echo '<th><label for="ws-plugin--s2member-profile-ip-restrictions">Reset IP Restrictions:</label> <a href="#" onclick="alert(\'A single Username is only valid for a certain number of unique IP addresses (as configured in your s2Member General Options). Once that limit is reached, s2Member assumes there has been a security breach. At that time, s2Member will place a temporary ban (preventing access).\\n\\nIf you have spoken to a legitimate Customer that is receiving an error upon logging in (ex: 503 / too many IP addresses), you can remove this temporary ban by checking the box below. If the abusive behavior continues, s2Member will automatically re-instate IP Restrictions in the future. If you would like to gain further control over IP Restrictions, please check your General Options panel for s2Member.\'); return false;" tabindex="-1">[?]</a></th>'."\n";
261
  echo '<td><label><input type="checkbox" name="ws_plugin__s2member_profile_ip_restrictions" id="ws-plugin--s2member-profile-ip-restrictions" value="1" /> Yes, delete/reset IP Restrictions associated with this Username.</label>'.((c_ws_plugin__s2member_ip_restrictions::specific_ip_restriction_at_or_above_max(strtolower($user->user_login)) || c_ws_plugin__s2member_ip_restrictions::specific_ip_restriction_breached_security(strtolower($user->user_login))) ? '<br /><em>*WARNING* this User is at (or above) max allowable IP addresses (based on your IP Restrictions).</em>' : '<br /><em>*Note* this User is NOT currently banned by any of your IP Restrictions.</em>').'</td>'."\n";
262
  echo '</tr>'."\n";
263
 
includes/classes/utilities.inc.php CHANGED
@@ -100,7 +100,7 @@ if(!class_exists('c_ws_plugin__s2member_utilities'))
100
  $checksum .= (c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? '-'.WS_PLUGIN__S2MEMBER_PRO_VERSION : ''; // Pro version string?
101
  $checksum .= '-'.abs(crc32($GLOBALS['WS_PLUGIN__']['s2member']['c']['checksum'].$GLOBALS['WS_PLUGIN__']['s2member']['o']['options_checksum'].$GLOBALS['WS_PLUGIN__']['s2member']['o']['options_version']));
102
 
103
- return $checksum; // (i.e. version-pro version-checksum)
104
  }
105
 
106
  /**
@@ -156,7 +156,7 @@ if(!class_exists('c_ws_plugin__s2member_utilities'))
156
  $badge = preg_replace('/%%no_cache%%/i', (($no_cache) ? '&amp;no_cache='.urlencode(mt_rand(0, PHP_INT_MAX)) : ''), $badge);
157
  $badge = preg_replace('/%%display_on_failure%%/i', (($display_on_failure) ? '&amp;display_on_failure=1' : ''), $badge);
158
  }
159
- return (!empty($badge)) ? $badge : ''; // Return Security Badge.
160
  }
161
 
162
  /**
@@ -202,12 +202,12 @@ if(!class_exists('c_ws_plugin__s2member_utilities'))
202
  *
203
  * @return array Array of backtrace callers (lowercase).
204
  */
205
- public static function callers($debug_backtrace = array())
206
  {
207
  $callers = array(); // Initialize array.
208
- foreach(($debug_backtrace = (is_array($debug_backtrace)) ? $debug_backtrace : debug_backtrace()) as $caller)
209
- if(isset ($caller['class'], $caller['function']) || (!isset ($caller['class']) && isset ($caller['function'])))
210
- $callers[] = (isset ($caller['class'])) ? $caller['class'].'::'.$caller['function'] : $caller['function'];
211
 
212
  return array_map('strtolower', array_unique($callers));
213
  }
100
  $checksum .= (c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? '-'.WS_PLUGIN__S2MEMBER_PRO_VERSION : ''; // Pro version string?
101
  $checksum .= '-'.abs(crc32($GLOBALS['WS_PLUGIN__']['s2member']['c']['checksum'].$GLOBALS['WS_PLUGIN__']['s2member']['o']['options_checksum'].$GLOBALS['WS_PLUGIN__']['s2member']['o']['options_version']));
102
 
103
+ return $checksum; // (i.e., version-pro version-checksum)
104
  }
105
 
106
  /**
156
  $badge = preg_replace('/%%no_cache%%/i', (($no_cache) ? '&amp;no_cache='.urlencode(mt_rand(0, PHP_INT_MAX)) : ''), $badge);
157
  $badge = preg_replace('/%%display_on_failure%%/i', (($display_on_failure) ? '&amp;display_on_failure=1' : ''), $badge);
158
  }
159
+ return !empty($badge) ? $badge : ''; // Return Security Badge.
160
  }
161
 
162
  /**
202
  *
203
  * @return array Array of backtrace callers (lowercase).
204
  */
205
+ public static function callers($debug_backtrace = NULL)
206
  {
207
  $callers = array(); // Initialize array.
208
+ foreach(($debug_backtrace = is_array($debug_backtrace) ? $debug_backtrace : debug_backtrace()) as $caller)
209
+ if(isset($caller['class'], $caller['function']) || (!isset($caller['class']) && isset($caller['function'])))
210
+ $callers[] = isset($caller['class']) ? $caller['class'].'::'.$caller['function'] : $caller['function'];
211
 
212
  return array_map('strtolower', array_unique($callers));
213
  }
includes/classes/utils-conds.inc.php CHANGED
@@ -106,7 +106,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_conds'))
106
 
107
  /**
108
  * Determines whether or not this is a Multisite Farm;
109
- * *( i.e. if ``MULTISITE_FARM == true`` inside `/wp-config.php` )*.
110
  *
111
  * With s2Member, this option may also indicate a Multisite Blog Farm.
112
  * ``$GLOBALS['WS_PLUGIN__']['s2member']['o']['mms_registration_file'] === 'wp-signup'``.
106
 
107
  /**
108
  * Determines whether or not this is a Multisite Farm;
109
+ * *( i.e., if ``MULTISITE_FARM == true`` inside `/wp-config.php` )*.
110
  *
111
  * With s2Member, this option may also indicate a Multisite Blog Farm.
112
  * ``$GLOBALS['WS_PLUGIN__']['s2member']['o']['mms_registration_file'] === 'wp-signup'``.
includes/classes/utils-dirs.inc.php CHANGED
@@ -164,7 +164,7 @@ if (!class_exists ("c_ws_plugin__s2member_utils_dirs"))
164
  * @package s2Member\Utilities
165
  * @since 111013
166
  *
167
- * @param string $jctn Directory location of the Junction (i.e. the link).
168
  * @param string $target Target directory that this Junction will connect to.
169
  * @return bool True if created successfully, or already exists, else false.
170
  */
164
  * @package s2Member\Utilities
165
  * @since 111013
166
  *
167
+ * @param string $jctn Directory location of the Junction (i.e., the link).
168
  * @param string $target Target directory that this Junction will connect to.
169
  * @return bool True if created successfully, or already exists, else false.
170
  */
includes/classes/utils-encryption.inc.php CHANGED
@@ -72,16 +72,16 @@ if(!class_exists('c_ws_plugin__s2member_utils_encryption'))
72
  public static function uunnci_key_20_max()
73
  {
74
  $microtime_19_max = number_format(microtime(TRUE), 9, '.', '');
75
- // e.g. `9999999999`.`999999999` (max decimals: `9`, max overall precision: `19`).
76
- // Assuming timestamp is never > 10 digits; i.e. before `Sat, 20 Nov 2286 17:46:39 GMT`.
77
 
78
  list($seconds_10_max, $microseconds_9_max) = explode('.', $microtime_19_max, 2);
79
- // e.g. `array(`9999999999`, `999999999`)`. Max total digits combined: `19`.
80
 
81
- $seconds_base36 = base_convert($seconds_10_max, '10', '36'); // e.g. max `9999999999`, to base 36.
82
- $microseconds_base36 = base_convert($microseconds_9_max, '10', '36'); // e.g. max `999999999`, to base 36.
83
- $mt_rand_base36 = base_convert(mt_rand(1, 999999999), '10', '36'); // e.g. max `999999999`, to base 36.
84
- $key = 'k'.$mt_rand_base36.$seconds_base36.$microseconds_base36; // e.g. `kgjdgxr4ldqpdrgjdgxr`.
85
 
86
  return $key; // Max possible value: `kgjdgxr4ldqpdrgjdgxr` (20 chars).
87
  }
@@ -141,7 +141,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_encryption'))
141
  $e = isset($base64[0]) ? c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64) : '';
142
 
143
  if(function_exists('mcrypt_decrypt') && in_array('rijndael-256', mcrypt_list_algorithms()) && in_array('cbc', mcrypt_list_modes()))
144
- if(isset($e[0]) && preg_match('/^~r2\:([a-zA-Z0-9]+)(?:\:([a-zA-Z0-9]+))?\|(.*?)$/s', $e, $iv_md5_e))
145
  {
146
  $key = c_ws_plugin__s2member_utils_encryption::key($key);
147
  $key = substr($key, 0, mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
@@ -209,7 +209,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_encryption'))
209
  $base64 = is_string($base64) ? $base64 : '';
210
  $e = isset($base64[0]) ? c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64) : '';
211
 
212
- if(isset($e[0]) && preg_match('/^~xe(?:\:([a-zA-Z0-9]+))?\|(.*?)$/s', $e, $md5_e))
213
  {
214
  $key = c_ws_plugin__s2member_utils_encryption::key($key);
215
 
72
  public static function uunnci_key_20_max()
73
  {
74
  $microtime_19_max = number_format(microtime(TRUE), 9, '.', '');
75
+ // e.g., `9999999999`.`999999999` (max decimals: `9`, max overall precision: `19`).
76
+ // Assuming timestamp is never > 10 digits; i.e., before `Sat, 20 Nov 2286 17:46:39 GMT`.
77
 
78
  list($seconds_10_max, $microseconds_9_max) = explode('.', $microtime_19_max, 2);
79
+ // e.g., `array(`9999999999`, `999999999`)`. Max total digits combined: `19`.
80
 
81
+ $seconds_base36 = base_convert($seconds_10_max, '10', '36'); // e.g., max `9999999999`, to base 36.
82
+ $microseconds_base36 = base_convert($microseconds_9_max, '10', '36'); // e.g., max `999999999`, to base 36.
83
+ $mt_rand_base36 = base_convert(mt_rand(1, 999999999), '10', '36'); // e.g., max `999999999`, to base 36.
84
+ $key = 'k'.$mt_rand_base36.$seconds_base36.$microseconds_base36; // e.g., `kgjdgxr4ldqpdrgjdgxr`.
85
 
86
  return $key; // Max possible value: `kgjdgxr4ldqpdrgjdgxr` (20 chars).
87
  }
141
  $e = isset($base64[0]) ? c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64) : '';
142
 
143
  if(function_exists('mcrypt_decrypt') && in_array('rijndael-256', mcrypt_list_algorithms()) && in_array('cbc', mcrypt_list_modes()))
144
+ if(isset($e[0]) && preg_match('/^~r2\:([a-zA-Z0-9]+)(?:\:([a-zA-Z0-9]+))?\|(.*)$/s', $e, $iv_md5_e))
145
  {
146
  $key = c_ws_plugin__s2member_utils_encryption::key($key);
147
  $key = substr($key, 0, mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
209
  $base64 = is_string($base64) ? $base64 : '';
210
  $e = isset($base64[0]) ? c_ws_plugin__s2member_utils_strings::base64_url_safe_decode($base64) : '';
211
 
212
+ if(isset($e[0]) && preg_match('/^~xe(?:\:([a-zA-Z0-9]+))?\|(.*)$/s', $e, $md5_e))
213
  {
214
  $key = c_ws_plugin__s2member_utils_encryption::key($key);
215
 
includes/classes/utils-forms.inc.php CHANGED
@@ -46,7 +46,7 @@ if (!class_exists ("c_ws_plugin__s2member_utils_forms"))
46
  {
47
  foreach ((array)c_ws_plugin__s2member_utils_forms::form_whips_2_array($form) as $name => $value)
48
  {
49
- if (strlen ($name) && strlen ($value)) // Check $name -› $value lengths.
50
 
51
  if (strlen ($value = (preg_match ("/^http(s)?\:\/\//i", $value)) ? rawurlencode ($value) : urlencode ($value)))
52
  {
46
  {
47
  foreach ((array)c_ws_plugin__s2member_utils_forms::form_whips_2_array($form) as $name => $value)
48
  {
49
+ if (strlen ($name) && strlen ($value)) // Check $name $value lengths.
50
 
51
  if (strlen ($value = (preg_match ("/^http(s)?\:\/\//i", $value)) ? rawurlencode ($value) : urlencode ($value)))
52
  {
includes/classes/utils-gets.inc.php CHANGED
@@ -102,7 +102,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_gets'))
102
  if(($tag = trim($tag)) && is_numeric($tag)) // Force integers.
103
  $tag_ids[] = ($tag_id = (int)$tag); // Force integer values here.
104
 
105
- else if($tag && is_string($tag)) // A string (i.e. a tag name or a tag slug)?
106
  {
107
  if(is_object($term = get_term_by('name', $tag, 'post_tag')))
108
  $tag_ids[] = (int)$term->term_id;
@@ -382,7 +382,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_gets'))
382
  * The ``$exclude_conflicts`` argument should be used whenever we introduce a list of option values to a site owner. Helping them avoid mishaps.
383
  * Please note, the ``$exclude_conflicts`` argument implements a resource-intensive processing routine.
384
  *
385
- * @return array Unique array of all Singulars *(i.e. Posts/Pages )* protected with Specific Post/Page Access.
386
  * Includes Custom Post Types also, as specified by site owner's Specific Post/Page Restrictions.
387
  */
388
  public static function get_all_singulars_with_sp($exclude_conflicts = FALSE)
102
  if(($tag = trim($tag)) && is_numeric($tag)) // Force integers.
103
  $tag_ids[] = ($tag_id = (int)$tag); // Force integer values here.
104
 
105
+ else if($tag && is_string($tag)) // A string (i.e., a tag name or a tag slug)?
106
  {
107
  if(is_object($term = get_term_by('name', $tag, 'post_tag')))
108
  $tag_ids[] = (int)$term->term_id;
382
  * The ``$exclude_conflicts`` argument should be used whenever we introduce a list of option values to a site owner. Helping them avoid mishaps.
383
  * Please note, the ``$exclude_conflicts`` argument implements a resource-intensive processing routine.
384
  *
385
+ * @return array Unique array of all Singulars *(i.e., Posts/Pages )* protected with Specific Post/Page Access.
386
  * Includes Custom Post Types also, as specified by site owner's Specific Post/Page Restrictions.
387
  */
388
  public static function get_all_singulars_with_sp($exclude_conflicts = FALSE)
includes/classes/utils-logs.inc.php CHANGED
@@ -33,8 +33,8 @@ if(!class_exists('c_ws_plugin__s2member_utils_logs'))
33
  * @since 130315
34
  * @package s2Member\Utilities
35
  *
36
- * @param string $slug The file name; i.e. a slug.
37
- * e.g. `mailchimp-api`, `s2-http-api-debug`.
38
  *
39
  * @param mixed $data The data to log.
40
  */
@@ -210,30 +210,31 @@ if(!class_exists('c_ws_plugin__s2member_utils_logs'))
210
  */
211
  public static $log_file_descriptions = array
212
  ( // Array keys are regex patterns matching their associated log file names.
213
- '/paypal\-api/' => array('short' => 'PayPal API communication.', 'long' => 'This log file records all communication between s2Member and PayPal APIs. Such as PayPal Button Encryption and PayPal Pro API calls that process transactions. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
214
- '/paypal\-payflow\-api/' => array('short' => 'PayPal (PayFlow Edition) API communication.', 'long' => 'This log file records all communication between s2Member and the PayPal (PayFlow Edition) APIs. Only applicable if you operate a PayPal Payments Pro (PayFlow Edition) account. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
215
- '/gateway\-core\-ipn/' => array('short' => 'Core PayPal IPN and post-processing handler.', 'long' => 'This log file records all communication between s2Member and the PayPal IPN service. Also logs all post-processing routines from other Payment Gateway integrations, where s2Member translates its communication with other Payment Gateways into a format it\'s core PayPal processing routines can understand. All transactions pass through s2Member\'s core PayPal processor and they will be logged in this file. Including transactions processed via s2Member Pro Forms; for all Payment Gateway integrations.'),
216
- '/gateway\-core\-rtn/' => array('short' => 'Core PayPal PDT/Auto-Return communication.', 'long' => 'This log file records all communication between s2Member and the PayPal PDT Auto-Return system (i.e. routines that help s2Member process Thank-You pages). Also logs all Auto-Return routines from other Payment Gateway integrations (those implemented via Payment Buttons), where s2Member translates its communication with other Payment Gateways into a format it\'s core PayPal processing routines can understand. Not used in s2Member Pro Form integrations however.'),
217
 
218
- '/stripe\-api/' => array('short' => 'Stripe API communication.', 'long' => 'This log file records all communication between s2Member and Stripe APIs.'),
219
- '/stripe\-ipn/' => array('short' => 'Stripe Webhook/IPN communication.', 'long' => 'This log file records the Webhook/IPN data that Stripe sends to s2Member.'),
220
 
221
- '/authnet\-api/' => array('short' => 'Authorize.Net API communication.', 'long' => 'This log file records all communication between s2Member and Authorize.Net APIs (for both AIM and ARB integrations).'),
222
- '/authnet\-arb/' => array('short' => 'Authorize.Net ARB Subscription status checks.', 'long' => 'This log file records s2Member\'s Authorize.Net ARB Subscription status checks. s2Member polls the ARB service periodically to check the status of existing Members (e.g. to see if billing is still active or not).'),
223
- '/authnet\-ipn/' => array('short' => 'Authorize.Net Silent Post/IPN communication.', 'long' => 'This log file records the Silent Post/IPN data Authorize.Net sends to s2Member with details regarding new transactions.'),
 
 
 
224
 
225
  '/alipay\-ipn/' => array('short' => 'AliPay IPN communication.', 'long' => 'This log file records the IPN data AliPay sends to s2Member with details regarding new transactions. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
226
- '/alipay\-rtn/' => array('short' => 'AliPay Auto-Return communication.', 'long' => 'This log file records the Auto-Return data AliPay sends to s2Member with details regarding new transactions (i.e. logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor).'),
227
 
228
  '/clickbank\-ipn/' => array('short' => 'ClickBank IPN communication.', 'long' => 'This log file records the IPN data ClickBank sends to s2Member with details regarding new transactions, cancellations, expirations, etc. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
229
- '/clickbank\-rtn/' => array('short' => 'ClickBank Auto-Return communication.', 'long' => 'This log file records the Auto-Return data ClickBank sends to s2Member with details regarding new transactions (i.e. logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor).'),
230
 
231
- '/google\-rtn/' => array('short' => 'Google Auto-Return communication.', 'long' => 'This log file records the Auto-Return data Google sends to s2Member with details regarding new transactions (i.e. logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor). NOTE (regarding Google Wallet)... this particular log file is currently implemented for a possible future use ONLY. At this time there is no need for an Auto-Return handler with Google Wallet, because Google Wallet return handling is done via email-only at this time.'),
232
  '/google\-ipn/' => array('short' => 'Google Postback/IPN communication.', 'long' => 'This log file records the Postback/IPN data Google sends to s2Member with details regarding new transactions, cancellations, expirations, etc. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
233
 
234
  '/ccbill\-ipn/' => array('short' => 'ccBill Bg Post/IPN communication.', 'long' => 'This log file records the Bg Post/IPN data ccBill sends to s2Member with details regarding new transactions. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
235
- '/ccbill\-rtn/' => array('short' => 'ccBill Auto-Return communication.', 'long' => 'This log file records the Auto-Return data ccBill sends to s2Member with details regarding new transactions (i.e. logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor).'),
236
- '/ccbill\-dl\-ipn/' => array('short' => 'ccBill Datalink Subscription status checks.', 'long' => 'This log file records s2Member\'s ccBill Datalink Subscription status checks that may result in actions taken by s2Member. s2Member polls the ccBill Datalink service periodically to check the status of existing Members (e.g. to see if billing is still active or not).'),
237
  '/ccbill\-dl/' => array('short' => 'ccBill Datalink collections.', 'long' => 'This log file records s2Member\'s ccBill Datalink connections. s2Member polls the ccBill Datalink service periodically to obtain information about existing Users/Members.'),
238
 
239
  '/mailchimp\-api/' => array('short' => 'MailChimp API communication.', 'long' => 'This log file records all of s2Member\'s communication with MailChimp APIs.'),
33
  * @since 130315
34
  * @package s2Member\Utilities
35
  *
36
+ * @param string $slug The file name; i.e., a slug.
37
+ * e.g., `mailchimp-api`, `s2-http-api-debug`.
38
  *
39
  * @param mixed $data The data to log.
40
  */
210
  */
211
  public static $log_file_descriptions = array
212
  ( // Array keys are regex patterns matching their associated log file names.
213
+ '/gateway\-core\-ipn/' => array('short' => 'Core IPN and post-processing handler.', 'long' => 'This log file records all communication between s2Member and the IPN/Webhook/Callback services associated with various payment gateways. All transactions pass through s2Member\'s core processor, and they will be logged in this file; including transactions processed via s2Member Pro-Forms—for all Payment Gateway integrations.'),
214
+ '/gateway\-core\-rtn/' => array('short' => 'Core PDT/Auto-Return communication.', 'long' => 'This log file records all communication between s2Member and the PDT/Auto-Return/Thank-You services associated with various payment gateways (i.e., routines that help s2Member process Thank-You pages). This log file is not used in s2Member Pro-Form integrations however.'),
 
 
215
 
216
+ '/stripe\-api/' => array('short' => 'Stripe API communication.', 'long' => 'This log file records all communication between s2Member and Stripe APIs. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
217
+ '/stripe\-ipn/' => array('short' => 'Stripe Webhook/IPN communication.', 'long' => 'This log file records the Webhook/IPN data that Stripe sends to s2Member. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
218
 
219
+ '/paypal\-api/' => array('short' => 'PayPal API communication.', 'long' => 'This log file records all communication between s2Member and PayPal APIs. Such as PayPal Button Encryption and PayPal Pro API calls that process transactions. This log file may be used (in some scenarios), even if you\'re running a PayPal Payments Pro (Payflow Edition) account. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
220
+ '/paypal\-payflow\-api/' => array('short' => 'PayPal Pro (PayFlow Edition) API communication.', 'long' => 'This log file records all communication between s2Member and the PayPal Payments Pro (PayFlow Edition) APIs. This log file is only used if you operate a PayPal Payments Pro (PayFlow Edition) account; i.e., only if you integrate s2Member Pro with Payflow for Recurring Billing. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
221
+
222
+ '/authnet\-api/' => array('short' => 'Authorize.Net API communication.', 'long' => 'This log file records all communication between s2Member and Authorize.Net APIs (for both AIM and ARB integrations). See also: gateway-core-ipn.log (s2Member\'s core processor).'),
223
+ '/authnet\-arb/' => array('short' => 'Authorize.Net ARB Subscription status checks.', 'long' => 'This log file records s2Member\'s Authorize.Net ARB Subscription status checks. s2Member polls the ARB service periodically to check the status of existing Members (e.g., to see if billing is still active or not).'),
224
+ '/authnet\-ipn/' => array('short' => 'Authorize.Net Silent Post/IPN communication.', 'long' => 'This log file records the Silent Post/IPN data Authorize.Net sends to s2Member with details regarding new transactions. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
225
 
226
  '/alipay\-ipn/' => array('short' => 'AliPay IPN communication.', 'long' => 'This log file records the IPN data AliPay sends to s2Member with details regarding new transactions. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
227
+ '/alipay\-rtn/' => array('short' => 'AliPay Auto-Return communication.', 'long' => 'This log file records the Auto-Return data AliPay sends to s2Member with details regarding new transactions (i.e., logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor).'),
228
 
229
  '/clickbank\-ipn/' => array('short' => 'ClickBank IPN communication.', 'long' => 'This log file records the IPN data ClickBank sends to s2Member with details regarding new transactions, cancellations, expirations, etc. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
230
+ '/clickbank\-rtn/' => array('short' => 'ClickBank Auto-Return communication.', 'long' => 'This log file records the Auto-Return data ClickBank sends to s2Member with details regarding new transactions (i.e., logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor).'),
231
 
232
+ '/google\-rtn/' => array('short' => 'Google Auto-Return communication.', 'long' => 'This log file records the Auto-Return data Google sends to s2Member with details regarding new transactions (i.e., logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor). NOTE (regarding Google Wallet)... this particular log file is currently implemented for a possible future use ONLY. At this time there is no need for an Auto-Return handler with Google Wallet, because Google Wallet return handling is done via email-only at this time.'),
233
  '/google\-ipn/' => array('short' => 'Google Postback/IPN communication.', 'long' => 'This log file records the Postback/IPN data Google sends to s2Member with details regarding new transactions, cancellations, expirations, etc. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
234
 
235
  '/ccbill\-ipn/' => array('short' => 'ccBill Bg Post/IPN communication.', 'long' => 'This log file records the Bg Post/IPN data ccBill sends to s2Member with details regarding new transactions. See also: gateway-core-ipn.log (s2Member\'s core processor).'),
236
+ '/ccbill\-rtn/' => array('short' => 'ccBill Auto-Return communication.', 'long' => 'This log file records the Auto-Return data ccBill sends to s2Member with details regarding new transactions (i.e., logs routines that help s2Member process Thank-You pages). See also: gateway-core-rtn.log (s2Member\'s core processor).'),
237
+ '/ccbill\-dl\-ipn/' => array('short' => 'ccBill Datalink Subscription status checks.', 'long' => 'This log file records s2Member\'s ccBill Datalink Subscription status checks that may result in actions taken by s2Member. s2Member polls the ccBill Datalink service periodically to check the status of existing Members (e.g., to see if billing is still active or not).'),
238
  '/ccbill\-dl/' => array('short' => 'ccBill Datalink collections.', 'long' => 'This log file records s2Member\'s ccBill Datalink connections. s2Member polls the ccBill Datalink service periodically to obtain information about existing Users/Members.'),
239
 
240
  '/mailchimp\-api/' => array('short' => 'MailChimp API communication.', 'long' => 'This log file records all of s2Member\'s communication with MailChimp APIs.'),
includes/classes/utils-time.inc.php CHANGED
@@ -103,20 +103,20 @@ if(!class_exists('c_ws_plugin__s2member_utils_time'))
103
  * Calculate Auto-EOT Time, based on `user_id`, `period1`, `period3`, `last_payment_time`, or an optional `eotper`.
104
  *
105
  * Used by s2Member's built-in Auto-EOT System, and also by its IPN routines.
106
- * `last_payment_time` can be forced w/ ``$lpt`` *(i.e. for delayed eots)*.
107
  *
108
  * @package s2Member\Utilities
109
  * @since 3.5
110
  *
111
  * @param int|string $user_id Optional. A WordPress User ID.
112
  *
113
- * @param string $period1 Optional. First Intial "Period Term" *( i.e. `0 D` )*.
114
  * Only used when ``$user_id`` is passed in.
115
  *
116
- * @param string $period3 Optional. Regular "Period Term" *( i.e. `1 M` )*.
117
  * Only used when ``$user_id`` is passed in.
118
  *
119
- * @param string $eotper Optional. A Fixed "Period Term" *( i.e. `1 M` )*.
120
  * This replaces ``$period1`` / ``$period3``.
121
  * Not used when ``$user_id`` is passed in.
122
  * Only when ``$user_id`` is not passed in.
@@ -228,7 +228,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_time'))
228
  * @param string $term_or_period_term A Term, or a "Period Term" combination.
229
  * @param string $directive Optional. One of `recurring|singular|plural`. Defaults to `recurring`.
230
  *
231
- * @return string|bool A Term Cycle *( i.e. `Daily`, `Weekly`, `Monthly`, `Yearly`, `Lifetime`, etc. )*, else false on failure.
232
  *
233
  * @todo Add support here for fixed recurring payments configured through `rrt=""`.
234
  */
@@ -274,7 +274,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_time'))
274
  * @param string $period_term A "Period Term" combination.
275
  * @param bool|int|string $recurring Defaults to false. If true, the ``$period_term`` is recurring. Can also be the string `0|1|BN`.
276
  *
277
- * @return string Verbose *(lowercase)* Period Term description *( i.e. `weekly`, `every 3 weeks`, `lifetime`, `3 months`, `1 month`, etc. )*.
278
  *
279
  * @todo Add support here for fixed recurring payments configured through `rrt=""`.
280
  */
@@ -291,14 +291,14 @@ if(!class_exists('c_ws_plugin__s2member_utils_time'))
291
  $period_term = strtolower($cycle_recurring); // Results in an "ly" ending.
292
 
293
  else if($recurring) // Otherwise, it's recurring; but NOT an "ly" ending.
294
- /* translators: Each cycle ( i.e. `each day/week/month` or `every 2 days/weeks/months`, etc. ). Cycles are translated elsewhere. */
295
  $period_term = strtolower(sprintf(_nx('each %2$s', 'every %1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
296
 
297
  else if(strtoupper($term) === 'L') // One-payment for lifetime access.
298
  $period_term = strtolower(_x('lifetime', 's2member-front', 's2member')); // Life.
299
 
300
  else // Otherwise, this is NOT recurring. Results in X days/weeks/months/years/lifetime.
301
- /* translators: Membership cycle ( i.e. `1 day/week/month` or `2 days/weeks/months`, etc. ). Most of this is translated elsewhere. */
302
  $period_term = strtolower(sprintf(_nx('%1$s %2$s', '%1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
303
 
304
  return $period_term; // Return converted value.
@@ -317,7 +317,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_time'))
317
  * @param string $period_term A "Period Term" combo, with space separation.
318
  * @param bool|int|string $recurring Defaults to false. If true, the ``$period_term`` is recurring. Can also be the string `0|1|BN`.
319
  *
320
- * @return string Verbose *(lowercase)* Amount Period Term description *( i.e. `1.00`, `1.00 / monthly`, `1.00 every 3 months`, `1.00 for 1 month`, `1.00 for 3 months`, etc. )*.
321
  *
322
  * @todo Add support here for fixed recurring payments configured through `rrt=""`.
323
  */
@@ -334,14 +334,14 @@ if(!class_exists('c_ws_plugin__s2member_utils_time'))
334
  $amount_period_term = number_format($amount, 2, '.', '').' / '.strtolower($cycle_recurring);
335
 
336
  else if($recurring) // Otherwise, it's recurring; but NOT an "ly" ending.
337
- /* translators: Each cycle ( i.e. `each day/week/month` or `every 2 days/weeks/months`, etc. ). Cycles are translated elsewhere. */
338
  $amount_period_term = number_format($amount, 2, '.', '').' '.strtolower(sprintf(_nx('each %2$s', 'every %1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
339
 
340
  else if(strtoupper($term) === 'L') // One-payment for lifetime access.
341
  $amount_period_term = number_format($amount, 2, '.', ''); // Price.
342
 
343
  else // Otherwise, this is NOT recurring. Results in 0.00 for X days/weeks/months/years/lifetime.
344
- /* translators: Cycle ( i.e. `for 1 day/week/month` or `for 2 days/weeks/months`, etc. ). Most of this is translated elsewhere. */
345
  $amount_period_term = number_format($amount, 2, '.', '').' '.strtolower(sprintf(_nx('for %1$s %2$s', 'for %1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
346
 
347
  return $amount_period_term; // Return converted value.
103
  * Calculate Auto-EOT Time, based on `user_id`, `period1`, `period3`, `last_payment_time`, or an optional `eotper`.
104
  *
105
  * Used by s2Member's built-in Auto-EOT System, and also by its IPN routines.
106
+ * `last_payment_time` can be forced w/ ``$lpt`` *(i.e., for delayed eots)*.
107
  *
108
  * @package s2Member\Utilities
109
  * @since 3.5
110
  *
111
  * @param int|string $user_id Optional. A WordPress User ID.
112
  *
113
+ * @param string $period1 Optional. First Intial "Period Term" *( i.e., `0 D` )*.
114
  * Only used when ``$user_id`` is passed in.
115
  *
116
+ * @param string $period3 Optional. Regular "Period Term" *( i.e., `1 M` )*.
117
  * Only used when ``$user_id`` is passed in.
118
  *
119
+ * @param string $eotper Optional. A Fixed "Period Term" *( i.e., `1 M` )*.
120
  * This replaces ``$period1`` / ``$period3``.
121
  * Not used when ``$user_id`` is passed in.
122
  * Only when ``$user_id`` is not passed in.
228
  * @param string $term_or_period_term A Term, or a "Period Term" combination.
229
  * @param string $directive Optional. One of `recurring|singular|plural`. Defaults to `recurring`.
230
  *
231
+ * @return string|bool A Term Cycle *( i.e., `Daily`, `Weekly`, `Monthly`, `Yearly`, `Lifetime`, etc. )*, else false on failure.
232
  *
233
  * @todo Add support here for fixed recurring payments configured through `rrt=""`.
234
  */
274
  * @param string $period_term A "Period Term" combination.
275
  * @param bool|int|string $recurring Defaults to false. If true, the ``$period_term`` is recurring. Can also be the string `0|1|BN`.
276
  *
277
+ * @return string Verbose *(lowercase)* Period Term description *( i.e., `weekly`, `every 3 weeks`, `lifetime`, `3 months`, `1 month`, etc. )*.
278
  *
279
  * @todo Add support here for fixed recurring payments configured through `rrt=""`.
280
  */
291
  $period_term = strtolower($cycle_recurring); // Results in an "ly" ending.
292
 
293
  else if($recurring) // Otherwise, it's recurring; but NOT an "ly" ending.
294
+ /* translators: Each cycle ( i.e., `each day/week/month` or `every 2 days/weeks/months`, etc. ). Cycles are translated elsewhere. */
295
  $period_term = strtolower(sprintf(_nx('each %2$s', 'every %1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
296
 
297
  else if(strtoupper($term) === 'L') // One-payment for lifetime access.
298
  $period_term = strtolower(_x('lifetime', 's2member-front', 's2member')); // Life.
299
 
300
  else // Otherwise, this is NOT recurring. Results in X days/weeks/months/years/lifetime.
301
+ /* translators: Membership cycle ( i.e., `1 day/week/month` or `2 days/weeks/months`, etc. ). Most of this is translated elsewhere. */
302
  $period_term = strtolower(sprintf(_nx('%1$s %2$s', '%1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
303
 
304
  return $period_term; // Return converted value.
317
  * @param string $period_term A "Period Term" combo, with space separation.
318
  * @param bool|int|string $recurring Defaults to false. If true, the ``$period_term`` is recurring. Can also be the string `0|1|BN`.
319
  *
320
+ * @return string Verbose *(lowercase)* Amount Period Term description *( i.e., `1.00`, `1.00 / monthly`, `1.00 every 3 months`, `1.00 for 1 month`, `1.00 for 3 months`, etc. )*.
321
  *
322
  * @todo Add support here for fixed recurring payments configured through `rrt=""`.
323
  */
334
  $amount_period_term = number_format($amount, 2, '.', '').' / '.strtolower($cycle_recurring);
335
 
336
  else if($recurring) // Otherwise, it's recurring; but NOT an "ly" ending.
337
+ /* translators: Each cycle ( i.e., `each day/week/month` or `every 2 days/weeks/months`, etc. ). Cycles are translated elsewhere. */
338
  $amount_period_term = number_format($amount, 2, '.', '').' '.strtolower(sprintf(_nx('each %2$s', 'every %1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
339
 
340
  else if(strtoupper($term) === 'L') // One-payment for lifetime access.
341
  $amount_period_term = number_format($amount, 2, '.', ''); // Price.
342
 
343
  else // Otherwise, this is NOT recurring. Results in 0.00 for X days/weeks/months/years/lifetime.
344
+ /* translators: Cycle ( i.e., `for 1 day/week/month` or `for 2 days/weeks/months`, etc. ). Most of this is translated elsewhere. */
345
  $amount_period_term = number_format($amount, 2, '.', '').' '.strtolower(sprintf(_nx('for %1$s %2$s', 'for %1$s %3$s', $period, 's2member-front', 's2member'), $period, $cycle_singular, $cycle_plural));
346
 
347
  return $amount_period_term; // Return converted value.
includes/classes/utils-users.inc.php CHANGED
@@ -304,7 +304,7 @@ if(!class_exists('c_ws_plugin__s2member_utils_users'))
304
  *
305
  * @param string $field_id Required. A unique Custom Registration/Profile Field ID, that you configured with s2Member.
306
  * Or, this could be set to any property that exists on the WP_User object for a particular User;
307
- * ( i.e. `id`, `ID`, `user_login`, `user_email`, `first_name`, `last_name`, `display_name`, `ip`, `IP`,
308
  * `s2member_registration_ip`, `s2member_custom`, `s2member_subscr_id`, `s2member_subscr_or_wp_id`,
309
  * `s2member_subscr_gateway`, `s2member_custom_fields`, `s2member_file_download_access_[log|arc]`,
310
  * `s2member_auto_eot_time`, `s2member_last_payment_time`, `s2member_paid_registration_times`,
304
  *
305
  * @param string $field_id Required. A unique Custom Registration/Profile Field ID, that you configured with s2Member.
306
  * Or, this could be set to any property that exists on the WP_User object for a particular User;
307
+ * ( i.e., `id`, `ID`, `user_login`, `user_email`, `first_name`, `last_name`, `display_name`, `ip`, `IP`,
308
  * `s2member_registration_ip`, `s2member_custom`, `s2member_subscr_id`, `s2member_subscr_or_wp_id`,
309
  * `s2member_subscr_gateway`, `s2member_custom_fields`, `s2member_file_download_access_[log|arc]`,
310
  * `s2member_auto_eot_time`, `s2member_last_payment_time`, `s2member_paid_registration_times`,
includes/externals/mailchimp/Mailchimp-o.php CHANGED
@@ -1187,7 +1187,7 @@ class Mailchimp_o {
1187
  * @example xml-rpc_listMergeVarAdd.php
1188
  *
1189
  * @param string $id the list id to connect to. Get by calling lists()
1190
- * @param string $tag The merge tag to add, e.g. FNAME
1191
  * @param string $name The long description of the tag being added, used for user displays
1192
  * @param array $options optional Various options for this merge var. <em>note:</em> for historical purposes this can also take a "boolean"
1193
  string field_type optional one of: text, number, radio, dropdown, date, address, phone, url, imageurl - defaults to text
1187
  * @example xml-rpc_listMergeVarAdd.php
1188
  *
1189
  * @param string $id the list id to connect to. Get by calling lists()
1190
+ * @param string $tag The merge tag to add, e.g., FNAME
1191
  * @param string $name The long description of the tag being added, used for user displays
1192
  * @param array $options optional Various options for this merge var. <em>note:</em> for historical purposes this can also take a "boolean"
1193
  string field_type optional one of: text, number, radio, dropdown, date, address, phone, url, imageurl - defaults to text
includes/externals/markdown/nc-markdown.inc.php CHANGED
@@ -863,7 +863,7 @@ class NC_Markdown_Parser {
863
  # an aspect of Markdown's syntax that's hard to parse perfectly
864
  # without resorting to mind-reading. Perhaps the solution is to
865
  # change the syntax rules such that sub-lists must start with a
866
- # starting cardinal number; e.g. "1." or "a.".
867
 
868
  $this->list_level++;
869
 
@@ -1284,7 +1284,7 @@ class NC_Markdown_Parser {
1284
 
1285
  function encodeEmailAddress($addr) {
1286
  #
1287
- # Input: an email address, e.g. "foo@example.com"
1288
  #
1289
  # Output: the email address as a mailto link, with each character
1290
  # of the address encoded as either a decimal or hex entity, in
863
  # an aspect of Markdown's syntax that's hard to parse perfectly
864
  # without resorting to mind-reading. Perhaps the solution is to
865
  # change the syntax rules such that sub-lists must start with a
866
+ # starting cardinal number; e.g., "1." or "a.".
867
 
868
  $this->list_level++;
869
 
1284
 
1285
  function encodeEmailAddress($addr) {
1286
  #
1287
+ # Input: an email address, e.g., "foo@example.com"
1288
  #
1289
  # Output: the email address as a mailto link, with each character
1290
  # of the address encoded as either a decimal or hex entity, in
includes/functions/api-functions.inc.php CHANGED
@@ -108,7 +108,7 @@ if(!function_exists("is_user_not_logged_in"))
108
  * @since 110524RC
109
  *
110
  * @param int|string $id A numeric WordPress User ID.
111
- * @param string $role A WordPress Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
112
  * @return bool True if the specific User is/has the specified Role, else false.
113
  *
114
  * @see s2Member\API_Functions\user_is()
@@ -193,7 +193,7 @@ if(!function_exists("user_is"))
193
  * @since 110524RC
194
  *
195
  * @param int|string $id A numeric WordPress User ID.
196
- * @param string $role A WordPress Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
197
  * @return bool True if the specific User is/does NOT have the specified Role, else false.
198
  *
199
  * @see s2Member\API_Functions\user_is()
@@ -270,7 +270,7 @@ if(!function_exists("user_is_not"))
270
  * @package s2Member\API_Functions
271
  * @since 3.5
272
  *
273
- * @param string $role A WordPress Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
274
  * @return bool True if the current User is/has the specified Role, else false.
275
  *
276
  * @see s2Member\API_Functions\user_is()
@@ -354,7 +354,7 @@ if(!function_exists("current_user_is"))
354
  * @package s2Member\API_Functions
355
  * @since 3.5
356
  *
357
- * @param string $role A WordPress Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
358
  * @return bool True if the current User is/does NOT have the specified Role, else false.
359
  *
360
  * @see s2Member\API_Functions\user_is()
@@ -437,7 +437,7 @@ if(!function_exists("current_user_is_not"))
437
  * @since 3.5
438
  *
439
  * @param int|string $blog_id A WordPress Blog ID *(must be numeric)*.
440
- * @param string $role A WordPress Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
441
  * @return bool True if the current User is/has the specified Role, on the specified Blog, else false.
442
  *
443
  * @see s2Member\API_Functions\user_is()
@@ -516,7 +516,7 @@ if(!function_exists("current_user_is_for_blog"))
516
  * @since 3.5
517
  *
518
  * @param int|string $blog_id A WordPress Blog ID *(must be numeric)*.
519
- * @param string $role A WordPress Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
520
  * @return bool True if the current User is/does NOT have the specified Role, on the specified Blog, else false.
521
  *
522
  * @see s2Member\API_Functions\user_is()
@@ -585,7 +585,7 @@ if(!function_exists("current_user_is_not_for_blog"))
585
  * @since 3.5
586
  *
587
  * @param int|string $id A numeric WordPress User ID.
588
- * @param string $capability A WordPress Capability ID *( i.e. `access_s2member_level[0-9]+`, `access_s2member_ccap_music` )*.
589
  * @return bool True if the specific User does NOT have the specified Capability or Role, else false.
590
  *
591
  * @see s2Member\API_Functions\user_is()
@@ -653,8 +653,8 @@ if(!function_exists("user_cannot"))
653
  * @package s2Member\API_Functions
654
  * @since 3.5
655
  *
656
- * @param string $capability A WordPress Capability ID *( i.e. `access_s2member_level[0-9]+`, `access_s2member_ccap_music` )*.
657
- * Or a Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
658
  * @return bool True if the current User does NOT have the specified Capability or Role, else false.
659
  *
660
  * @see s2Member\API_Functions\user_is()
@@ -723,8 +723,8 @@ if(!function_exists("current_user_cannot"))
723
  * @since 3.5
724
  *
725
  * @param int|string $blog_id A WordPress Blog ID *(must be numeric)*.
726
- * @param string $capability A WordPress Capability ID *( i.e. `access_s2member_level[0-9]+`, `access_s2member_ccap_music` )*.
727
- * Or a Role ID *( i.e. `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
728
  * @return bool True if the current User does NOT have the specified Capability or Role, else false.
729
  *
730
  * @see s2Member\API_Functions\user_is()
@@ -771,7 +771,7 @@ if(!function_exists("current_user_cannot_for_blog"))
771
  * o Note: when passing in a URL or URI, the $type parameter must be set to `URI` or `uri`. Case insensitive.
772
  *
773
  * **Parameter $type (str Optional).**
774
- * One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e. a Post or Page)*.
775
  *
776
  * **Parameter $check_user (bool Optional).**
777
  * Consider the current User? Defaults to false.
@@ -824,9 +824,9 @@ if(!function_exists("current_user_cannot_for_blog"))
824
  *
825
  * @param int|string $what Optional. Defaults to the current $post ID when called from within {@link http://codex.wordpress.org/The_Loop The Loop}.
826
  * If passed in, this should be a WordPress Category ID, Tag ID, Post ID, or Page ID. Or a full URL. A URI is also fine.
827
- * @param string $type Optional. One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e. a Post or Page)*.
828
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
829
- * @return array|bool A non-empty array *(meaning true)*, or false if the content is not protected *(i.e. available publicly)*.
830
  * When/if content IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
831
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the content.
832
  * In other words, the reason why it's protected; based on your s2Member configuration.
@@ -903,7 +903,7 @@ if(!function_exists("is_protected_by_s2member"))
903
  * o Note: when passing in a URL or URI, the $type parameter must be set to `URI` or `uri`. Case insensitive.
904
  *
905
  * **Parameter $type (str Optional).**
906
- * One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e. a Post or Page)*.
907
  *
908
  * ———— Code Sample Using Function Parameters ————
909
  * ```
@@ -949,7 +949,7 @@ if(!function_exists("is_protected_by_s2member"))
949
  *
950
  * @param int|string $what Optional. Defaults to the current $post ID when called from within {@link http://codex.wordpress.org/The_Loop The Loop}.
951
  * If passed in, this should be a WordPress Category ID, Tag ID, Post ID, or Page ID. Or a full URL. A URI is also fine.
952
- * @param string $type Optional. One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e. a Post or Page)*.
953
  * @return bool True if the current User IS permitted, else false if the content is NOT available to the current User;
954
  * based on your configuration of s2Member, and based on the current User's Role/Capabilities.
955
  *
@@ -1040,7 +1040,7 @@ if(!function_exists("is_permitted_by_s2member"))
1040
  *
1041
  * @param int $cat_id Required. This should be a WordPress Category ID.
1042
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1043
- * @return array|bool A non-empty array *(meaning true)*, or false if the Category is not protected *(i.e. available publicly)*.
1044
  * When/if the Category IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1045
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Category.
1046
  * In other words, the reason why it's protected; based on your s2Member configuration.
@@ -1196,7 +1196,7 @@ if(!function_exists("is_category_permitted_by_s2member"))
1196
  *
1197
  * @param int|string $tag_id_slug_or_name Required. This should be a WordPress Tag ID, Tag Slug, or Tag Name.
1198
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1199
- * @return array|bool A non-empty array *(meaning true)*, or false if the Tag is not protected *(i.e. available publicly)*.
1200
  * When/if the Tag IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1201
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Tag.
1202
  * In other words, the reason why it's protected; based on your s2Member configuration.
@@ -1352,7 +1352,7 @@ if(!function_exists("is_tag_permitted_by_s2member"))
1352
  *
1353
  * @param int $post_id Required. This should be a WordPress Post ID, or a Custom Post Type ID.
1354
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1355
- * @return array|bool A non-empty array *(meaning true)*, or false if the Post is not protected *(i.e. available publicly)*.
1356
  * When/if the Post IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1357
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Post.
1358
  * In other words, the reason why it's protected; based on your s2Member configuration.
@@ -1495,7 +1495,7 @@ if(!function_exists("is_post_permitted_by_s2member"))
1495
  *
1496
  * @param int $page_id Required. This should be a WordPress Page ID.
1497
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1498
- * @return array|bool A non-empty array *(meaning true)*, or false if the Page is not protected *(i.e. available publicly)*.
1499
  * When/if the Page IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1500
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Page.
1501
  * In other words, the reason why it's protected; based on your s2Member configuration.
@@ -1645,7 +1645,7 @@ if(!function_exists("is_page_permitted_by_s2member"))
1645
  *
1646
  * @param string $uri_or_full_url Required. This should be a URI starting with `/`, or a full URL is also fine.
1647
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1648
- * @return array|bool A non-empty array *(meaning true)*, or false if the URI or URL is not protected *(i.e. available publicly)*.
1649
  * When/if the URI or URL IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1650
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the URI or URL.
1651
  * In other words, the reason why it's protected; based on your s2Member configuration.
@@ -1852,7 +1852,7 @@ if(!function_exists("detach_s2member_query_filters"))
1852
  * Generates a File Download URL that provides access to a File protected by s2Member.
1853
  *
1854
  * By default, s2Member uses your Basic Download Restrictions. For more information on this,
1855
- * please check your Dashboard under: `s2Member -› Download Options -› Basic Download Restrictions`.
1856
  *
1857
  * ———— HTML/PHP Code Samples ————
1858
  * ```
@@ -1867,8 +1867,8 @@ if(!function_exists("detach_s2member_query_filters"))
1867
  *
1868
  * ———— Advanced Download Restrictions ————
1869
  *
1870
- * Or, you can also force s2Member to allow File Downloads, by requesting a File Download Key ( i.e. `file_download_key => true` ).
1871
- * When a File Download Key is requested through this parameter ( i.e. `file_download_key => true` ); it tells s2Member to allow the download of this particular file,
1872
  * regardless of Membership Level; and WITHOUT checking any Basic Restrictions, that you may, or may not have configured.
1873
  *
1874
  * ———— HTML/PHP Code Samples Using A Download Key ————
@@ -1887,27 +1887,27 @@ if(!function_exists("detach_s2member_query_filters"))
1887
  * **Parameter $config (array Required).** This should be an array with one or more of the following elements.
1888
  *
1889
  * o ``"file_download" => "file.zip"`` Location of the file, relative to the `/s2member-files/` directory; or, relative to the root of your Amazon S3 Bucket, when applicable.
1890
- * o ``"file_download_key" => false`` Defaults to `false`. If `true`, s2Member will return a URL with an s2Member-generated File Download Key. You don't need to generate the File Download Key yourself, s2Member does it for you. If you set this to `ip-forever`, the File Download Key that s2Member generates will last forever, for a specific IP Address; otherwise, by default, all File Download Keys expire after 24 hours automatically. If you set this to `universal`, s2Member will generate a File Download Key that is good for anyone/everyone forever, with NO restrictions on who/where/when a file is accessed *(e.g. be careful with this one)*.
1891
- * o ``"file_stream" => false`` Defaults to `false`. If `true`, s2Member will return a URL containing a parameter/directive, which forces the File Download to take place over the RTMP protocol. This ONLY works when/if s2Member is configured to run with both Amazon S3/CloudFront. Please note however, it's better to use the example code provided in the your Dashboard. See: `s2Member -› Download Options -› JW Player and the RTMP Protocol`. Also note, if ``$get_streamer_array`` is passed, s2Member will automatically force ``"file_stream" => true`` for you.
1892
- * o ``"file_inline" => null`` Defaults to `null`. If `true`, s2Member will serve the file inline, instead of as an actual File Download. If empty, s2Member will look at your Inline File Extensions configuration, and serve the file inline; if, and only if, its extension matches one found in your configuration. By default, s2Member serves all files as attachments *(i.e. downloads)*. Please check your Dashboard regarding Inline File Extensions. Also note, this Shortcode Attribute does NOTHING for files served via Amazon CloudFront. See the tech-notes listed in the Amazon CloudFront section of your Dashboard for further details and workarounds.
1893
  * o ``"file_storage" => null`` Defaults to `null`. Can be one of `local|s3|cf`. When specified, s2Member will serve the file from a specific source location. For example, if you've configured Amazon S3 and/or CloudFront; but, there are a few files that you want to upload locally to the `/s2member-files/` directory; you can force s2Member to serve a file from local storage by setting ``"file_storage" => "local"`` explicitly.
1894
  * o ``"file_remote" => false`` Defaults to `false`. If `true`, s2Member will authenticate access to the File Download via Remote Header Authorization, instead of through your web site. This is similar to `.htaccess` protection routines of yester-year. Please check the Remote Authorization and Podcasting section in your Dashboard for further details about how this works.
1895
- * o ``"file_ssl" => null`` Defaults to `null`. If `true`, s2Member will generate a File Download URL with an SSL protocol *( i.e. the URL will start with `https://` or `rtmpe://` )*. If `null`, s2Member will only generate a File Download URL with an SSL protocol, when/if the Post/Page/URL, is also being viewed over SSL. Otherwise, s2Member will use a non-SSL protocol by default.
1896
  * o ``"file_rewrite" => false`` Defaults to `false`. If `true`, s2Member will generate a File Download URL that takes full advantage of s2Member's Advanced Mod Rewrite functionality. If you're running an Apache web server, or another server that supports `mod_rewrite`, we highly recommend turning this on. s2Member's `mod_rewrite` URLs do NOT contain query string parameters, making them more portable/compatible with other software applications and/or plugins for WordPress.
1897
  * o ``"file_rewrite_base" => null`` Defaults to `null`. If set to a URL, starting with `http` or another valid protocol, s2Member will generate a File Download URL that takes full advantage of s2Member's Advanced Mod Rewrite functionality, and it will use the rewrite base URL as a prefix. This could be useful on some WordPress installations that use advanced directory structures. It could also be useful for site owners using virtual directories that point to `/s2member-files/`. Note, if `rewrite_base` is set, s2Member will automatically force ``"rewrite" => true`` for you.
1898
  * o ``"skip_confirmation" => false`` Defaults to `false`. If `true`, s2Member will generate a File Download URL which contains a directive, telling s2Member NOT to introduce any JavaScript confirmation prompts on your site, for this File Download URL. Please note, s2Member will automatically detect links, anywhere in your content, and/or anywhere in your theme files, that contain `s2member_file_download` or `s2member-files`. Whenever a logged-in Member clicks a link that contains `s2member_file_download` or `s2member-files`, the system will politely ask the User to confirm the download using a very intuitive JavaScript confirmation prompt, which contains specific details about your configured download limitations. This way your Members will be aware of how many files they've downloaded in the current period; and they'll be able to make a conscious decision about whether to proceed with a specific download or not.
1899
- * o ``"url_to_storage_source" => false`` Defaults to `false`. If `true`, s2Member will generate a File Download URL which points directly to the storage source. This is only functional with Amazon S3 and/or CloudFront integrations. If you create a URL that points directly to the storage source *(i.e. points directly to Amazon S3 or CloudFront)*, s2Member will NOT be able to further authenticate the current User/Member; and, s2Member will NOT be able to count the File Download against the current User's account record, because the URL being generated does not pass back through s2Member at all, it points directly to the storage source. For this reason, if you set ``"url_to_storage_source" => true``, you should also set ``"check_user" => true`` and ``"count_against_user" => true``, telling s2Member to authenticate the current User, and if authenticated, count this File Download URL against the current User's account record in real-time *(i.e. as the URL is being generated)*, while it still has a chance to do so. This is useful when you stream files over the RTMP protocol; where an `http://` URL is not feasible. It also helps in situations where a 3rd-party software application will not work as intended, with s2Member's internal redirection to Amazon S3/CloudFront files. Important, when ``"check_user" => true`` and/or ``"count_against_user" => true``, this API Function will return `false` in situations where the current User/Member does NOT have access to the file.
1900
- * o ``"count_against_user" => false`` Defaults to `false`. If `true`, it will automatically force ``"check_user" => true`` as well. In other words, s2Member will authenticate the current User, and if authenticated, count this File Download URL against the current User's account record in real-time *(i.e. as the URL is being generated)*. This is off by default. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, count the File Download against their account record, and serve the File Download. In other words, under normal circumstances, there is no reason to set ``"check_user" => true`` and/or ``"count_against_user" => true`` when generating the URL itself. However, this is a useful config option when ``"url_to_storage_source" => true``. Please note, when ``"check_user" => true`` and/or ``"count_against_user" => true``, this API Function will return `false` in situations where the current User/Member does NOT have access to the file.
1901
  * o ``"check_user => false`` Defaults to `false`. If `true`, s2Member will authenticate the current User before allowing the File Download URL to be generated. This is off by default. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, and serve the File Download to the User/Member. In other words, under normal circumstances, there is no reason to set ``"check_user" => true`` and/or ``"count_against_user" => true`` when generating the URL itself. However, this IS a useful config option when ``"url_to_storage_source" => true``. Please note, when ``"check_user" => true`` and/or ``"count_against_user" => true``, this API Function will return `false` in situations where the current User/Member does NOT have access to the file.
1902
  *
1903
- * **Parameter $get_streamer_array(bool Optional).** Defaults to `false`. If `true`, this API Function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member -› Download Options -› JW Player & RTMP Protocol Examples`. Note, if this is true, s2Member will automatically force ``"url_to_storage_source" => true`` and ``"file_stream" => true``. For that reason, you should carefully review the details and warning above regarding `url_to_storage_source`. If you set ``$get_streamer_array``, you should also set ``"check_user" => true`` and ``"count_against_user" => true``.
1904
  *
1905
  * @package s2Member\API_Functions
1906
  * @since 110926
1907
  *
1908
  * @param array $config Required. This is an array of configuration options associated with permissions being checked against the current User/Member; and also the actual URL generated by this routine.
1909
  * Possible ``$config`` array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `check_user`.
1910
- * @param bool $get_streamer_array Optional. Defaults to `false`. If `true`, this API Function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member -› Download Options -› JW Player & RTMP Protocol Examples`. Note, if this is true, s2Member will automatically force ``"url_to_storage_source" => true`` and ``"file_stream" => true``. For that reason, you should carefully review the details and warning above regarding `url_to_storage_source`. If you set ``$get_streamer_array``, you should also set ``"check_user" => true`` and ``"count_against_user" => true``.
1911
  * @return string A File Download URL string on success; or an array on success, with elements `streamer`, `file`, `url` when/if ``$get_streamer_array`` is true; else false on any type of failure.
1912
  *
1913
  * @see s2Member\API_Functions\s2member_file_download_key()
@@ -1923,7 +1923,7 @@ if(!function_exists("s2member_file_download_url"))
1923
  * Generates a File Download Key that provides access to a File protected by s2Member.
1924
  *
1925
  * By default, s2Member uses your Basic Download Restrictions. For more information on this,
1926
- * please check your Dashboard under: `s2Member -› Download Options -› Basic Download Restrictions`.
1927
  *
1928
  * ———— Advanced Download Restrictions ————
1929
  *
@@ -1955,8 +1955,8 @@ if(!function_exists("s2member_file_download_url"))
1955
  * @since 3.5
1956
  *
1957
  * @param string $file Location of the protected File, relative to the `/s2member-files/` directory.
1958
- * @param str|bool $directive Optional. Defaults to false. If you set this to any non-zero value ( i.e. the string `universal` ),
1959
- * the resulting Key will be universal *(i.e. valid for any User, at any time, from any browser)*. That is to say; universal, for this particular File.
1960
  * It is also possible to pass in the ``$directive`` string `ip-forever`, making the Key last forever, but only for a specific IP address.
1961
  * @return string The File Download Key. Which is an MD5 hash *(always 32 characters)*, URL-safe.
1962
  *
@@ -2255,11 +2255,11 @@ if(!function_exists("s2member_paid_registration_time"))
2255
  * @param array $access_caps Optional. If not passed, this returns all times for all caps.
2256
  * If passed, please pass an array of specific access capabilities to get the times for.
2257
  * If removal times are desired, you should add a `-` prefix.
2258
- * e.g. `array('ccap_music','level2','-ccap_video')`
2259
  *
2260
  * @return array An array of all access capability times.
2261
  * Keys are UTC timestamps (w/ microtime precision), values are the capabilities (including `-` prefixed removals).
2262
- * e.g. `array('1234567890.0001' => 'ccap_music', '1234567890.0002' => 'level2', '1234567890.0003' => '-ccap_video')`
2263
  */
2264
  if(!function_exists("s2member_access_cap_times") && !function_exists("s2member_capability_times"))
2265
  {
@@ -2322,10 +2322,10 @@ if(!function_exists("s2member_access_cap_times") && !function_exists("s2member_c
2322
  * $s2member_auto_eot_time = get_user_field ("s2member_auto_eot_time"); # Auto EOT-Time for the current User (when applicable).
2323
  * $s2member_last_payment_time = get_user_field ("s2member_last_payment_time"); # Timestamp. Last time an actual payment was received by s2Member.
2324
  * $s2member_paid_registration_times = get_user_field ("s2member_paid_registration_times"); # Timestamps. Associative array of all Paid Registration Times.
2325
- * $s2member_access_role = get_user_field ("s2member_access_role"); # A WordPress Role ID (i.e. s2member_level[0-9]+, administrator, editor, author, contributor, subscriber).
2326
  * $s2member_access_level = get_user_field ("s2member_access_level"); # An s2Member Membership Access Level number.
2327
- * $s2member_access_label = get_user_field ("s2member_access_label"); # An s2Member Membership Access Label (i.e. Bronze, Gold, Silver, Platinum, or whatever is configured).
2328
- * $s2member_access_ccaps = get_user_field ("s2member_access_ccaps"); # An array of Custom Capabilities the current User has (i.e. music,videos).
2329
  * $s2member_login_counter = get_user_field ("s2member_login_counter"); # Number of times the User has logged into your site.
2330
  * !>
2331
  * ```
@@ -2336,9 +2336,9 @@ if(!function_exists("s2member_access_cap_times") && !function_exists("s2member_c
2336
  * [s2Get user_field="s2member_subscr_or_wp_id" /] # Paid Subscr. ID, else WordPress User ID.
2337
  * [s2Get user_field="s2member_subscr_gateway" /] # Paid Subscr. Gateway Code for the current User.
2338
  * [s2Get user_field="s2member_registration_ip" /] # IP Address the current User had during registration.
2339
- * [s2Get user_field="s2member_access_role" /] # A WordPress Role ID (i.e. s2member_level[0-9]+, administrator, editor, author, contributor, subscriber).
2340
  * [s2Get user_field="s2member_access_level" /] # An s2Member Membership Access Level number.
2341
- * [s2Get user_field="s2member_access_label" /] # An s2Member Membership Access Label (i.e. Bronze, Gold, Silver, Platinum, or whatever is configured).
2342
  * [s2Get user_field="s2member_login_counter" /] # Number of times the User has logged into your site.
2343
  * ```
2344
  * ———— Pulling Data From Your Own Custom Fields ————
@@ -2421,7 +2421,7 @@ if(!function_exists("s2member_access_cap_times") && !function_exists("s2member_c
2421
  *
2422
  * @param string $field_id Required. A unique Custom Registration/Profile Field ID, that you configured with s2Member.
2423
  * Or, this could be set to any property that exists on the WP_User object for a particular User;
2424
- * ( i.e. `id`, `ID`, `user_login`, `user_email`, `first_name`, `last_name`, `display_name`, `ip`, `IP`,
2425
  * `s2member_registration_ip`, `s2member_custom`, `s2member_subscr_id`, `s2member_subscr_or_wp_id`,
2426
  * `s2member_subscr_gateway`, `s2member_custom_fields`, `s2member_file_download_access_[log|arc]`,
2427
  * `s2member_auto_eot_time`, `s2member_last_payment_time`, `s2member_paid_registration_times`,
108
  * @since 110524RC
109
  *
110
  * @param int|string $id A numeric WordPress User ID.
111
+ * @param string $role A WordPress Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
112
  * @return bool True if the specific User is/has the specified Role, else false.
113
  *
114
  * @see s2Member\API_Functions\user_is()
193
  * @since 110524RC
194
  *
195
  * @param int|string $id A numeric WordPress User ID.
196
+ * @param string $role A WordPress Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
197
  * @return bool True if the specific User is/does NOT have the specified Role, else false.
198
  *
199
  * @see s2Member\API_Functions\user_is()
270
  * @package s2Member\API_Functions
271
  * @since 3.5
272
  *
273
+ * @param string $role A WordPress Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
274
  * @return bool True if the current User is/has the specified Role, else false.
275
  *
276
  * @see s2Member\API_Functions\user_is()
354
  * @package s2Member\API_Functions
355
  * @since 3.5
356
  *
357
+ * @param string $role A WordPress Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
358
  * @return bool True if the current User is/does NOT have the specified Role, else false.
359
  *
360
  * @see s2Member\API_Functions\user_is()
437
  * @since 3.5
438
  *
439
  * @param int|string $blog_id A WordPress Blog ID *(must be numeric)*.
440
+ * @param string $role A WordPress Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
441
  * @return bool True if the current User is/has the specified Role, on the specified Blog, else false.
442
  *
443
  * @see s2Member\API_Functions\user_is()
516
  * @since 3.5
517
  *
518
  * @param int|string $blog_id A WordPress Blog ID *(must be numeric)*.
519
+ * @param string $role A WordPress Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
520
  * @return bool True if the current User is/does NOT have the specified Role, on the specified Blog, else false.
521
  *
522
  * @see s2Member\API_Functions\user_is()
585
  * @since 3.5
586
  *
587
  * @param int|string $id A numeric WordPress User ID.
588
+ * @param string $capability A WordPress Capability ID *( i.e., `access_s2member_level[0-9]+`, `access_s2member_ccap_music` )*.
589
  * @return bool True if the specific User does NOT have the specified Capability or Role, else false.
590
  *
591
  * @see s2Member\API_Functions\user_is()
653
  * @package s2Member\API_Functions
654
  * @since 3.5
655
  *
656
+ * @param string $capability A WordPress Capability ID *( i.e., `access_s2member_level[0-9]+`, `access_s2member_ccap_music` )*.
657
+ * Or a Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
658
  * @return bool True if the current User does NOT have the specified Capability or Role, else false.
659
  *
660
  * @see s2Member\API_Functions\user_is()
723
  * @since 3.5
724
  *
725
  * @param int|string $blog_id A WordPress Blog ID *(must be numeric)*.
726
+ * @param string $capability A WordPress Capability ID *( i.e., `access_s2member_level[0-9]+`, `access_s2member_ccap_music` )*.
727
+ * Or a Role ID *( i.e., `s2member_level[0-9]+`, `administrator`, `editor`, `author`, `contributor`, `subscriber` )*.
728
  * @return bool True if the current User does NOT have the specified Capability or Role, else false.
729
  *
730
  * @see s2Member\API_Functions\user_is()
771
  * o Note: when passing in a URL or URI, the $type parameter must be set to `URI` or `uri`. Case insensitive.
772
  *
773
  * **Parameter $type (str Optional).**
774
+ * One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e., a Post or Page)*.
775
  *
776
  * **Parameter $check_user (bool Optional).**
777
  * Consider the current User? Defaults to false.
824
  *
825
  * @param int|string $what Optional. Defaults to the current $post ID when called from within {@link http://codex.wordpress.org/The_Loop The Loop}.
826
  * If passed in, this should be a WordPress Category ID, Tag ID, Post ID, or Page ID. Or a full URL. A URI is also fine.
827
+ * @param string $type Optional. One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e., a Post or Page)*.
828
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
829
+ * @return array|bool A non-empty array *(meaning true)*, or false if the content is not protected *(i.e., available publicly)*.
830
  * When/if content IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
831
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the content.
832
  * In other words, the reason why it's protected; based on your s2Member configuration.
903
  * o Note: when passing in a URL or URI, the $type parameter must be set to `URI` or `uri`. Case insensitive.
904
  *
905
  * **Parameter $type (str Optional).**
906
+ * One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e., a Post or Page)*.
907
  *
908
  * ———— Code Sample Using Function Parameters ————
909
  * ```
949
  *
950
  * @param int|string $what Optional. Defaults to the current $post ID when called from within {@link http://codex.wordpress.org/The_Loop The Loop}.
951
  * If passed in, this should be a WordPress Category ID, Tag ID, Post ID, or Page ID. Or a full URL. A URI is also fine.
952
+ * @param string $type Optional. One of `category`, `tag`, `post`, `page`, `singular` or `uri`. Defaults to `singular` *(i.e., a Post or Page)*.
953
  * @return bool True if the current User IS permitted, else false if the content is NOT available to the current User;
954
  * based on your configuration of s2Member, and based on the current User's Role/Capabilities.
955
  *
1040
  *
1041
  * @param int $cat_id Required. This should be a WordPress Category ID.
1042
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1043
+ * @return array|bool A non-empty array *(meaning true)*, or false if the Category is not protected *(i.e., available publicly)*.
1044
  * When/if the Category IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1045
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Category.
1046
  * In other words, the reason why it's protected; based on your s2Member configuration.
1196
  *
1197
  * @param int|string $tag_id_slug_or_name Required. This should be a WordPress Tag ID, Tag Slug, or Tag Name.
1198
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1199
+ * @return array|bool A non-empty array *(meaning true)*, or false if the Tag is not protected *(i.e., available publicly)*.
1200
  * When/if the Tag IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1201
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Tag.
1202
  * In other words, the reason why it's protected; based on your s2Member configuration.
1352
  *
1353
  * @param int $post_id Required. This should be a WordPress Post ID, or a Custom Post Type ID.
1354
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1355
+ * @return array|bool A non-empty array *(meaning true)*, or false if the Post is not protected *(i.e., available publicly)*.
1356
  * When/if the Post IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1357
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Post.
1358
  * In other words, the reason why it's protected; based on your s2Member configuration.
1495
  *
1496
  * @param int $page_id Required. This should be a WordPress Page ID.
1497
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1498
+ * @return array|bool A non-empty array *(meaning true)*, or false if the Page is not protected *(i.e., available publicly)*.
1499
  * When/if the Page IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1500
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the Page.
1501
  * In other words, the reason why it's protected; based on your s2Member configuration.
1645
  *
1646
  * @param string $uri_or_full_url Required. This should be a URI starting with `/`, or a full URL is also fine.
1647
  * @param bool $check_user Optional. Consider the current User? Defaults to false.
1648
+ * @return array|bool A non-empty array *(meaning true)*, or false if the URI or URL is not protected *(i.e., available publicly)*.
1649
  * When/if the URI or URL IS protected, the return array will include one of these keys ``["s2member_(level|sp|ccap)_req"]``
1650
  * indicating the Level #, Specific Post/Page ID #, or Custom Capability required to access the URI or URL.
1651
  * In other words, the reason why it's protected; based on your s2Member configuration.
1852
  * Generates a File Download URL that provides access to a File protected by s2Member.
1853
  *
1854
  * By default, s2Member uses your Basic Download Restrictions. For more information on this,
1855
+ * please check your Dashboard under: `s2Member Download Options Basic Download Restrictions`.
1856
  *
1857
  * ———— HTML/PHP Code Samples ————
1858
  * ```
1867
  *
1868
  * ———— Advanced Download Restrictions ————
1869
  *
1870
+ * Or, you can also force s2Member to allow File Downloads, by requesting a File Download Key ( i.e., `file_download_key => true` ).
1871
+ * When a File Download Key is requested through this parameter ( i.e., `file_download_key => true` ); it tells s2Member to allow the download of this particular file,
1872
  * regardless of Membership Level; and WITHOUT checking any Basic Restrictions, that you may, or may not have configured.
1873
  *
1874
  * ———— HTML/PHP Code Samples Using A Download Key ————
1887
  * **Parameter $config (array Required).** This should be an array with one or more of the following elements.
1888
  *
1889
  * o ``"file_download" => "file.zip"`` Location of the file, relative to the `/s2member-files/` directory; or, relative to the root of your Amazon S3 Bucket, when applicable.
1890
+ * o ``"file_download_key" => false`` Defaults to `false`. If `true`, s2Member will return a URL with an s2Member-generated File Download Key. You don't need to generate the File Download Key yourself, s2Member does it for you. If you set this to `ip-forever`, the File Download Key that s2Member generates will last forever, for a specific IP Address; otherwise, by default, all File Download Keys expire after 24 hours automatically. If you set this to `universal`, s2Member will generate a File Download Key that is good for anyone/everyone forever, with NO restrictions on who/where/when a file is accessed *(e.g., be careful with this one)*.
1891
+ * o ``"file_stream" => false`` Defaults to `false`. If `true`, s2Member will return a URL containing a parameter/directive, which forces the File Download to take place over the RTMP protocol. This ONLY works when/if s2Member is configured to run with both Amazon S3/CloudFront. Please note however, it's better to use the example code provided in the your Dashboard. See: `s2Member Download Options JW Player and the RTMP Protocol`. Also note, if ``$get_streamer_array`` is passed, s2Member will automatically force ``"file_stream" => true`` for you.
1892
+ * o ``"file_inline" => null`` Defaults to `null`. If `true`, s2Member will serve the file inline, instead of as an actual File Download. If empty, s2Member will look at your Inline File Extensions configuration, and serve the file inline; if, and only if, its extension matches one found in your configuration. By default, s2Member serves all files as attachments *(i.e., downloads)*. Please check your Dashboard regarding Inline File Extensions. Also note, this Shortcode Attribute does NOTHING for files served via Amazon CloudFront. See the tech-notes listed in the Amazon CloudFront section of your Dashboard for further details and workarounds.
1893
  * o ``"file_storage" => null`` Defaults to `null`. Can be one of `local|s3|cf`. When specified, s2Member will serve the file from a specific source location. For example, if you've configured Amazon S3 and/or CloudFront; but, there are a few files that you want to upload locally to the `/s2member-files/` directory; you can force s2Member to serve a file from local storage by setting ``"file_storage" => "local"`` explicitly.
1894
  * o ``"file_remote" => false`` Defaults to `false`. If `true`, s2Member will authenticate access to the File Download via Remote Header Authorization, instead of through your web site. This is similar to `.htaccess` protection routines of yester-year. Please check the Remote Authorization and Podcasting section in your Dashboard for further details about how this works.
1895
+ * o ``"file_ssl" => null`` Defaults to `null`. If `true`, s2Member will generate a File Download URL with an SSL protocol *( i.e., the URL will start with `https://` or `rtmpe://` )*. If `null`, s2Member will only generate a File Download URL with an SSL protocol, when/if the Post/Page/URL, is also being viewed over SSL. Otherwise, s2Member will use a non-SSL protocol by default.
1896
  * o ``"file_rewrite" => false`` Defaults to `false`. If `true`, s2Member will generate a File Download URL that takes full advantage of s2Member's Advanced Mod Rewrite functionality. If you're running an Apache web server, or another server that supports `mod_rewrite`, we highly recommend turning this on. s2Member's `mod_rewrite` URLs do NOT contain query string parameters, making them more portable/compatible with other software applications and/or plugins for WordPress.
1897
  * o ``"file_rewrite_base" => null`` Defaults to `null`. If set to a URL, starting with `http` or another valid protocol, s2Member will generate a File Download URL that takes full advantage of s2Member's Advanced Mod Rewrite functionality, and it will use the rewrite base URL as a prefix. This could be useful on some WordPress installations that use advanced directory structures. It could also be useful for site owners using virtual directories that point to `/s2member-files/`. Note, if `rewrite_base` is set, s2Member will automatically force ``"rewrite" => true`` for you.
1898
  * o ``"skip_confirmation" => false`` Defaults to `false`. If `true`, s2Member will generate a File Download URL which contains a directive, telling s2Member NOT to introduce any JavaScript confirmation prompts on your site, for this File Download URL. Please note, s2Member will automatically detect links, anywhere in your content, and/or anywhere in your theme files, that contain `s2member_file_download` or `s2member-files`. Whenever a logged-in Member clicks a link that contains `s2member_file_download` or `s2member-files`, the system will politely ask the User to confirm the download using a very intuitive JavaScript confirmation prompt, which contains specific details about your configured download limitations. This way your Members will be aware of how many files they've downloaded in the current period; and they'll be able to make a conscious decision about whether to proceed with a specific download or not.
1899
+ * o ``"url_to_storage_source" => false`` Defaults to `false`. If `true`, s2Member will generate a File Download URL which points directly to the storage source. This is only functional with Amazon S3 and/or CloudFront integrations. If you create a URL that points directly to the storage source *(i.e., points directly to Amazon S3 or CloudFront)*, s2Member will NOT be able to further authenticate the current User/Member; and, s2Member will NOT be able to count the File Download against the current User's account record, because the URL being generated does not pass back through s2Member at all, it points directly to the storage source. For this reason, if you set ``"url_to_storage_source" => true``, you should also set ``"check_user" => true`` and ``"count_against_user" => true``, telling s2Member to authenticate the current User, and if authenticated, count this File Download URL against the current User's account record in real-time *(i.e., as the URL is being generated)*, while it still has a chance to do so. This is useful when you stream files over the RTMP protocol; where an `http://` URL is not feasible. It also helps in situations where a 3rd-party software application will not work as intended, with s2Member's internal redirection to Amazon S3/CloudFront files. Important, when ``"check_user" => true`` and/or ``"count_against_user" => true``, this API Function will return `false` in situations where the current User/Member does NOT have access to the file.
1900
+ * o ``"count_against_user" => false`` Defaults to `false`. If `true`, it will automatically force ``"check_user" => true`` as well. In other words, s2Member will authenticate the current User, and if authenticated, count this File Download URL against the current User's account record in real-time *(i.e., as the URL is being generated)*. This is off by default. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, count the File Download against their account record, and serve the File Download. In other words, under normal circumstances, there is no reason to set ``"check_user" => true`` and/or ``"count_against_user" => true`` when generating the URL itself. However, this is a useful config option when ``"url_to_storage_source" => true``. Please note, when ``"check_user" => true`` and/or ``"count_against_user" => true``, this API Function will return `false` in situations where the current User/Member does NOT have access to the file.
1901
  * o ``"check_user => false`` Defaults to `false`. If `true`, s2Member will authenticate the current User before allowing the File Download URL to be generated. This is off by default. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, and serve the File Download to the User/Member. In other words, under normal circumstances, there is no reason to set ``"check_user" => true`` and/or ``"count_against_user" => true`` when generating the URL itself. However, this IS a useful config option when ``"url_to_storage_source" => true``. Please note, when ``"check_user" => true`` and/or ``"count_against_user" => true``, this API Function will return `false` in situations where the current User/Member does NOT have access to the file.
1902
  *
1903
+ * **Parameter $get_streamer_array(bool Optional).** Defaults to `false`. If `true`, this API Function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member Download Options JW Player & RTMP Protocol Examples`. Note, if this is true, s2Member will automatically force ``"url_to_storage_source" => true`` and ``"file_stream" => true``. For that reason, you should carefully review the details and warning above regarding `url_to_storage_source`. If you set ``$get_streamer_array``, you should also set ``"check_user" => true`` and ``"count_against_user" => true``.
1904
  *
1905
  * @package s2Member\API_Functions
1906
  * @since 110926
1907
  *
1908
  * @param array $config Required. This is an array of configuration options associated with permissions being checked against the current User/Member; and also the actual URL generated by this routine.
1909
  * Possible ``$config`` array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `check_user`.
1910
+ * @param bool $get_streamer_array Optional. Defaults to `false`. If `true`, this API Function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member Download Options JW Player & RTMP Protocol Examples`. Note, if this is true, s2Member will automatically force ``"url_to_storage_source" => true`` and ``"file_stream" => true``. For that reason, you should carefully review the details and warning above regarding `url_to_storage_source`. If you set ``$get_streamer_array``, you should also set ``"check_user" => true`` and ``"count_against_user" => true``.
1911
  * @return string A File Download URL string on success; or an array on success, with elements `streamer`, `file`, `url` when/if ``$get_streamer_array`` is true; else false on any type of failure.
1912
  *
1913
  * @see s2Member\API_Functions\s2member_file_download_key()
1923
  * Generates a File Download Key that provides access to a File protected by s2Member.
1924
  *
1925
  * By default, s2Member uses your Basic Download Restrictions. For more information on this,
1926
+ * please check your Dashboard under: `s2Member Download Options Basic Download Restrictions`.
1927
  *
1928
  * ———— Advanced Download Restrictions ————
1929
  *
1955
  * @since 3.5
1956
  *
1957
  * @param string $file Location of the protected File, relative to the `/s2member-files/` directory.
1958
+ * @param str|bool $directive Optional. Defaults to false. If you set this to any non-zero value ( i.e., the string `universal` ),
1959
+ * the resulting Key will be universal *(i.e., valid for any User, at any time, from any browser)*. That is to say; universal, for this particular File.
1960
  * It is also possible to pass in the ``$directive`` string `ip-forever`, making the Key last forever, but only for a specific IP address.
1961
  * @return string The File Download Key. Which is an MD5 hash *(always 32 characters)*, URL-safe.
1962
  *
2255
  * @param array $access_caps Optional. If not passed, this returns all times for all caps.
2256
  * If passed, please pass an array of specific access capabilities to get the times for.
2257
  * If removal times are desired, you should add a `-` prefix.
2258
+ * e.g., `array('ccap_music','level2','-ccap_video')`
2259
  *
2260
  * @return array An array of all access capability times.
2261
  * Keys are UTC timestamps (w/ microtime precision), values are the capabilities (including `-` prefixed removals).
2262
+ * e.g., `array('1234567890.0001' => 'ccap_music', '1234567890.0002' => 'level2', '1234567890.0003' => '-ccap_video')`
2263
  */
2264
  if(!function_exists("s2member_access_cap_times") && !function_exists("s2member_capability_times"))
2265
  {
2322
  * $s2member_auto_eot_time = get_user_field ("s2member_auto_eot_time"); # Auto EOT-Time for the current User (when applicable).
2323
  * $s2member_last_payment_time = get_user_field ("s2member_last_payment_time"); # Timestamp. Last time an actual payment was received by s2Member.
2324
  * $s2member_paid_registration_times = get_user_field ("s2member_paid_registration_times"); # Timestamps. Associative array of all Paid Registration Times.
2325
+ * $s2member_access_role = get_user_field ("s2member_access_role"); # A WordPress Role ID (i.e., s2member_level[0-9]+, administrator, editor, author, contributor, subscriber).
2326
  * $s2member_access_level = get_user_field ("s2member_access_level"); # An s2Member Membership Access Level number.
2327
+ * $s2member_access_label = get_user_field ("s2member_access_label"); # An s2Member Membership Access Label (i.e., Bronze, Gold, Silver, Platinum, or whatever is configured).
2328
+ * $s2member_access_ccaps = get_user_field ("s2member_access_ccaps"); # An array of Custom Capabilities the current User has (i.e., music,videos).
2329
  * $s2member_login_counter = get_user_field ("s2member_login_counter"); # Number of times the User has logged into your site.
2330
  * !>
2331
  * ```
2336
  * [s2Get user_field="s2member_subscr_or_wp_id" /] # Paid Subscr. ID, else WordPress User ID.
2337
  * [s2Get user_field="s2member_subscr_gateway" /] # Paid Subscr. Gateway Code for the current User.
2338
  * [s2Get user_field="s2member_registration_ip" /] # IP Address the current User had during registration.
2339
+ * [s2Get user_field="s2member_access_role" /] # A WordPress Role ID (i.e., s2member_level[0-9]+, administrator, editor, author, contributor, subscriber).
2340
  * [s2Get user_field="s2member_access_level" /] # An s2Member Membership Access Level number.
2341
+ * [s2Get user_field="s2member_access_label" /] # An s2Member Membership Access Label (i.e., Bronze, Gold, Silver, Platinum, or whatever is configured).
2342
  * [s2Get user_field="s2member_login_counter" /] # Number of times the User has logged into your site.
2343
  * ```
2344
  * ———— Pulling Data From Your Own Custom Fields ————
2421
  *
2422
  * @param string $field_id Required. A unique Custom Registration/Profile Field ID, that you configured with s2Member.
2423
  * Or, this could be set to any property that exists on the WP_User object for a particular User;
2424
+ * ( i.e., `id`, `ID`, `user_login`, `user_email`, `first_name`, `last_name`, `display_name`, `ip`, `IP`,
2425
  * `s2member_registration_ip`, `s2member_custom`, `s2member_subscr_id`, `s2member_subscr_or_wp_id`,
2426
  * `s2member_subscr_gateway`, `s2member_custom_fields`, `s2member_file_download_access_[log|arc]`,
2427
  * `s2member_auto_eot_time`, `s2member_last_payment_time`, `s2member_paid_registration_times`,
includes/hooks.inc.php CHANGED
@@ -19,6 +19,8 @@ if(!defined('WPINC')) // MUST have WordPress.
19
  /*
20
  Add the plugin Actions/Filters here.
21
  */
 
 
22
  add_action('init', 'c_ws_plugin__s2member_translations::load', 2);
23
 
24
  add_action('init', 'c_ws_plugin__s2member_ssl::check_force_ssl', 3);
19
  /*
20
  Add the plugin Actions/Filters here.
21
  */
22
+ add_action('set_current_user', 'c_ws_plugin__s2member_user_securities::set_current_user', 2);
23
+
24
  add_action('init', 'c_ws_plugin__s2member_translations::load', 2);
25
 
26
  add_action('init', 'c_ws_plugin__s2member_ssl::check_force_ssl', 3);
includes/menu-pages/api-ops.inc.php CHANGED
@@ -42,7 +42,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
42
  echo '<tr class="ws-menu-page-table-tr">'."\n";
43
  echo '<td class="ws-menu-page-table-l">'."\n";
44
 
45
- echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form">'."\n";
46
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
  echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
48
 
@@ -56,8 +56,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
56
 
57
  echo '<div class="ws-menu-page-section ws-plugin--s2member-signup-notifications-section">'."\n";
58
  echo '<h3>Signup Notification URLs (optional)</h3>'."\n";
59
- echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever a new Subscription is created, you\'ll want to read this section. This is marked `Signup`, because the URLs that you list below, will be notified each time a "new", "paying" Member signs up. Depending on your fee structure, this may include a payment that establishes their Subscription, or it may not. This Notification will only be triggered once for each Member. Signup Notifications are sent right after a "new", "paying" Member signs up successfully through your Payment Gateway, regardless of whether any money has actually been transacted. In other words, this Notification is triggered anytime a "new", "paying" Member signs up through your Payment Gateway, even if you provided them with something for free <em>(i.e. even if no money is being transacted)</em>.</p>'."\n";
60
- echo '<p>This Notification will NOT be processed for Free Subscribers that register without going through your Payment Gateway at all (i.e. they simply register on-site; and there is no checkout whatsoever). This Notification will NOT be processed when an "existing" User/Member pays for a new Subscription <em>(see: Modification Notifications for that scenario)</em>.'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' And, this Notification will NOT be processed on Buy Now transactions for Independent Custom Capabilities <em>(see: Payment Notifications for that scenario)</em>.').'</p>'."\n";
61
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Signup Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
62
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
63
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_signup_notifications", get_defined_vars());
@@ -78,14 +78,14 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
78
  echo '<td>'."\n";
79
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
80
  echo '<textarea name="ws_plugin__s2member_signup_notification_urls" id="ws-plugin--s2member-signup-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["signup_notification_urls"]).'</textarea><br />'."\n";
81
- echo 'Signup Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
82
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
83
  echo '<ul class="ws-menu-page-li-margins">'."\n";
84
- echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e. there is only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
85
- echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
86
- echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remains constant throughout any &amp; all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
87
- echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g. <code>USD</code></li>'."\n";
88
- echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g. <code>$</code></li>'."\n";
89
  echo '<li><code>%%initial%%</code> = The Initial Fee charged during signup. If you offered a 100% Free Trial, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This will always represent the amount of money the Customer spent, whenever they initially signed up, no matter what. Even if that amount is 0.\\n\\nIf a Customer signs up, under the terms of a 100% Free Trial Period, this will be 0. So be careful using %%initial%% when you offer a 100% Free Trial Period, because a $0.00 sale amount could cause havoc with affiliate programs.\\n\\nIf you\\\'re offering a 100% Free Trial Period, and you need to track sales through affiliate programs, you can either hard-code an amount; or use `Payment Notifications` instead.\'); return false;">?</a> ]</li>'."\n";
90
  echo '<li><code>%%regular%%</code> = The Regular Amount of the Subscription. If you offer something 100% free, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This is how much the Subscription costs after an Initial Period expires. If you did NOT offer an Initial Period at a different price, %%initial%% and %%regular%% will be equal to the same thing.\'); return false;">?</a> ]</li>'."\n";
91
  echo '<li><code>%%recurring%%</code> = This is the amount that will be charged on a recurring basis, or <code>0</code> if non-recurring. [ <a href="#" onclick="alert(\'If Recurring Payments have not been required, this will be equal to 0. That being said, %%regular%% &amp; %%recurring%% are usually the same value. This variable can be used in two different ways. You can use it to determine what the Regular Recurring Rate is, or to determine whether the Subscription will recur or not. If it is going to recur, %%recurring%% will be > 0.\'); return false;">?</a> ]</li>'."\n";
@@ -102,11 +102,11 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
102
 
103
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
104
  {
105
- echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro Forms):</strong>'."\n";
106
  echo '<ul class="ws-menu-page-li-margins">'."\n";
107
- echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e. the full Coupon Code).</li>'."\n";
108
- echo '<li><code>%%coupon_code%%</code> = A Coupon Code if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
109
- echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e. the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
110
  echo '</ul>'."\n";
111
  }
112
 
@@ -116,7 +116,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
116
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
117
  echo '</ul>'."\n";
118
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
119
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
120
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
121
 
122
  echo '</td>'."\n";
@@ -184,7 +184,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
184
  echo '<td>'."\n";
185
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
186
  echo '<textarea name="ws_plugin__s2member_registration_notification_urls" id="ws-plugin--s2member-registration-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["registration_notification_urls"]).'</textarea><br />'."\n";
187
- echo 'Registration Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
188
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
189
  echo '<ul class="ws-menu-page-li-margins">'."\n";
190
  echo '<li><code>%%role%%</code> = The Role ID <code>(subscriber, s2member_level[0-9]+, administrator, editor, author, contributor)</code>.</li>'."\n";
@@ -206,7 +206,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
206
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
207
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
208
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
209
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
210
  echo '</ul>'."\n";
211
 
212
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
@@ -215,7 +215,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
215
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
216
  echo '</ul>'."\n";
217
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
218
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
219
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
220
 
221
  echo '</td>'."\n";
@@ -264,7 +264,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
264
  echo '<div class="ws-menu-page-section ws-plugin--s2member-payment-notifications-section">'."\n";
265
  echo '<h3>Payment Notification URLs (optional)</h3>'."\n";
266
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever payment transactions <em>(including Recurring Payments)</em> take place, you\'ll want to read this section. This is marked `Payment`, because the URLs that you list below, will be notified each time an actual payment occurs. Depending on your fee structure, this may include a first Initial Payment that establishes a Subscription. But more importantly, this will be triggered on all future payments that are received for the lifetime of the Subscription.</p>'."\n";
267
- echo '<p>So, unlike the `Signup` Notification, `Payment` Notifications take place whenever actual payments are received, instead of just once after signup is completed. If a payment is required during signup <em>(i.e. no Free Trial is being offered)</em>, a Signup Notification will be triggered, and a Payment Notification will ALSO be triggered. In other words, a Payment Notification occurs anytime funds are received, no matter what.</p>'."\n";
268
  echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '<p>Payment Notifications are also triggered whenever a Buy Now purchase for Independent Custom Capabilities takes place.</p>'."\n" : '';
269
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Payment Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
270
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
@@ -286,15 +286,15 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
286
  echo '<td>'."\n";
287
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
288
  echo '<textarea name="ws_plugin__s2member_payment_notification_urls" id="ws-plugin--s2member-payment-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["payment_notification_urls"]).'</textarea><br />'."\n";
289
- echo 'Payment Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
290
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
291
  echo '<ul class="ws-menu-page-li-margins">'."\n";
292
- echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There are some exceptions. If you are selling Lifetime or Fixed-Term (non-recurring) access, and/or Independent Custom Capabilities, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the payment.\\n\\nPayment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e. there is only ONE payment), which goes for Independent Custom Capability purchases too; using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
293
- echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
294
- echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remains constant throughout any &amp; all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
295
  echo '<li><code>%%txn_id%%</code> = The Payment Transaction ID, which is always unique for each payment received.</li>'."\n";
296
- echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g. <code>USD</code></li>'."\n";
297
- echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g. <code>$</code></li>'."\n";
298
  echo '<li><code>%%amount%%</code> = The Amount of the payment. Most affiliate programs calculate commissions from this.</li>'."\n";
299
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased the Membership Subscription'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' or Capabilities.').'</li>'."\n";
300
  echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who purchased the Membership Subscription'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' or Capabilities.').'</li>'."\n";
@@ -313,12 +313,12 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
313
 
314
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
315
  {
316
- echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro Forms):</strong><br />'."\n";
317
  echo '<em>These are ONLY included with payments that occur during checkout. They will NOT be provided with any future recurring payments.</em>'."\n";
318
  echo '<ul class="ws-menu-page-li-margins">'."\n";
319
- echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e. the full Coupon Code).</li>'."\n";
320
- echo '<li><code>%%coupon_code%%</code> = A Coupon Code if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
321
- echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e. the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
322
  echo '</ul>'."\n";
323
  }
324
 
@@ -327,7 +327,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
327
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
328
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
329
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
330
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
331
  echo '</ul>'."\n";
332
 
333
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
@@ -336,7 +336,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
336
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
337
  echo '</ul>'."\n";
338
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
339
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
340
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
341
 
342
  echo '</td>'."\n";
@@ -384,8 +384,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
384
 
385
  echo '<div class="ws-menu-page-section ws-plugin--s2member-modification-notifications-section">'."\n";
386
  echo '<h3>Modification Notification URLs (optional)</h3>'."\n";
387
- echo '<p>If you use affiliate software, or have back-office routines that need to be notified each time a new Subscription is created by an "existing" User/Member, or an "existing" Member modifies their paid Subscription terms, you\'ll want to read this section. This is marked `Modification`, because the URLs that you list below, will be notified each time an "existing" User/Member <em>(even if they are/were a Free Subscriber)</em> signs up for a paid Subscription <em>(i.e. a Modification takes place against an existing account within WordPress)</em>, or an "existing" Member modifies their paid Subscription terms <em>(again, a Modification takes places against an existing account within WordPress)</em>. Depending on your fee structure, this may include a payment that establishes their Subscription, or it may not.</p>'."\n";
388
- echo '<p>Modification Notifications are sent right after a Member signs up and/or modifies billing terms successfully through your Payment Gateway, regardless of whether any money has actually been transacted. In other words, this Notification is triggered, even if you provided them with something for free <em>(i.e. even if no money is being transacted)</em>.</p>'."\n";
389
  echo '<p>This Notification will NOT be processed for "new" Users/Members <em>(see: Signup Notifications for that scenario)</em>.'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' And, this Notification will NOT be processed for Independent Custom Capability purchases <em>(see: Payment Notifications for that scenario)</em>.').'</p>'."\n";
390
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Modification Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
391
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
@@ -407,14 +407,14 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
407
  echo '<td>'."\n";
408
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
409
  echo '<textarea name="ws_plugin__s2member_modification_notification_urls" id="ws-plugin--s2member-modification-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["modification_notification_urls"]).'</textarea><br />'."\n";
410
- echo 'Modification Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
411
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
412
  echo '<ul class="ws-menu-page-li-margins">'."\n";
413
- echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e. there is only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
414
- echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
415
- echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remains constant throughout any &amp; all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
416
- echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g. <code>USD</code></li>'."\n";
417
- echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g. <code>$</code></li>'."\n";
418
  echo '<li><code>%%initial%%</code> = The Initial Fee. If you offered a 100% Free Trial, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This will always represent the amount of money the Customer spent when they completed checkout, no matter what. Even if that amount is 0.\\n\\nIf a Customer upgrades/downgrades under the terms of a 100% Free Trial Period, this will be 0. So, please be careful using %%initial%% when you offer a 100% Free Trial Period, because a $0.00 sale amount could cause havoc with affiliate programs.\\n\\nIf you\\\'re offering a 100% Free Trial Period, and you need to track sales through affiliate programs, you can either hard-code an amount; or use `Payment Notifications` instead.\'); return false;">?</a> ]</li>'."\n";
419
  echo '<li><code>%%regular%%</code> = The Regular Amount of the Subscription. If you offer something 100% free, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This is how much the Subscription costs after an Initial Period expires. If you did NOT offer an Initial Period at a different price, %%initial%% and %%regular%% will be equal to the same thing.\'); return false;">?</a> ]</li>'."\n";
420
  echo '<li><code>%%recurring%%</code> = This is the amount that will be charged on a recurring basis, or <code>0</code> if non-recurring. [ <a href="#" onclick="alert(\'If Recurring Payments have not been required, this will be equal to 0. That being said, %%regular%% &amp; %%recurring%% are usually the same value. This variable can be used in two different ways. You can use it to determine what the Regular Recurring Rate is, or to determine whether the Subscription will recur or not. If it is going to recur, %%recurring%% will be > 0.\'); return false;">?</a> ]</li>'."\n";
@@ -437,11 +437,11 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
437
 
438
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
439
  {
440
- echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro Forms):</strong>'."\n";
441
  echo '<ul class="ws-menu-page-li-margins">'."\n";
442
- echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e. the full Coupon Code).</li>'."\n";
443
- echo '<li><code>%%coupon_code%%</code> = A Coupon Code if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
444
- echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e. the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
445
  echo '</ul>'."\n";
446
  }
447
 
@@ -450,7 +450,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
450
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
451
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
452
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
453
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
454
  echo '</ul>'."\n";
455
 
456
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
@@ -459,7 +459,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
459
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
460
  echo '</ul>'."\n";
461
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
462
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
463
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
464
 
465
  echo '</td>'."\n";
@@ -507,8 +507,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
507
  echo '<h3>Cancellation Notification URLs (optional)</h3>'."\n";
508
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Subscriptions are cancelled through your Payment Gateway, you\'ll want to read this section. This is marked `Cancellation`, because the URLs that you list below, will be notified each time a Subscription is cancelled. A Cancellation is triggered when you cancel a Customer\'s Subscription through your Payment Gateway, or when a Customer cancels their own Subscription.</p>'."\n";
509
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Cancellation Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
510
- echo '<p><em><strong>*Understanding Cancellations*</strong> It\'s important to realize that a Cancellation is not an EOT (End Of Term). All that happens during a Cancellation event, is that billing is stopped, and it\'s understood that the Customer is going to lose access, at some point in the future. This does NOT mean, that access will be revoked immediately. A separate EOT event will automatically handle a (demotion or deletion) later, at the appropriate time; which could be several days, or even a year after the Cancellation took place.</em></p>'."\n";
511
- echo '<p><em><strong>*Some Hairy Details*</strong> There might be times whenever you notice that a Member\'s Subscription has been cancelled through your Payment Gateway... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only remove the User\'s Membership privileges when an EOT (End Of Term) is processed, a refund occurs, a chargeback occurs, or when a cancellation occurs - which would later result in a delayed Auto-EOT by s2Member.</em></p>'."\n";
512
  echo '<p><em>s2Member will not process an EOT (End Of Term) until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will remove their Membership privileges; by either demoting them to a Free Subscriber, or deleting their account from the system (based on your configuration). s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.</em></p>'."\n";
513
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
514
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_cancellation_notifications", get_defined_vars());
@@ -529,12 +529,12 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
529
  echo '<td>'."\n";
530
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
531
  echo '<textarea name="ws_plugin__s2member_cancellation_notification_urls" id="ws-plugin--s2member-cancellation-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["cancellation_notification_urls"]).'</textarea><br />'."\n";
532
- echo 'Cancellation Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
533
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
534
  echo '<ul class="ws-menu-page-li-margins">'."\n";
535
- echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the original Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e. there was only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
536
- echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
537
- echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remained constant throughout the lifetime of the Membership. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
538
  echo '<li><code>%%item_number%%</code> = The Item Number (colon separated <em>level:custom_capabilities:fixed term</em>) that the Subscription was for.</li>'."\n";
539
  echo '<li><code>%%item_name%%</code> = The Item Name (as provided by the <code>desc=""</code> attribute in your Shortcode, which briefly describes the Item Number).</li>'."\n";
540
  echo '<li><code>%%user_first_name%%</code> = The First Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
@@ -551,7 +551,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
551
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
552
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
553
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
554
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
555
  echo '</ul>'."\n";
556
 
557
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
@@ -560,7 +560,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
560
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
561
  echo '</ul>'."\n";
562
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
563
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
564
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
565
 
566
  echo '</td>'."\n";
@@ -609,10 +609,10 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
609
  echo '<div class="ws-menu-page-section ws-plugin--s2member-eot-deletion-notifications-section">'."\n";
610
  echo '<h3>EOT/Deletion Notification URLs (optional)</h3>'."\n";
611
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Subscriptions have ended <em>(and a Member is demoted to a Free Subscriber)</em>, or when an account is deleted from the system, you\'ll want to read this section. This is marked `EOT/Deletion`, because the URLs that you list below, will be notified in both cases.</p>'."\n";
612
- echo '<p><strong>EOT = End Of Term.</strong> An EOT is triggered <em>immediately</em> when you refund a Customer, when a Customer forces a chargeback to occur, or when a paid Subscription ends naturally <em>(i.e. expires)</em>, and your Payment Gateway sends s2Member an EOT (End Of Term) response. Delayed EOTs occur after a Cancellation, either as a result of you cancelling a Customer\'s Subscription, or a Customer cancelling their own Subscription. A Cancellation usually results in a delayed EOT, because a Cancellation does NOT always warrant an immediate action; there could still be time left on their Subscription.</p>'."\n";
613
  echo '<p>Manual Deletions will trigger this Notification too. If you delete an account manually from within your WordPress Dashboard, your URLs can be notified automatically through this API Notification, and this scenario can be detected through the Replacement Code <code>%%eot_del_type%%</code>, which is documented below. So the two events are an EOT <em>(of any kind)</em> and/or a Manual Deletion.</p>'."\n";
614
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These EOT/Deletion Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
615
- echo '<p><em><strong>*Some Hairy Details*</strong> There might be times whenever you notice that a Member\'s Subscription has been cancelled through your Payment Gateway... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only remove the User\'s Membership privileges when an EOT (End Of Term) is processed, a refund occurs, a chargeback occurs, or when a cancellation occurs - which would later result in a delayed Auto-EOT by s2Member.</em></p>'."\n";
616
  echo '<p><em>s2Member will not process an EOT (End Of Term) until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will remove their Membership privileges; by either demoting them to a Free Subscriber, or deleting their account from the system (based on your configuration). s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.</em></p>'."\n";
617
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
618
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_eot_deletion_notifications", get_defined_vars());
@@ -633,13 +633,13 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
633
  echo '<td>'."\n";
634
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
635
  echo '<textarea name="ws_plugin__s2member_eot_del_notification_urls" id="ws-plugin--s2member-eot-del-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["eot_del_notification_urls"]).'</textarea><br />'."\n";
636
- echo 'EOT/Deletion Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
637
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
638
  echo '<ul class="ws-menu-page-li-margins">'."\n";
639
- echo '<li><code>%%eot_del_type%%</code> = The type of event that triggered this Notification. [ <a href="#" onclick="alert(\'List of possible values:\\n\\nuser-removal-deletion (i.e. manual removal/deletion)\\nauto-eot-cancellation-expiration-demotion\\nauto-eot-cancellation-expiration-deletion\\nipn-cancellation-expiration-demotion\\nipn-cancellation-expiration-deletion\\nipn-refund-reversal-demotion\\nipn-refund-reversal-deletion\'); return false;">list of possible values</a> ]</li>'."\n";
640
- echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the original Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e. there was only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
641
- echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
642
- echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remained constant throughout the lifetime of the Membership. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
643
  echo '<li><code>%%user_first_name%%</code> = The First Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
644
  echo '<li><code>%%user_last_name%%</code> = The Last Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
645
  echo '<li><code>%%user_full_name%%</code> = The Full Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
@@ -654,7 +654,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
654
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
655
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
656
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
657
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
658
  echo '</ul>'."\n";
659
 
660
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
@@ -663,7 +663,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
663
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
664
  echo '</ul>'."\n";
665
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
666
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
667
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
668
 
669
  echo '</td>'."\n";
@@ -711,7 +711,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
711
 
712
  echo '<div class="ws-menu-page-section ws-plugin--s2member-refund-reversal-notifications-section">'."\n";
713
  echo '<h3>Refund/Reversal Notification URLs (optional)</h3>'."\n";
714
- echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Subscriptions have been refunded or reversed <em>(i.e. charged back to you)</em>, you\'ll want to read this section. This is marked `Refund/Reversal`, because the URLs that you list below, will ONLY be notified in those specific cases, as opposed to EOT/Deletion Notifications, which are all-inclusive.</p>'."\n";
715
  echo '<p>This is very similar to the EOT/Deletion Notification described above. However, there is an important distinction. The all-inclusive EOT/Deletion Notification includes cancellations, expirations, failed payments, refunds, chargebacks, and even manual deletions by the Administrator from within the Dashboard. In other words, EOT/Deletion Notifications are processed ANY time a deletion or End Of Term action takes place. This API Notification, that is, Refund/Reversal Notifications, do NOT include all of those scenarios.</p>'."\n";
716
  echo '<p>So the distinction is that Refund/Reversal Notifications are ONLY sent under two specific circumstances: 1. You log into your Payment Gateway and refund a payment that is associated with a Subscription. 2. The Customer complains to your Payment Gateway and a chargeback occurs, forcing a Reversal. In both of these cases, an EOT/Deletion Notification will be sent <em>(as described in the previous section)</em>, but since EOT/Deletion is a broader Notification, these Refund/Reversal Notifications are here so you can nail down specific back-office operations in these two specific scenarios.</p>'."\n";
717
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Refund/Reversal Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
@@ -734,15 +734,15 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
734
  echo '<td>'."\n";
735
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
736
  echo '<textarea name="ws_plugin__s2member_ref_rev_notification_urls" id="ws-plugin--s2member-ref-rev-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["ref_rev_notification_urls"]).'</textarea><br />'."\n";
737
- echo 'Refund/Reversal Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
738
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
739
  echo '<ul class="ws-menu-page-li-margins">'."\n";
740
- echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the original Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e. there was only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
741
- echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
742
- echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remained constant throughout the lifetime of the Membership. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e. it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
743
  echo '<li><code>%%parent_txn_id%%</code> = The Parent Transaction ID, associated with the original payment being refunded/reversed.</li>'."\n";
744
- echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g. <code>USD</code></li>'."\n";
745
- echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g. <code>$</code></li>'."\n";
746
  echo '<li><code>%%-amount%%</code> = The Negative Amount of the payment, that was refunded or reversed back to the Customer.</li>'."\n";
747
  echo '<li><code>%%-fee%%</code> = The Negative Payment Gateway fee, that was refunded back to you as the Merchant/Seller.</li>'."\n";
748
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased the Membership Subscription.</li>'."\n";
@@ -760,7 +760,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
760
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
761
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
762
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
763
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
764
  echo '</ul>'."\n";
765
 
766
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
@@ -769,7 +769,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
769
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
770
  echo '</ul>'."\n";
771
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
772
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
773
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
774
 
775
  echo '</td>'."\n";
@@ -818,7 +818,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
818
  echo '<div class="ws-menu-page-section ws-plugin--s2member-sp-sale-notifications-section">'."\n";
819
  echo '<h3>Specific Post/Page ~ Sale Notification URLs (optional)</h3>'."\n";
820
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Specific Post/Page sales take place, you\'ll want to read this section. This is marked `Specific Post/Page`, because the URLs that you list below, will be notified each time a payment occurs, on a sale providing access to a Specific Post/Page.</p>'."\n";
821
- echo '<p>This is one of only two API Notifications that are sent for Specific Post/Page Access <em>(i.e. this one, and another below, for Refunds/Reversals)</em>. All of the other API Notifications are designed for Membership Level Access'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' and Independent Custom Capabilities').'. None of the other API Notifications will ever be processed for Specific Post/Page Access. If you intend to respond to events related to Specific Post/Page Access, you MUST use one of the two API Notifications specifically geared to Post/Page Access.</p>'."\n";
822
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Sale Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
823
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
824
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_sp_sale_notifications", get_defined_vars());
@@ -839,15 +839,15 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
839
  echo '<td>'."\n";
840
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
841
  echo '<textarea name="ws_plugin__s2member_sp_sale_notification_urls" id="ws-plugin--s2member-sp-sale-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sp_sale_notification_urls"]).'</textarea><br />'."\n";
842
- echo 'Specific Post/Page ~ Sale Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
843
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
844
  echo '<ul class="ws-menu-page-li-margins">'."\n";
845
  echo '<li><code>%%sp_access_url%%</code> = The full URL (generated by s2Member) where the Customer can gain access.</li>'."\n";
846
  echo '<li><code>%%sp_access_exp%%</code> = Human readable expiration for <code>%%sp_access_url%%</code>. Ex: <em>(link expires in <code>%%sp_access_exp%%</code>)</em>.</li>'."\n";
847
  echo '<li><code>%%txn_id%%</code> = The Paid Transaction ID. Payment Gateways assign a unique identifier for every purchase.</li>'."\n";
848
- echo '<li><code>%%txn_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe. Each Stripe Customer has this Customer ID; and also a Transaction ID associated with their purchase of the Specific Post/Page [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%txn_cid%% is simply set to the %%txn_id%% value; i.e. it is a duplicate of %%txn_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Transaction ID. See %%txn_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
849
- echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g. <code>USD</code></li>'."\n";
850
- echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g. <code>$</code></li>'."\n";
851
  echo '<li><code>%%amount%%</code> = The full Amount of the sale. If you offer something 100% free, this will be <code>0</code>.</li>'."\n";
852
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased Specific Post/Page Access.</li>'."\n";
853
  echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who purchased Specific Post/Page Access.</li>'."\n";
@@ -860,11 +860,11 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
860
 
861
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
862
  {
863
- echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro Forms):</strong>'."\n";
864
  echo '<ul class="ws-menu-page-li-margins">'."\n";
865
- echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e. the full Coupon Code).</li>'."\n";
866
- echo '<li><code>%%coupon_code%%</code> = A Coupon Code if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
867
- echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e. the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
868
  echo '</ul>'."\n";
869
  }
870
 
@@ -874,7 +874,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
874
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
875
  echo '</ul>'."\n";
876
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
877
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
878
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
879
 
880
  echo '</td>'."\n";
@@ -922,8 +922,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
922
 
923
  echo '<div class="ws-menu-page-section ws-plugin--s2member-sp-refund-reversal-notifications-section">'."\n";
924
  echo '<h3>Specific Post/Page ~ Refund/Reversal Notification URLs (optional)</h3>'."\n";
925
- echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever sales have been refunded or reversed <em>(i.e. charged back to you)</em>, you\'ll want to read this section. This is marked `Specific Post/Page`, because the URLs that you list below, will be notified each time a Refund or Reversal occurs, on a sale that provided access to a Specific Post/Page.</p>'."\n";
926
- echo '<p>This is one of only two Notifications that are sent for Specific Post/Page Access <em>(i.e. this one, and another above, for Sales)</em>. All of the other API Notifications are designed for Membership Level Access'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' and Independent Custom Capabilities').'. None of the other API Notifications will ever be processed for Specific Post/Page Access. If you intend to respond to events related to Specific Post/Page Access, you MUST use one of the two API Notifications specifically geared to Post/Page Access.</p>'."\n";
927
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Refund/Reversal Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
928
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
929
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_sp_refund_reversal_notifications", get_defined_vars());
@@ -944,13 +944,13 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
944
  echo '<td>'."\n";
945
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
946
  echo '<textarea name="ws_plugin__s2member_sp_ref_rev_notification_urls" id="ws-plugin--s2member-sp-ref-rev-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sp_ref_rev_notification_urls"]).'</textarea><br />'."\n";
947
- echo 'Specific Post/Page ~ Refund/Reversal Notifications take place silently behind-the-scene, using an HTTP connection.<br /><br />'."\n";
948
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
949
  echo '<ul class="ws-menu-page-li-margins">'."\n";
950
  echo '<li><code>%%parent_txn_id%%</code> = The Parent Transaction ID, associated with the original payment being refunded/reversed.</li>'."\n";
951
- echo '<li><code>%%parent_txn_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe. Each Stripe Customer has this Customer ID; and also a Transaction ID associated with their purchase of the Specific Post/Page [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%parent_txn_cid%% is simply set to the %%parent_txn_id%% value; i.e. it is a duplicate of %%parent_txn_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Transaction ID. See %%parent_txn_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
952
- echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g. <code>USD</code></li>'."\n";
953
- echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g. <code>$</code></li>'."\n";
954
  echo '<li><code>%%-amount%%</code> = The Negative Amount of the payment, that was refunded or reversed back to the Customer.</li>'."\n";
955
  echo '<li><code>%%-fee%%</code> = The Negative Payment Gateway fee, that was refunded back to you as the Merchant/Seller.</li>'."\n";
956
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased access to a Specific Post/Page.</li>'."\n";
@@ -968,7 +968,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
968
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
969
  echo '</ul>'."\n";
970
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
971
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
972
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
973
 
974
  echo '</td>'."\n";
@@ -1010,8 +1010,6 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_api_ops"))
1010
 
1011
  do_action("ws_plugin__s2member_during_api_ops_page_after_left_sections", get_defined_vars());
1012
 
1013
- echo '<div class="ws-menu-page-hr"></div>'."\n";
1014
-
1015
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
1016
 
1017
  echo '</form>'."\n";
42
  echo '<tr class="ws-menu-page-table-tr">'."\n";
43
  echo '<td class="ws-menu-page-table-l">'."\n";
44
 
45
+ echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" autocomplete="off">'."\n";
46
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
  echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
48
 
56
 
57
  echo '<div class="ws-menu-page-section ws-plugin--s2member-signup-notifications-section">'."\n";
58
  echo '<h3>Signup Notification URLs (optional)</h3>'."\n";
59
+ echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever a new Subscription is created, you\'ll want to read this section. This is marked `Signup`, because the URLs that you list below, will be notified each time a "new", "paying" Member signs up. Depending on your fee structure, this may include a payment that establishes their Subscription, or it may not. This Notification will only be triggered once for each Member. Signup Notifications are sent right after a "new", "paying" Member signs up successfully through your Payment Gateway, regardless of whether any money has actually been transacted. In other words, this Notification is triggered anytime a "new", "paying" Member signs up through your Payment Gateway, even if you provided them with something for free <em>(i.e., even if no money is being transacted)</em>.</p>'."\n";
60
+ echo '<p>This Notification will NOT be processed for Free Subscribers that register without going through your Payment Gateway at all (i.e., they simply register on-site; and there is no checkout whatsoever). This Notification will NOT be processed when an "existing" User/Member pays for a new Subscription <em>(see: Modification Notifications for that scenario)</em>.'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' And, this Notification will NOT be processed on Buy Now transactions for Independent Custom Capabilities <em>(see: Payment Notifications for that scenario)</em>.').'</p>'."\n";
61
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Signup Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
62
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
63
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_signup_notifications", get_defined_vars());
78
  echo '<td>'."\n";
79
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
80
  echo '<textarea name="ws_plugin__s2member_signup_notification_urls" id="ws-plugin--s2member-signup-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["signup_notification_urls"]).'</textarea><br />'."\n";
81
+ echo 'Signup Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
82
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
83
  echo '<ul class="ws-menu-page-li-margins">'."\n";
84
+ echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there is only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
85
+ echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
86
+ echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remains constant throughout any &amp; all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
87
+ echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
88
+ echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
89
  echo '<li><code>%%initial%%</code> = The Initial Fee charged during signup. If you offered a 100% Free Trial, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This will always represent the amount of money the Customer spent, whenever they initially signed up, no matter what. Even if that amount is 0.\\n\\nIf a Customer signs up, under the terms of a 100% Free Trial Period, this will be 0. So be careful using %%initial%% when you offer a 100% Free Trial Period, because a $0.00 sale amount could cause havoc with affiliate programs.\\n\\nIf you\\\'re offering a 100% Free Trial Period, and you need to track sales through affiliate programs, you can either hard-code an amount; or use `Payment Notifications` instead.\'); return false;">?</a> ]</li>'."\n";
90
  echo '<li><code>%%regular%%</code> = The Regular Amount of the Subscription. If you offer something 100% free, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This is how much the Subscription costs after an Initial Period expires. If you did NOT offer an Initial Period at a different price, %%initial%% and %%regular%% will be equal to the same thing.\'); return false;">?</a> ]</li>'."\n";
91
  echo '<li><code>%%recurring%%</code> = This is the amount that will be charged on a recurring basis, or <code>0</code> if non-recurring. [ <a href="#" onclick="alert(\'If Recurring Payments have not been required, this will be equal to 0. That being said, %%regular%% &amp; %%recurring%% are usually the same value. This variable can be used in two different ways. You can use it to determine what the Regular Recurring Rate is, or to determine whether the Subscription will recur or not. If it is going to recur, %%recurring%% will be > 0.\'); return false;">?</a> ]</li>'."\n";
102
 
103
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
104
  {
105
+ echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):</strong>'."\n";
106
  echo '<ul class="ws-menu-page-li-margins">'."\n";
107
+ echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).</li>'."\n";
108
+ echo '<li><code>%%coupon_code%%</code> = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
109
+ echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e., the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
110
  echo '</ul>'."\n";
111
  }
112
 
116
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
117
  echo '</ul>'."\n";
118
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
119
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
120
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
121
 
122
  echo '</td>'."\n";
184
  echo '<td>'."\n";
185
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
186
  echo '<textarea name="ws_plugin__s2member_registration_notification_urls" id="ws-plugin--s2member-registration-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["registration_notification_urls"]).'</textarea><br />'."\n";
187
+ echo 'Registration Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
188
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
189
  echo '<ul class="ws-menu-page-li-margins">'."\n";
190
  echo '<li><code>%%role%%</code> = The Role ID <code>(subscriber, s2member_level[0-9]+, administrator, editor, author, contributor)</code>.</li>'."\n";
206
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
207
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
208
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
209
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member General Options Registration/Profile Fields</em>.</li>'."\n";
210
  echo '</ul>'."\n";
211
 
212
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
215
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
216
  echo '</ul>'."\n";
217
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
218
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
219
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
220
 
221
  echo '</td>'."\n";
264
  echo '<div class="ws-menu-page-section ws-plugin--s2member-payment-notifications-section">'."\n";
265
  echo '<h3>Payment Notification URLs (optional)</h3>'."\n";
266
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever payment transactions <em>(including Recurring Payments)</em> take place, you\'ll want to read this section. This is marked `Payment`, because the URLs that you list below, will be notified each time an actual payment occurs. Depending on your fee structure, this may include a first Initial Payment that establishes a Subscription. But more importantly, this will be triggered on all future payments that are received for the lifetime of the Subscription.</p>'."\n";
267
+ echo '<p>So, unlike the `Signup` Notification, `Payment` Notifications take place whenever actual payments are received, instead of just once after signup is completed. If a payment is required during signup <em>(i.e., no Free Trial is being offered)</em>, a Signup Notification will be triggered, and a Payment Notification will ALSO be triggered. In other words, a Payment Notification occurs anytime funds are received, no matter what.</p>'."\n";
268
  echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '<p>Payment Notifications are also triggered whenever a Buy Now purchase for Independent Custom Capabilities takes place.</p>'."\n" : '';
269
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Payment Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
270
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
286
  echo '<td>'."\n";
287
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
288
  echo '<textarea name="ws_plugin__s2member_payment_notification_urls" id="ws-plugin--s2member-payment-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["payment_notification_urls"]).'</textarea><br />'."\n";
289
+ echo 'Payment Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
290
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
291
  echo '<ul class="ws-menu-page-li-margins">'."\n";
292
+ echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There are some exceptions. If you are selling Lifetime or Fixed-Term (non-recurring) access, and/or Independent Custom Capabilities, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the payment.\\n\\nPayment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there is only ONE payment), which goes for Independent Custom Capability purchases too; using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
293
+ echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
294
+ echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remains constant throughout any &amp; all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
295
  echo '<li><code>%%txn_id%%</code> = The Payment Transaction ID, which is always unique for each payment received.</li>'."\n";
296
+ echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
297
+ echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
298
  echo '<li><code>%%amount%%</code> = The Amount of the payment. Most affiliate programs calculate commissions from this.</li>'."\n";
299
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased the Membership Subscription'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' or Capabilities.').'</li>'."\n";
300
  echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who purchased the Membership Subscription'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' or Capabilities.').'</li>'."\n";
313
 
314
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
315
  {
316
+ echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):</strong><br />'."\n";
317
  echo '<em>These are ONLY included with payments that occur during checkout. They will NOT be provided with any future recurring payments.</em>'."\n";
318
  echo '<ul class="ws-menu-page-li-margins">'."\n";
319
+ echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).</li>'."\n";
320
+ echo '<li><code>%%coupon_code%%</code> = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
321
+ echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e., the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
322
  echo '</ul>'."\n";
323
  }
324
 
327
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
328
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
329
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
330
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member General Options Registration/Profile Fields</em>.</li>'."\n";
331
  echo '</ul>'."\n";
332
 
333
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
336
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
337
  echo '</ul>'."\n";
338
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
339
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
340
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
341
 
342
  echo '</td>'."\n";
384
 
385
  echo '<div class="ws-menu-page-section ws-plugin--s2member-modification-notifications-section">'."\n";
386
  echo '<h3>Modification Notification URLs (optional)</h3>'."\n";
387
+ echo '<p>If you use affiliate software, or have back-office routines that need to be notified each time a new Subscription is created by an "existing" User/Member, or an "existing" Member modifies their paid Subscription terms, you\'ll want to read this section. This is marked `Modification`, because the URLs that you list below, will be notified each time an "existing" User/Member <em>(even if they are/were a Free Subscriber)</em> signs up for a paid Subscription <em>(i.e., a Modification takes place against an existing account within WordPress)</em>, or an "existing" Member modifies their paid Subscription terms <em>(again, a Modification takes places against an existing account within WordPress)</em>. Depending on your fee structure, this may include a payment that establishes their Subscription, or it may not.</p>'."\n";
388
+ echo '<p>Modification Notifications are sent right after a Member signs up and/or modifies billing terms successfully through your Payment Gateway, regardless of whether any money has actually been transacted. In other words, this Notification is triggered, even if you provided them with something for free <em>(i.e., even if no money is being transacted)</em>.</p>'."\n";
389
  echo '<p>This Notification will NOT be processed for "new" Users/Members <em>(see: Signup Notifications for that scenario)</em>.'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' And, this Notification will NOT be processed for Independent Custom Capability purchases <em>(see: Payment Notifications for that scenario)</em>.').'</p>'."\n";
390
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Modification Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
391
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
407
  echo '<td>'."\n";
408
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
409
  echo '<textarea name="ws_plugin__s2member_modification_notification_urls" id="ws-plugin--s2member-modification-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["modification_notification_urls"]).'</textarea><br />'."\n";
410
+ echo 'Modification Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
411
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
412
  echo '<ul class="ws-menu-page-li-margins">'."\n";
413
+ echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there is only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
414
+ echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
415
+ echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remains constant throughout any &amp; all future payments. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
416
+ echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
417
+ echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
418
  echo '<li><code>%%initial%%</code> = The Initial Fee. If you offered a 100% Free Trial, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This will always represent the amount of money the Customer spent when they completed checkout, no matter what. Even if that amount is 0.\\n\\nIf a Customer upgrades/downgrades under the terms of a 100% Free Trial Period, this will be 0. So, please be careful using %%initial%% when you offer a 100% Free Trial Period, because a $0.00 sale amount could cause havoc with affiliate programs.\\n\\nIf you\\\'re offering a 100% Free Trial Period, and you need to track sales through affiliate programs, you can either hard-code an amount; or use `Payment Notifications` instead.\'); return false;">?</a> ]</li>'."\n";
419
  echo '<li><code>%%regular%%</code> = The Regular Amount of the Subscription. If you offer something 100% free, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This is how much the Subscription costs after an Initial Period expires. If you did NOT offer an Initial Period at a different price, %%initial%% and %%regular%% will be equal to the same thing.\'); return false;">?</a> ]</li>'."\n";
420
  echo '<li><code>%%recurring%%</code> = This is the amount that will be charged on a recurring basis, or <code>0</code> if non-recurring. [ <a href="#" onclick="alert(\'If Recurring Payments have not been required, this will be equal to 0. That being said, %%regular%% &amp; %%recurring%% are usually the same value. This variable can be used in two different ways. You can use it to determine what the Regular Recurring Rate is, or to determine whether the Subscription will recur or not. If it is going to recur, %%recurring%% will be > 0.\'); return false;">?</a> ]</li>'."\n";
437
 
438
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
439
  {
440
+ echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):</strong>'."\n";
441
  echo '<ul class="ws-menu-page-li-margins">'."\n";
442
+ echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).</li>'."\n";
443
+ echo '<li><code>%%coupon_code%%</code> = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
444
+ echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e., the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
445
  echo '</ul>'."\n";
446
  }
447
 
450
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
451
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
452
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
453
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member General Options Registration/Profile Fields</em>.</li>'."\n";
454
  echo '</ul>'."\n";
455
 
456
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
459
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
460
  echo '</ul>'."\n";
461
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
462
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
463
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
464
 
465
  echo '</td>'."\n";
507
  echo '<h3>Cancellation Notification URLs (optional)</h3>'."\n";
508
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Subscriptions are cancelled through your Payment Gateway, you\'ll want to read this section. This is marked `Cancellation`, because the URLs that you list below, will be notified each time a Subscription is cancelled. A Cancellation is triggered when you cancel a Customer\'s Subscription through your Payment Gateway, or when a Customer cancels their own Subscription.</p>'."\n";
509
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Cancellation Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
510
+ echo '<p><em><strong>Understanding Cancellations:</strong> It\'s important to realize that a Cancellation is not an EOT (End Of Term). All that happens during a Cancellation event, is that billing is stopped, and it\'s understood that the Customer is going to lose access, at some point in the future. This does NOT mean, that access will be revoked immediately. A separate EOT event will automatically handle a (demotion or deletion) later, at the appropriate time; which could be several days, or even a year after the Cancellation took place.</em></p>'."\n";
511
+ echo '<p><em><strong>Some Hairy Details:</strong> There might be times whenever you notice that a Member\'s Subscription has been cancelled through your Payment Gateway... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only remove the User\'s Membership privileges when an EOT (End Of Term) is processed, a refund occurs, a chargeback occurs, or when a cancellation occurs - which would later result in a delayed Auto-EOT by s2Member.</em></p>'."\n";
512
  echo '<p><em>s2Member will not process an EOT (End Of Term) until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will remove their Membership privileges; by either demoting them to a Free Subscriber, or deleting their account from the system (based on your configuration). s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.</em></p>'."\n";
513
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
514
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_cancellation_notifications", get_defined_vars());
529
  echo '<td>'."\n";
530
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
531
  echo '<textarea name="ws_plugin__s2member_cancellation_notification_urls" id="ws-plugin--s2member-cancellation-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["cancellation_notification_urls"]).'</textarea><br />'."\n";
532
+ echo 'Cancellation Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
533
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
534
  echo '<ul class="ws-menu-page-li-margins">'."\n";
535
+ echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the original Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there was only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
536
+ echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
537
+ echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remained constant throughout the lifetime of the Membership. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
538
  echo '<li><code>%%item_number%%</code> = The Item Number (colon separated <em>level:custom_capabilities:fixed term</em>) that the Subscription was for.</li>'."\n";
539
  echo '<li><code>%%item_name%%</code> = The Item Name (as provided by the <code>desc=""</code> attribute in your Shortcode, which briefly describes the Item Number).</li>'."\n";
540
  echo '<li><code>%%user_first_name%%</code> = The First Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
551
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
552
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
553
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
554
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member General Options Registration/Profile Fields</em>.</li>'."\n";
555
  echo '</ul>'."\n";
556
 
557
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
560
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
561
  echo '</ul>'."\n";
562
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
563
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
564
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
565
 
566
  echo '</td>'."\n";
609
  echo '<div class="ws-menu-page-section ws-plugin--s2member-eot-deletion-notifications-section">'."\n";
610
  echo '<h3>EOT/Deletion Notification URLs (optional)</h3>'."\n";
611
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Subscriptions have ended <em>(and a Member is demoted to a Free Subscriber)</em>, or when an account is deleted from the system, you\'ll want to read this section. This is marked `EOT/Deletion`, because the URLs that you list below, will be notified in both cases.</p>'."\n";
612
+ echo '<p><strong>EOT = End Of Term.</strong> An EOT is triggered <em>immediately</em> when you refund a Customer, when a Customer forces a chargeback to occur, or when a paid Subscription ends naturally <em>(i.e., expires)</em>, and your Payment Gateway sends s2Member an EOT (End Of Term) response. Delayed EOTs occur after a Cancellation, either as a result of you cancelling a Customer\'s Subscription, or a Customer cancelling their own Subscription. A Cancellation usually results in a delayed EOT, because a Cancellation does NOT always warrant an immediate action; there could still be time left on their Subscription.</p>'."\n";
613
  echo '<p>Manual Deletions will trigger this Notification too. If you delete an account manually from within your WordPress Dashboard, your URLs can be notified automatically through this API Notification, and this scenario can be detected through the Replacement Code <code>%%eot_del_type%%</code>, which is documented below. So the two events are an EOT <em>(of any kind)</em> and/or a Manual Deletion.</p>'."\n";
614
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These EOT/Deletion Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
615
+ echo '<p><em><strong>Some Hairy Details:</strong> There might be times whenever you notice that a Member\'s Subscription has been cancelled through your Payment Gateway... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only remove the User\'s Membership privileges when an EOT (End Of Term) is processed, a refund occurs, a chargeback occurs, or when a cancellation occurs - which would later result in a delayed Auto-EOT by s2Member.</em></p>'."\n";
616
  echo '<p><em>s2Member will not process an EOT (End Of Term) until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will remove their Membership privileges; by either demoting them to a Free Subscriber, or deleting their account from the system (based on your configuration). s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.</em></p>'."\n";
617
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
618
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_eot_deletion_notifications", get_defined_vars());
633
  echo '<td>'."\n";
634
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
635
  echo '<textarea name="ws_plugin__s2member_eot_del_notification_urls" id="ws-plugin--s2member-eot-del-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["eot_del_notification_urls"]).'</textarea><br />'."\n";
636
+ echo 'EOT/Deletion Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
637
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
638
  echo '<ul class="ws-menu-page-li-margins">'."\n";
639
+ echo '<li><code>%%eot_del_type%%</code> = The type of event that triggered this Notification. [ <a href="#" onclick="alert(\'List of possible values:\\n\\nuser-removal-deletion (i.e., manual removal/deletion)\\nauto-eot-cancellation-expiration-demotion\\nauto-eot-cancellation-expiration-deletion\\nipn-cancellation-expiration-demotion\\nipn-cancellation-expiration-deletion\\nipn-refund-reversal-demotion\\nipn-refund-reversal-deletion\'); return false;">list of possible values</a> ]</li>'."\n";
640
+ echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the original Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there was only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
641
+ echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
642
+ echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remained constant throughout the lifetime of the Membership. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
643
  echo '<li><code>%%user_first_name%%</code> = The First Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
644
  echo '<li><code>%%user_last_name%%</code> = The Last Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
645
  echo '<li><code>%%user_full_name%%</code> = The Full Name listed on their User account. This might be different than what is on file with your Payment Gateway.</li>'."\n";
654
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
655
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
656
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
657
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member General Options Registration/Profile Fields</em>.</li>'."\n";
658
  echo '</ul>'."\n";
659
 
660
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
663
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
664
  echo '</ul>'."\n";
665
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
666
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
667
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
668
 
669
  echo '</td>'."\n";
711
 
712
  echo '<div class="ws-menu-page-section ws-plugin--s2member-refund-reversal-notifications-section">'."\n";
713
  echo '<h3>Refund/Reversal Notification URLs (optional)</h3>'."\n";
714
+ echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Subscriptions have been refunded or reversed <em>(i.e., charged back to you)</em>, you\'ll want to read this section. This is marked `Refund/Reversal`, because the URLs that you list below, will ONLY be notified in those specific cases, as opposed to EOT/Deletion Notifications, which are all-inclusive.</p>'."\n";
715
  echo '<p>This is very similar to the EOT/Deletion Notification described above. However, there is an important distinction. The all-inclusive EOT/Deletion Notification includes cancellations, expirations, failed payments, refunds, chargebacks, and even manual deletions by the Administrator from within the Dashboard. In other words, EOT/Deletion Notifications are processed ANY time a deletion or End Of Term action takes place. This API Notification, that is, Refund/Reversal Notifications, do NOT include all of those scenarios.</p>'."\n";
716
  echo '<p>So the distinction is that Refund/Reversal Notifications are ONLY sent under two specific circumstances: 1. You log into your Payment Gateway and refund a payment that is associated with a Subscription. 2. The Customer complains to your Payment Gateway and a chargeback occurs, forcing a Reversal. In both of these cases, an EOT/Deletion Notification will be sent <em>(as described in the previous section)</em>, but since EOT/Deletion is a broader Notification, these Refund/Reversal Notifications are here so you can nail down specific back-office operations in these two specific scenarios.</p>'."\n";
717
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Refund/Reversal Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
734
  echo '<td>'."\n";
735
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
736
  echo '<textarea name="ws_plugin__s2member_ref_rev_notification_urls" id="ws-plugin--s2member-ref-rev-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["ref_rev_notification_urls"]).'</textarea><br />'."\n";
737
+ echo 'Refund/Reversal Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
738
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
739
  echo '<ul class="ws-menu-page-li-margins">'."\n";
740
+ echo '<li><code>%%subscr_id%%</code> = The Paid Subscription ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the original Transaction ID for the purchase. Payment Gateways do not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there was only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
741
+ echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remained constant throughout the lifetime of the Membership. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that required a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
742
+ echo '<li><code>%%subscr_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe, which remained constant throughout the lifetime of the Membership. Each Stripe Customer has this Customer ID; and also a Subscription and/or Transaction ID [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%subscr_cid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Subscription and/or Transaction ID. See %%subscr_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
743
  echo '<li><code>%%parent_txn_id%%</code> = The Parent Transaction ID, associated with the original payment being refunded/reversed.</li>'."\n";
744
+ echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
745
+ echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
746
  echo '<li><code>%%-amount%%</code> = The Negative Amount of the payment, that was refunded or reversed back to the Customer.</li>'."\n";
747
  echo '<li><code>%%-fee%%</code> = The Negative Payment Gateway fee, that was refunded back to you as the Merchant/Seller.</li>'."\n";
748
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased the Membership Subscription.</li>'."\n";
760
  echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
761
  echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
762
  echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
763
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member General Options Registration/Profile Fields</em>.</li>'."\n";
764
  echo '</ul>'."\n";
765
 
766
  echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
769
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
770
  echo '</ul>'."\n";
771
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
772
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
773
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
774
 
775
  echo '</td>'."\n";
818
  echo '<div class="ws-menu-page-section ws-plugin--s2member-sp-sale-notifications-section">'."\n";
819
  echo '<h3>Specific Post/Page ~ Sale Notification URLs (optional)</h3>'."\n";
820
  echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever Specific Post/Page sales take place, you\'ll want to read this section. This is marked `Specific Post/Page`, because the URLs that you list below, will be notified each time a payment occurs, on a sale providing access to a Specific Post/Page.</p>'."\n";
821
+ echo '<p>This is one of only two API Notifications that are sent for Specific Post/Page Access <em>(i.e., this one, and another below, for Refunds/Reversals)</em>. All of the other API Notifications are designed for Membership Level Access'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' and Independent Custom Capabilities').'. None of the other API Notifications will ever be processed for Specific Post/Page Access. If you intend to respond to events related to Specific Post/Page Access, you MUST use one of the two API Notifications specifically geared to Post/Page Access.</p>'."\n";
822
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Sale Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
823
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
824
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_sp_sale_notifications", get_defined_vars());
839
  echo '<td>'."\n";
840
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
841
  echo '<textarea name="ws_plugin__s2member_sp_sale_notification_urls" id="ws-plugin--s2member-sp-sale-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sp_sale_notification_urls"]).'</textarea><br />'."\n";
842
+ echo 'Specific Post/Page ~ Sale Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
843
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
844
  echo '<ul class="ws-menu-page-li-margins">'."\n";
845
  echo '<li><code>%%sp_access_url%%</code> = The full URL (generated by s2Member) where the Customer can gain access.</li>'."\n";
846
  echo '<li><code>%%sp_access_exp%%</code> = Human readable expiration for <code>%%sp_access_url%%</code>. Ex: <em>(link expires in <code>%%sp_access_exp%%</code>)</em>.</li>'."\n";
847
  echo '<li><code>%%txn_id%%</code> = The Paid Transaction ID. Payment Gateways assign a unique identifier for every purchase.</li>'."\n";
848
+ echo '<li><code>%%txn_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe. Each Stripe Customer has this Customer ID; and also a Transaction ID associated with their purchase of the Specific Post/Page [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%txn_cid%% is simply set to the %%txn_id%% value; i.e., it is a duplicate of %%txn_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Transaction ID. See %%txn_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
849
+ echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
850
+ echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
851
  echo '<li><code>%%amount%%</code> = The full Amount of the sale. If you offer something 100% free, this will be <code>0</code>.</li>'."\n";
852
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased Specific Post/Page Access.</li>'."\n";
853
  echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who purchased Specific Post/Page Access.</li>'."\n";
860
 
861
  if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
862
  {
863
+ echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):</strong>'."\n";
864
  echo '<ul class="ws-menu-page-li-margins">'."\n";
865
+ echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).</li>'."\n";
866
+ echo '<li><code>%%coupon_code%%</code> = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
867
+ echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e., the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
868
  echo '</ul>'."\n";
869
  }
870
 
874
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
875
  echo '</ul>'."\n";
876
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
877
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
878
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
879
 
880
  echo '</td>'."\n";
922
 
923
  echo '<div class="ws-menu-page-section ws-plugin--s2member-sp-refund-reversal-notifications-section">'."\n";
924
  echo '<h3>Specific Post/Page ~ Refund/Reversal Notification URLs (optional)</h3>'."\n";
925
+ echo '<p>If you use affiliate software, or have back-office routines that need to be notified whenever sales have been refunded or reversed <em>(i.e., charged back to you)</em>, you\'ll want to read this section. This is marked `Specific Post/Page`, because the URLs that you list below, will be notified each time a Refund or Reversal occurs, on a sale that provided access to a Specific Post/Page.</p>'."\n";
926
+ echo '<p>This is one of only two Notifications that are sent for Specific Post/Page Access <em>(i.e., this one, and another above, for Sales)</em>. All of the other API Notifications are designed for Membership Level Access'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' and Independent Custom Capabilities').'. None of the other API Notifications will ever be processed for Specific Post/Page Access. If you intend to respond to events related to Specific Post/Page Access, you MUST use one of the two API Notifications specifically geared to Post/Page Access.</p>'."\n";
927
  echo '<p>Please note, this feature is not to be confused with the PayPal IPN service. PayPal IPN <em>(and other service integrations)</em> are already built into s2Member. They remain active at all times. These Refund/Reversal Notifications are an added layer of functionality, and they are completely optional.</p>'."\n";
928
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/building-an-api-notification-handler/" target="_blank" rel="external">Building An API Notification Handler</a>.</p>'."\n";
929
  do_action("ws_plugin__s2member_during_api_ops_page_during_left_sections_during_sp_refund_reversal_notifications", get_defined_vars());
944
  echo '<td>'."\n";
945
  echo 'You can input multiple Notification URLs by inserting one per line.<br />'."\n";
946
  echo '<textarea name="ws_plugin__s2member_sp_ref_rev_notification_urls" id="ws-plugin--s2member-sp-ref-rev-notification-urls" rows="3" wrap="off">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sp_ref_rev_notification_urls"]).'</textarea><br />'."\n";
947
+ echo 'Specific Post/Page ~ Refund/Reversal Notifications take place silently behind-the-scenes, using an HTTP connection.<br /><br />'."\n";
948
  echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
949
  echo '<ul class="ws-menu-page-li-margins">'."\n";
950
  echo '<li><code>%%parent_txn_id%%</code> = The Parent Transaction ID, associated with the original payment being refunded/reversed.</li>'."\n";
951
+ echo '<li><code>%%parent_txn_cid%%</code> = Applicable only with Stripe integration. This is the Customer\'s ID in Stripe. Each Stripe Customer has this Customer ID; and also a Transaction ID associated with their purchase of the Specific Post/Page [ <a href="#" onclick="alert(\'Applicable only when you integrate s2Member with Stripe. In all other cases, the %%parent_txn_cid%% is simply set to the %%parent_txn_id%% value; i.e., it is a duplicate of %%parent_txn_id%% when running anything other than Stripe.\\n\\nEach Stripe Customer has a Customer ID; and also a Transaction ID. See %%parent_txn_id%% for further details.\'); return false;">?</a> ]</li>'."\n";
952
+ echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
953
+ echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
954
  echo '<li><code>%%-amount%%</code> = The Negative Amount of the payment, that was refunded or reversed back to the Customer.</li>'."\n";
955
  echo '<li><code>%%-fee%%</code> = The Negative Payment Gateway fee, that was refunded back to you as the Merchant/Seller.</li>'."\n";
956
  echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased access to a Specific Post/Page.</li>'."\n";
968
  echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
969
  echo '</ul>'."\n";
970
  echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
971
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
972
  echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
973
 
974
  echo '</td>'."\n";
1010
 
1011
  do_action("ws_plugin__s2member_during_api_ops_page_after_left_sections", get_defined_vars());
1012
 
 
 
1013
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
1014
 
1015
  echo '</form>'."\n";
includes/menu-pages/code-samples/api-mop-vars-o.x-php CHANGED
@@ -25,7 +25,7 @@ Here is a breakdown on each of these Variables:
25
  Tip: `_s2member_seeking[file]` = (File path, relative to /s2member-files/, or your Amazon® S3 Bucket)
26
  Tip: `_s2member_seeking[ruri]` = (A base 64 encoded URI; only applies w/ Membership URI Restrictions)
27
  Tip: These array elements are mutually exclusive: [post|page|catg|ptag|file|ruri].
28
- Only ONE of these array elements (i.e. Content Types) will be specified.
29
 
30
  * `_s2member_req[level]` = a Membership Level # required for access (only applies to Membership Level Access)
31
  * `_s2member_req[ccap]` = a Custom Capability required for access (only applies to Custom Capability Restrictions)
@@ -49,7 +49,7 @@ Additional Variables that might be useful to developers:
49
 
50
  * `_s2member_res[type]` = (A string. One of: post|page|catg|ptag|file|ruri|ccap|sp|sys)
51
  Tip: Actual Restriction Type that's preventing access; may differ from `_s2member_seeking[type]`.
52
- Tip: Restriction Type `sys` Systematic (i.e. something s2Member protects automatically)
53
  Tip: Restriction Type `ccap` represents Custom Capability Restrictions by s2Member.
54
  Tip: Restriction Type `sp` represents Specific Post/Page Access Restrictions.
55
  Tip: this Restriction Type Variable is ALWAYS passed through.
25
  Tip: `_s2member_seeking[file]` = (File path, relative to /s2member-files/, or your Amazon® S3 Bucket)
26
  Tip: `_s2member_seeking[ruri]` = (A base 64 encoded URI; only applies w/ Membership URI Restrictions)
27
  Tip: These array elements are mutually exclusive: [post|page|catg|ptag|file|ruri].
28
+ Only ONE of these array elements (i.e., Content Types) will be specified.
29
 
30
  * `_s2member_req[level]` = a Membership Level # required for access (only applies to Membership Level Access)
31
  * `_s2member_req[ccap]` = a Custom Capability required for access (only applies to Custom Capability Restrictions)
49
 
50
  * `_s2member_res[type]` = (A string. One of: post|page|catg|ptag|file|ruri|ccap|sp|sys)
51
  Tip: Actual Restriction Type that's preventing access; may differ from `_s2member_seeking[type]`.
52
+ Tip: Restriction Type `sys` Systematic (i.e., something s2Member protects automatically)
53
  Tip: Restriction Type `ccap` represents Custom Capability Restrictions by s2Member.
54
  Tip: Restriction Type `sp` represents Specific Post/Page Access Restrictions.
55
  Tip: this Restriction Type Variable is ALWAYS passed through.
includes/menu-pages/code-samples/api-mop-vars-ro.x-php CHANGED
@@ -24,7 +24,7 @@ Here is a breakdown on each of these Variables:
24
  * `s2member_seeking` = [post|page|catg|ptag]-[ID number] (Post ID, Page ID, Category ID, Tag ID)
25
  Or: `s2member_seeking` = ruri-[base64 encoded URI] (only applies w/ Membership URI Restrictions)
26
  Or: `s2member_seeking` = file-[protected file name] (only applies w/ Download Restrictions)
27
- Or: `s2member_seeking` = bbpress (only applies when the s2Member -› bbPress Bridge is installed)
28
  * `s2member_level_req` = a Membership Level # required for access (only applies to Membership Level Access)
29
  * `s2member_ccap_req` = a Custom Capability required for access (only applies to Custom Capability Restrictions)
30
  * `s2member_sp_req` = a Specific Post/Page ID required (only applies to Specific Post/Page Access Restrictions)
24
  * `s2member_seeking` = [post|page|catg|ptag]-[ID number] (Post ID, Page ID, Category ID, Tag ID)
25
  Or: `s2member_seeking` = ruri-[base64 encoded URI] (only applies w/ Membership URI Restrictions)
26
  Or: `s2member_seeking` = file-[protected file name] (only applies w/ Download Restrictions)
27
+ Or: `s2member_seeking` = bbpress (only applies when the s2Member bbPress Bridge is installed)
28
  * `s2member_level_req` = a Membership Level # required for access (only applies to Membership Level Access)
29
  * `s2member_ccap_req` = a Custom Capability required for access (only applies to Custom Capability Restrictions)
30
  * `s2member_sp_req` = a Specific Post/Page ID required (only applies to Specific Post/Page Access Restrictions)
includes/menu-pages/code-samples/api-mop-vars.x-php CHANGED
@@ -22,7 +22,7 @@ Here is a breakdown that explains each of the values:
22
  - Restriction Type `ruri` indicates that Request URI Access Restrictions triggered the redirection.
23
  - Restriction Type `ccap` indicates Custom Capability Restrictions triggered the redirection.
24
  - Restriction Type `sp` indicates Specific Post/Page Access Restrictions triggered the redirection.
25
- - Restriction Type `sys` indicates Systematic (i.e. something s2Member protects automatically).
26
 
27
  * `[requirement type]` = A string. One of: `level|ccap|sp`
28
  - Requirement Type `level` indicates that a Membership Level is required for access.
22
  - Restriction Type `ruri` indicates that Request URI Access Restrictions triggered the redirection.
23
  - Restriction Type `ccap` indicates Custom Capability Restrictions triggered the redirection.
24
  - Restriction Type `sp` indicates Specific Post/Page Access Restrictions triggered the redirection.
25
+ - Restriction Type `sys` indicates Systematic (i.e., something s2Member protects automatically).
26
 
27
  * `[requirement type]` = A string. One of: `level|ccap|sp`
28
  - Requirement Type `level` indicates that a Membership Level is required for access.
includes/menu-pages/code-samples/current-user-subscr-id.x-php CHANGED
@@ -3,7 +3,7 @@ This may output something like: I-82234JD0923423
3
 
4
  This is the Paid Subscription ID associated with their account.
5
  Or, for Lifetime subscriptions sold through Buy Now buttons, this will hold the Paid Transaction ID associated with their purchase.
6
- Or, if the User is a Free Subscriber (i.e. NOT a paying Member), this will simply be an empty string.
7
 
8
  ---- s2member Shortcode Equivalent ----
9
 
3
 
4
  This is the Paid Subscription ID associated with their account.
5
  Or, for Lifetime subscriptions sold through Buy Now buttons, this will hold the Paid Transaction ID associated with their purchase.
6
+ Or, if the User is a Free Subscriber (i.e., NOT a paying Member), this will simply be an empty string.
7
 
8
  ---- s2member Shortcode Equivalent ----
9
 
includes/menu-pages/code-samples/current-user-subscr-or-wp-id.x-php CHANGED
@@ -4,7 +4,7 @@ This may output something like: I-82234JD0923423
4
 
5
  This is the Paid Subscription ID associated with their account.
6
  Or, for Lifetime subscriptions sold through Buy Now buttons, this will hold the Paid Transaction ID associated with their purchase.
7
- Or, if the User is a Free Subscriber (i.e. NOT a paying Member), this will hold their Free Subscriber ID (i.e. a WordPress® User ID).
8
 
9
  ---- s2member Shortcode Equivalent ----
10
 
4
 
5
  This is the Paid Subscription ID associated with their account.
6
  Or, for Lifetime subscriptions sold through Buy Now buttons, this will hold the Paid Transaction ID associated with their purchase.
7
+ Or, if the User is a Free Subscriber (i.e., NOT a paying Member), this will hold their Free Subscriber ID (i.e., a WordPress® User ID).
8
 
9
  ---- s2member Shortcode Equivalent ----
10
 
includes/menu-pages/code-samples/s2-conditional-supplements-2.x-php CHANGED
@@ -1,7 +1,7 @@
1
  <?php if (current_user_cannot("access_s2member_level1")){ ?>
2
  Content for Members who CANNOT access Level #1 on this Blog.
3
  <?php } else if(current_user_cannot_for_blog(24, "access_s2member_level1")) { ?>
4
- Content for Members who CANNOT access Level #1 on Blog ID# 24. (i.e. Multisite Networking)
5
  <?php } else if(is_user_not_logged_in()) { ?>
6
  Some public content.
7
  <?php } ?>
1
  <?php if (current_user_cannot("access_s2member_level1")){ ?>
2
  Content for Members who CANNOT access Level #1 on this Blog.
3
  <?php } else if(current_user_cannot_for_blog(24, "access_s2member_level1")) { ?>
4
+ Content for Members who CANNOT access Level #1 on Blog ID# 24. (i.e., Multisite Networking)
5
  <?php } else if(is_user_not_logged_in()) { ?>
6
  Some public content.
7
  <?php } ?>
includes/menu-pages/code-samples/s2-conditional-supplements-3.x-php CHANGED
@@ -1,7 +1,7 @@
1
  <?php if(current_user_cannot("access_s2member_level4") && (current_user_can("access_s2member_level2") || current_user_can_for_blog(24, "access_s2member_level2"))) { ?>
2
 
3
  This Member CANNOT access Level #4, but...
4
- they CAN access Level #2 on this Blog, OR (||) on Blog ID# 24. (i.e. Multisite Networking)
5
 
6
  <?php if(current_user_can("access_s2member_ccap_free_gift")){ ?>
7
  Also display free gift here. This is a Custom Capability check, using a nested Conditional.
@@ -9,7 +9,7 @@
9
 
10
  <?php } else if(current_user_can("access_s2member_level1") || current_user_can_for_blog(24, "access_s2member_level1")) { ?>
11
 
12
- Content for Members who can access Level #1 on this Blog, OR (||) on Blog ID# 24. (i.e. Multisite Networking)
13
 
14
  <?php } else if(is_user_not_logged_in()) { ?>
15
 
1
  <?php if(current_user_cannot("access_s2member_level4") && (current_user_can("access_s2member_level2") || current_user_can_for_blog(24, "access_s2member_level2"))) { ?>
2
 
3
  This Member CANNOT access Level #4, but...
4
+ they CAN access Level #2 on this Blog, OR (||) on Blog ID# 24. (i.e., Multisite Networking)
5
 
6
  <?php if(current_user_can("access_s2member_ccap_free_gift")){ ?>
7
  Also display free gift here. This is a Custom Capability check, using a nested Conditional.
9
 
10
  <?php } else if(current_user_can("access_s2member_level1") || current_user_can_for_blog(24, "access_s2member_level1")) { ?>
11
 
12
+ Content for Members who can access Level #1 on this Blog, OR (||) on Blog ID# 24. (i.e., Multisite Networking)
13
 
14
  <?php } else if(is_user_not_logged_in()) { ?>
15
 
includes/menu-pages/code-samples/s2drip-example1.x-php CHANGED
@@ -2,7 +2,7 @@
2
  Some content for Members at Level 1 (or higher).
3
 
4
  This content will show only after day 3 of their paid membership.
5
- ~ i.e. it starts showing on day 4.
6
 
7
  It will continue to be shown every day thereafter.
8
  [/s2Drip]
@@ -11,10 +11,10 @@
11
  Some content for Members at Level 1 (or higher).
12
 
13
  This content will show only after day 3 of their paid membership.
14
- ~ i.e. it starts showing on day 4.
15
 
16
  In addition, this content will stop being shown on day 31 of their paid membership.
17
- ... i.e. show until (and including) day 30; stop showing after day 30.
18
  [/s2Drip]
19
 
20
  [s2Drip access="level1 AND (ccap_music OR ccap_videos)" from_day="4" to_day="30"]
@@ -22,16 +22,16 @@
22
  They must also have the CCAP `music` or the CCAP `videos` too.
23
 
24
  This content will show only after day 3 of their paid membership.
25
- ~ i.e. it starts showing on day 4.
26
 
27
  In addition, this content will stop being shown on day 31 of their paid membership.
28
- ... i.e. show until (and including) day 30; stop showing after day 30.
29
  [/s2Drip]
30
 
31
  **NOTE** The use of [s2Drip] requires s2Member Pro.
32
 
33
  **NOTE** [s2Drip] will NOT work as expected when testing against an Administrator who is logged in.
34
  While Administrators DO gain full access to all content, Administrators generally
35
- do NOT have a paid registration time; i.e. they are not an actual customer.
36
  Thus, do NOT test this as an Administrator, you WILL be confused :-)
37
  Administrators will always see the content, no matter.
2
  Some content for Members at Level 1 (or higher).
3
 
4
  This content will show only after day 3 of their paid membership.
5
+ ~ i.e., it starts showing on day 4.
6
 
7
  It will continue to be shown every day thereafter.
8
  [/s2Drip]
11
  Some content for Members at Level 1 (or higher).
12
 
13
  This content will show only after day 3 of their paid membership.
14
+ ~ i.e., it starts showing on day 4.
15
 
16
  In addition, this content will stop being shown on day 31 of their paid membership.
17
+ ... i.e., show until (and including) day 30; stop showing after day 30.
18
  [/s2Drip]
19
 
20
  [s2Drip access="level1 AND (ccap_music OR ccap_videos)" from_day="4" to_day="30"]
22
  They must also have the CCAP `music` or the CCAP `videos` too.
23
 
24
  This content will show only after day 3 of their paid membership.
25
+ ~ i.e., it starts showing on day 4.
26
 
27
  In addition, this content will stop being shown on day 31 of their paid membership.
28
+ ... i.e., show until (and including) day 30; stop showing after day 30.
29
  [/s2Drip]
30
 
31
  **NOTE** The use of [s2Drip] requires s2Member Pro.
32
 
33
  **NOTE** [s2Drip] will NOT work as expected when testing against an Administrator who is logged in.
34
  While Administrators DO gain full access to all content, Administrators generally
35
+ do NOT have a paid registration time; i.e., they are not an actual customer.
36
  Thus, do NOT test this as an Administrator, you WILL be confused :-)
37
  Administrators will always see the content, no matter.
includes/menu-pages/code-samples/s2member-paid-registration-time.x-php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  The $level argument is optional. It defaults to the first/initial Paid Registration Time, regardless of Level#.
4
  Or you could do this: s2member_paid_registration_time("level1"); which will give you the Registration Time at Level #1.
5
- If a User/Member has never paid for Level #1 (i.e. they signed up at Level#2), the function will return 0.
6
 
7
  The argument $user_id defaults to the current user that is logged in.
8
 
2
 
3
  The $level argument is optional. It defaults to the first/initial Paid Registration Time, regardless of Level#.
4
  Or you could do this: s2member_paid_registration_time("level1"); which will give you the Registration Time at Level #1.
5
+ If a User/Member has never paid for Level #1 (i.e., they signed up at Level#2), the function will return 0.
6
 
7
  The argument $user_id defaults to the current user that is logged in.
8
 
includes/menu-pages/code-samples/sc-current-user-is-specific-content-else-farm.x-php CHANGED
@@ -1,4 +1,4 @@
1
- Notice that NESTED Conditionals require a preceding underscore (i.e. _s2If, __s2If, ___s2If).
2
  You can go up to ten levels deep ( __________s2If ).
3
 
4
  [s2If current_user_is(s2member_level4)]
1
+ Notice that NESTED Conditionals require a preceding underscore (i.e., _s2If, __s2If, ___s2If).
2
  You can go up to ten levels deep ( __________s2If ).
3
 
4
  [s2If current_user_is(s2member_level4)]
includes/menu-pages/code-samples/sc-current-user-is-specific-content-else.x-php CHANGED
@@ -1,4 +1,4 @@
1
- Notice that NESTED Conditionals require a preceding underscore (i.e. _s2If, __s2If, ___s2If).
2
  You can go up to ten levels deep ( __________s2If ).
3
 
4
  [s2If current_user_is(s2member_level4)]
1
+ Notice that NESTED Conditionals require a preceding underscore (i.e., _s2If, __s2If, ___s2If).
2
  You can go up to ten levels deep ( __________s2If ).
3
 
4
  [s2If current_user_is(s2member_level4)]
includes/menu-pages/code-samples/sc-s2-conditional-supplements-1-farm.x-php CHANGED
@@ -17,7 +17,7 @@
17
  You have [s2Get constant="S2MEMBER_CURRENT_USER_ACCESS_LABEL" /].
18
 
19
  ^ This uses the s2Get Shortcode to retrieve the value of s2Member API Constants.
20
- These are also documented under: `s2Member -› API Scripting`.
21
 
22
  So, this might come out to something like:
23
  `Hi there John.
17
  You have [s2Get constant="S2MEMBER_CURRENT_USER_ACCESS_LABEL" /].
18
 
19
  ^ This uses the s2Get Shortcode to retrieve the value of s2Member API Constants.
20
+ These are also documented under: `s2Member API Scripting`.
21
 
22
  So, this might come out to something like:
23
  `Hi there John.
includes/menu-pages/code-samples/sc-s2-conditional-supplements-1.x-php CHANGED
@@ -5,7 +5,7 @@
5
  [s2If current_user_is(s2member_level2) OR current_user_is_for_blog(24,s2member_level2)]
6
 
7
  They are either a Level #2 Member on this Blog,
8
- OR ... they're at Level# 2 on Blog ID# 24 (i.e. Multisite Networking)
9
 
10
  * Note the use of `OR` here. True if either condition is met.
11
 
@@ -19,7 +19,7 @@
19
  You have [s2Get constant="S2MEMBER_CURRENT_USER_ACCESS_LABEL" /].
20
 
21
  ^ This uses the s2Get Shortcode to retrieve the value of s2Member API Constants.
22
- These are also documented under: `s2Member -› API Scripting`.
23
 
24
  So, this might come out to something like:
25
  `Hi there John.
5
  [s2If current_user_is(s2member_level2) OR current_user_is_for_blog(24,s2member_level2)]
6
 
7
  They are either a Level #2 Member on this Blog,
8
+ OR ... they're at Level# 2 on Blog ID# 24 (i.e., Multisite Networking)
9
 
10
  * Note the use of `OR` here. True if either condition is met.
11
 
19
  You have [s2Get constant="S2MEMBER_CURRENT_USER_ACCESS_LABEL" /].
20
 
21
  ^ This uses the s2Get Shortcode to retrieve the value of s2Member API Constants.
22
+ These are also documented under: `s2Member API Scripting`.
23
 
24
  So, this might come out to something like:
25
  `Hi there John.
includes/menu-pages/code-samples/sc-s2-conditional-supplements-3-farm.x-php CHANGED
@@ -5,7 +5,7 @@
5
 
6
  [_s2If current_user_can(access_s2member_level3)]
7
 
8
- Notice that NESTED Conditionals require a preceding underscore (i.e. _s2If, __s2If, ___s2If).
9
  You can go up to ten levels deep ( __________s2If ).
10
 
11
  Nested PayPal Form/Button Shortcodes ARE fine too.
5
 
6
  [_s2If current_user_can(access_s2member_level3)]
7
 
8
+ Notice that NESTED Conditionals require a preceding underscore (i.e., _s2If, __s2If, ___s2If).
9
  You can go up to ten levels deep ( __________s2If ).
10
 
11
  Nested PayPal Form/Button Shortcodes ARE fine too.
includes/menu-pages/code-samples/sc-s2-conditional-supplements-3.x-php CHANGED
@@ -6,7 +6,7 @@
6
  [_s2If current_user_can(access_s2member_ccap_free_gift)]
7
 
8
  Display free gift here. This is a Custom Capability check, using a nested Conditional.
9
- Notice that NESTED Conditionals require a preceding underscore (i.e. _s2If, __s2If, ___s2If).
10
  You can go up to ten levels deep ( __________s2If ).
11
 
12
  Nested PayPal Form/Button Shortcodes ARE fine too.
6
  [_s2If current_user_can(access_s2member_ccap_free_gift)]
7
 
8
  Display free gift here. This is a Custom Capability check, using a nested Conditional.
9
+ Notice that NESTED Conditionals require a preceding underscore (i.e., _s2If, __s2If, ___s2If).
10
  You can go up to ten levels deep ( __________s2If ).
11
 
12
  Nested PayPal Form/Button Shortcodes ARE fine too.
includes/menu-pages/down-ops.inc.php CHANGED
@@ -42,7 +42,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
42
  echo '<tr class="ws-menu-page-table-tr">'."\n";
43
  echo '<td class="ws-menu-page-table-l">'."\n";
44
 
45
- echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" action="'.esc_attr(remove_query_arg('ws_plugin__s2member_cf_options_reset')).'">'."\n";
46
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
  echo '<input type="hidden" name="ws_plugin__s2member_amazon_cf_files_distros_auto_config_status" id="ws-plugin--s2member-amazon-cf-files-distros-auto-config-status" value="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_distros_auto_config_status"]).'" />'."\n";
48
  echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
@@ -58,7 +58,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
58
  echo '<div class="ws-menu-page-section ws-plugin--s2member-restrictions-section">'."\n";
59
  echo '<h3>File Download Restrictions (required, if providing access to protected files)</h3>'."\n";
60
  echo '<p>If your Membership offering allows access to restricted files, you\'ll want to configure these options.</p>'."\n";
61
- echo '<p class="info" style="font-size:100%;"><strong>NOTE:</strong> If you intend to offer File Downloads in one way or another, you must configure at least one of the options below. For security purposes, s2Member\'s File Download functionality is disabled unless &amp; until at least one of the options below have been configured; i.e. s2Member expects you to configure Basic Downloads for at least one Membership Level before any sort of download-related functionality will work. This includes functionality associated with the <code>[s2File /]</code> &amp; <code>[s2Stream /]</code> Shortcodes also.</p>'."\n";
62
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_restrictions", get_defined_vars());
63
 
64
  echo '<div class="ws-menu-page-hr"></div>'."\n";
@@ -94,7 +94,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
94
  echo '<td>'."\n";
95
  echo '<input type="text" maxlength="9" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_file_downloads_allowed" id="ws-plugin--s2member-level'.$n.'-file-downloads-allowed" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_file_downloads_allowed"]).'" style="width:200px;" /> every <input type="text" maxlength="3" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_file_downloads_allowed_days" id="ws-plugin--s2member-level'.$n.'-file-downloads-allowed-days" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_file_downloads_allowed_days"]).'" style="width:200px;" onkeyup="if(this.value > 365){ alert(\'(365 days is the maximum).\\nThis keeps the logs optimized.\'); this.value = 365; }" /> day(s).<br />'."\n";
96
  echo 'Only this many unique downloads will be permitted every X day(s), at '.(($n === $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]) ? 'highest Level #'.$n : 'Level #'.$n.' or higher').'.<br />'."\n";
97
- echo '<em>* To allow UNLIMITED downloads, use: <code>999999999</code> (i.e. <code>999999999</code> = unlimited).</em>'."\n";
98
  echo '</td>'."\n";
99
 
100
  echo '</tr>'."\n";
@@ -251,18 +251,19 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
251
 
252
  echo '<div class="ws-menu-page-section ws-plugin--s2member-amazon-s3-section">'."\n";
253
  echo '<h3>Amazon S3/CDN Storage &amp; Delivery (optional)</h3>'."\n";
 
254
  echo '<a href="http://aws.amazon.com/s3/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/amazon-logo.png" class="ws-menu-page-right" style="width:250px; height:100px; border:0;" alt="." /></a>'."\n";
255
- echo '<p>Please note, all of this is optional. s2Member can be configured here to ONLY use Amazon S3 <em>(i.e. without Amazon CloudFront)</em>. Or, s2Member can be configured to use BOTH Amazon S3 and Amazon CloudFront together. If you want to use Amazon S3 Storage, but you don\'t care about Amazon CloudFront, feel free to leave the entire Amazon CloudFront section empty. The configuration options in the Amazon CloudFront section are ONLY required if you are planning to use both Amazon S3 and Amazon CloudFront together.</p>'."\n";
256
  echo '<p>Amazon Simple Storage Service (<a href="http://aws.amazon.com/s3/" target="_blank" rel="external">Amazon S3</a>). Amazon S3 is storage for the Internet. It is designed to make web-scale computing easier for developers. Amazon S3 provides a simple web services interface that can be used to store and retrieve any amount of data, at any time, from anywhere on the web. It gives developers access to the same highly scalable, reliable, secure, fast, inexpensive infrastructure that Amazon uses to run its own global network of web sites. s2Member has been integrated with Amazon S3, so that <em>(if you wish)</em>, instead of using the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory, you can store all of your protected files inside an Amazon S3 Bucket.</p>'."\n";
257
- echo '<p>If you configure the options below, s2Member will assume all protected files are inside your Amazon S3 Bucket; and the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory is no longer used at all. That being said, all other aspects of s2Member\'s File Download protection remain the same. The only thing that changes, is the location of your protected files. In other words, Basic Download Restrictions, Download Keys, Inline Extensions, Custom Capability and/or Membership Level Files will all continue to work just as before. The only difference is that s2Member will use your Amazon S3 Bucket as a CDN <em>(i.e. Content Delivery Network)</em> instead of the local <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory.</p>'."\n";
258
- echo '<p>s2Member assumes that you\'re creating a new Amazon S3 Bucket, specifically for this installation; and that your Bucket is NOT available publicly. In other words, if you type this URL into your browser <em>(i.e. <code>http://your-bucket-name.s3.amazonaws.com/</code>)</em>, you should get an error that says: <code>Access Denied</code>. That\'s good, that\'s exactly what you want. You can create your Amazon S3 Bucket using the <a href="https://console.aws.amazon.com/s3/home" target="_blank" rel="external">Amazon interface</a>. Or, some people prefer to use this popular Firefox extension (<a href="http://www.s3fox.net/" target="_blank" rel="external">S3 Fox Organizer</a>).</p>'."\n";
259
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_amazon_s3", get_defined_vars());
260
 
261
  echo '<div class="ws-menu-page-hr"></div>'."\n";
262
 
263
  echo '<p><em><strong>Dev Note w/Technical Details:</strong> s2Member uses "Digitally Signed URLs", authenticated by the Amazon S3 API. Documented for developers <a href="http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html" target="_blank" rel="external">here</a>. To put it simply, s2Member will generate Amazon S3 URLs (internally); which allow Customers temporary access to specific files inside your S3 Bucket. s2Member\'s Digitally Signed URLs leading to Amazon S3, give a Customer 24 hours to connect to the file inside your S3 Bucket. This connection period of 24 hours is largely irrelevant when used in combination with s2Member, because access is renewed for another 24 hours each time you make a file available to a User/Member, and they are authenticated by your configuration of s2Member. This connection period of 24 hours is just a secondary line of defense to further prevent the possibility of link sharing. If you need to change this connection timeout of <code>24 hours</code> for some reason (not likely), you can use this WordPress Filter: <code>ws_plugin__s2member_amazon_s3_file_expires_time</code>.</em></p>'."\n";
264
- echo '<p><em><strong>Linking To Protected Files:</strong> Nothing changes. s2Member\'s integration with Amazon S3 serves protected files through the same links that all s2Member site owners use. For example, you might use: <code>'.esc_html(home_url("/?s2member_file_download=example-file.zip")).'</code>, where <strong>s2member_file_download</strong> = the file, relative to the root of your Amazon S3 Bucket. In other words, just the file name in most cases. s2Member will redirect Users/Members to a digitally signed Amazon S3 URL, which allows them access to a particular file via Amazon S3. For further details, please review this section of your Dashboard: <code>s2Member -› Download Options -› Basic Download Restrictions</code>. Also see: <code>s2Member -› Download Options -› Advanced Mod-Rewrite Linkage</code>.</em></p>'."\n";
265
- echo '<p><em><strong>Content Type, Disposition &amp; Inline Files:</strong> The query string parameter <code>&amp;s2member_file_inline=yes</code> DOES work for files served directly through Amazon S3. s2Member DOES have control over the <code>Content-Type</code> and <code>Content-Disposition</code> headers for files being served through Amazon S3. However, Amazon CloudFront servers do NOT automatically determine the MIME type for the objects they serve. If you integrate both Amazon S3 and CloudFront, s2Member will NOT have control over headers. Therefore, when you upload a file to your Amazon S3 Bucket, you should set its Content-Type header. Again, with the Amazon S3/CloudFront combination, you MUST configure headers yourself (such as <code>Content-Type: video/webm</code>, or <code>Content-Disposition: inline|attachment</code>) that you want Amazon CloudFront to send for a particular file. It\'s quite easy. You do this by setting <code>Properties -› Metadata (i.e. headers)</code> on a per-file basis, from inside your Amazon S3 Management Console. In short, when you upload a file to your Amazon S3 Bucket, if you want that file to be served a certain way, be sure to configure its <code>Properties -› Metadata</code> accordingly.</em></p>'."\n";
266
 
267
  echo '<div class="ws-menu-page-hr"></div>'."\n";
268
 
@@ -326,7 +327,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
326
 
327
  echo '<td>'."\n";
328
  echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_amazon_s3_files_access_key" id="ws-plugin--s2member-amazon-s3-files-access-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"]).'" /><br />'."\n";
329
- echo 'See: <code>Amazon Web Services Account -› Security Credentials -› Access Keys</code><br />'."\n";
330
  echo '<em><small>Amazon suggests creating a new IAM user. Use the Keys for that IAM user here.</small></em>'."\n";
331
  echo '</td>'."\n";
332
 
@@ -344,7 +345,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
344
 
345
  echo '<td>'."\n";
346
  echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_amazon_s3_files_secret_key" id="ws-plugin--s2member-amazon-s3-files-secret-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"]).'" /><br />'."\n";
347
- echo 'See: <code>Amazon Web Services Account -› Security Credentials -› Access Keys</code><br />'."\n";
348
  echo '</td>'."\n";
349
 
350
  echo '</tr>'."\n";
@@ -364,20 +365,21 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
364
 
365
  echo '<div class="ws-menu-page-section ws-plugin--s2member-amazon-cf-section">'."\n";
366
  echo '<h3>Amazon S3/CloudFront CDN Storage &amp; Delivery (optional)</h3>'."\n";
 
367
  echo '<a href="http://aws.amazon.com/cloudfront/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/amazon-logo.png" class="ws-menu-page-right" style="width:250px; height:100px; border:0;" alt="." /></a>'."\n";
368
- echo '<p>Please note, all of this is optional. s2Member can be configured to ONLY use Amazon S3 <em>(i.e. without Amazon CloudFront)</em>. Or, s2Member can be configured to use BOTH Amazon S3 and Amazon CloudFront together. If you don\'t want to use Amazon CloudFront, please leave this entire section empty. The configuration options in this section are ONLY required if you are planning to use both Amazon S3 and Amazon CloudFront together.</p>'."\n";
369
- echo '<p>Amazon Simple Storage Service (<a href="http://aws.amazon.com/s3/" target="_blank" rel="external">Amazon S3</a>) combined with <a href="http://aws.amazon.com/cloudfront/" target="_blank" rel="external">Amazon CloudFront</a>. Amazon CloudFront is a web service for content delivery. It integrates with other Amazon Web Services <em>(i.e. Amazon S3 Storage)</em> to give developers and businesses an easy way to distribute content to end users with low latency, and with high data transfer speeds. Amazon CloudFront delivers your static and streaming content using a global network of edge locations. Requests for your Amazon S3 Bucket Objects <em>(i.e. your protected files)</em> are automatically routed to the nearest edge location, so content is delivered with the best possible performance. s2Member has been integrated with both Amazon S3 and with Amazon CloudFront. So <em>(if you wish)</em>, instead of using the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory, you can store all of your protected files inside an Amazon S3 Bucket and serve them via Amazon CloudFront. But again, please understand, the configuration options in this section are ONLY required if you\'re going to use both Amazon S3 &amp; CloudFront together.</p>'."\n";
370
  echo '<p><strong>One of the great things about Amazon CloudFront</strong>, is its ability to <strong>stream/seek media files</strong> in the truest sense of the word. For sites delivering protected <em>FLV/MP4/OGG/WEBM</em> and other streaming audio/video file types over the <em>RTMP</em> protocol, Amazon CloudFront is our recommendation. Once you\'ve successfully configured s2Member to use both Amazon S3 and Amazon CloudFront together, please review the section below regarding <code>JW Player &amp; RTMP Protocol Examples</code>. s2Member will automatically serve your protected files over the <em>RTMP</em> protocol using an Amazon CloudFront Streaming Distribution.</p>'."\n";
371
- echo '<p>If you configure the options below, s2Member will assume all protected files are inside your Amazon S3 Bucket; and the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory is no longer used at all. That being said, all other aspects of s2Member\'s File Download protection remain the same. The only thing that changes, is the location of your protected files. In other words, Basic Download Restrictions, Download Keys, Custom Capability and/or Membership Level Files will all continue to work just as before. The only difference is that s2Member will use your Amazon S3 Bucket, automatically connecting it to both of the Amazon CloudFront Distributions, which s2Member auto-configures for you <em>(see below)</em>. In this way, s2Member uses Amazon CloudFront as a CDN <em>(i.e. Content Delivery Network)</em> for your protected files.</p>'."\n";
372
- echo '<p>s2Member assumes that you\'re creating a new Amazon S3 Bucket, specifically for this installation; and that your Bucket is NOT available publicly. In other words, if you type this URL into your browser <em>(i.e. <code>http://your-bucket-name.s3.amazonaws.com/</code>)</em>, you should get an error that says: <code>Access Denied</code>. That\'s good, that\'s exactly what you want. You can create your Amazon S3 Bucket using the <a href="https://console.aws.amazon.com/s3/home" target="_blank" rel="external">Amazon interface</a>. Or, some people prefer to use this popular Firefox extension (<a href="http://www.s3fox.net/" target="_blank" rel="external">S3 Fox Organizer</a>). You will also need to enable CloudFront inside your Web Services account at Amazon. Don\'t worry about creating or configuring any CloudFront Distributions, s2Member will auto-create and auto-configure those for you, allowing you to serve protected files.</p>'."\n";
373
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_amazon_cf", get_defined_vars());
374
 
375
  echo '<div class="ws-menu-page-hr"></div>'."\n";
376
 
377
- echo '<p><em><strong>Dev Note w/Technical Details:</strong> s2Member\'s auto-configuration routines for Amazon CloudFront (below), are designed to create &amp; configure various components on your Amazon Web Services account, which are all requirements for you to <a href="http://docs.amazonwebservices.com/AmazonCloudFront/2010-11-01/DeveloperGuide/index.html?HowToPrivateContent.html" target="_blank" rel="external">serve protected files through the Amazon S3/CloudFront combination</a>. These components include: an Origin Access Identity, read permissions for the Origin Access Identity, and two private content Distributions. One private content Distribution for file downloads, and another private content Distribution for streaming media files; both connected to and sourced by your Amazon S3 Bucket. In addition, s2Member will automatically configure an ACL &amp; Policy (i.e. permissions) on your Amazon S3 Bucket to make sure your protected object/files are NOT available to the public.</em></p>'."\n";
378
- echo '<p><em><strong>Dev Note w/Technical Details:</strong> s2Member uses "Digitally Signed URLs", authenticated by the Amazon CloudFront API. Documented for developers <a href="http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html" target="_blank" rel="external">here</a>. To put it simply, s2Member will generate Amazon CloudFront URLs (internally); which allow Customers temporary access to specific files inside your S3 Bucket via CloudFront Distributions. s2Member\'s Digitally Signed URLs leading to Amazon S3/CloudFront, give a Customer 24 hours to connect to the file inside your S3 Bucket. This connection period of 24 hours is largely irrelevant when used in combination with s2Member, because access is renewed for another 24 hours each time you make a file available to a User/Member, and they are authenticated by your configuration of s2Member. This connection period of 24 hours is just a secondary line of defense to further prevent the possibility of link sharing. If you need to change this connection timeout of <code>24 hours</code> for some reason (not likely), you can use this WordPress Filter: <code>ws_plugin__s2member_amazon_cf_file_expires_time</code>.</em></p>'."\n";
379
- echo '<p><em><strong>Linking To Protected Files:</strong> RTMP streams are special, but nothing else changes. s2Member\'s integration with Amazon S3/CloudFront serves protected files through the same links that all s2Member site owners use. For example, you might use: <code>'.esc_html(home_url("/?s2member_file_download=example-file.zip")).'</code>, where <strong>s2member_file_download</strong> = the file, relative to the root of your Amazon S3 Bucket. In other words, just the file name in most cases. s2Member will redirect Users/Members to a digitally signed Amazon CloudFront URL, which allows them access to a particular file via Amazon CloudFront. For further details, please review this section of your Dashboard: <code>s2Member -› Download Options -› Basic Download Restrictions</code>. Also see: <code>s2Member -› Download Options -› Advanced Mod-Rewrite Linkage</code>. If you\'re streaming audio/video files over the RTMP protocol, please review the section below: <code>JW Player &amp; RTMP Protocol Examples</code>.</em></p>'."\n";
380
- echo '<p><em><strong>Content Type, Disposition &amp; Inline Files:</strong> An IMPORTANT issue. The query string parameter <code>&amp;s2member_file_inline=yes</code> does NOTHING for files served via Amazon CloudFront. s2Member has NO control over the <code>Content-Type</code> and/or <code>Content-Disposition</code> headers for a file being served through Amazon CloudFront, and CloudFront servers do NOT automatically determine the MIME type for the objects they serve. Therefore, when you upload a file to your Amazon S3 Bucket, you should set its Content-Type header. That is, you MUST configure headers yourself (such as <code>Content-Type: video/webm</code>, or <code>Content-Disposition: inline|attachment</code>) that you want Amazon CloudFront to send for a particular file. It\'s quite easy. You do this by setting <code>Properties -› Metadata (i.e. headers)</code> on a per-file basis, from inside your Amazon S3 Management Console. In short, when you upload a file to your Amazon S3 Bucket, if you want that file to be served a certain way, be sure to configure its <code>Properties -› Metadata</code> accordingly.</em></p>'."\n";
381
  echo (stripos(PHP_OS, "win") === 0 && c_ws_plugin__s2member_utils_conds::is_localhost()) ? '<p><em><strong>Localhost Developers:</strong> s2Member\'s Amazon CloudFront integration requires the <a href="http://php.net/manual/en/function.openssl-sign.php" target="_blank" rel="external">openssl_sign()</a> function in PHP so it can digitially sign CloudFront URLs. This function is sometimes problematic on localhost servers such as WAMP &amp; EasyPHP. We recommend installing <a href="http://www.slproweb.com/products/Win32OpenSSL.html" target="_blank" rel="external">this lightweight alternative for Windows</a> while you\'re developing. s2Member will automatically find it here: <code>C:\OpenSSL-Win[32/64]\bin\openssl.exe</code>.'.((file_exists("c:\openssl-win32\bin\openssl.exe") || file_exists("c:\openssl-win64\bin\openssl.exe")) ? ' <strong class="ws-menu-page-hilite">( s2Member has detected that OpenSSL-Win[32/64] IS installed in the correct location, thank you! )</strong>' : ' <strong class="ws-menu-page-hilite">(s2Member has detected that OpenSSL-Win[32/64] is NOT currently available)</strong>').'</em></p>'."\n" : '';
382
 
383
  if($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_distros_auto_config_status"] === "configured")
@@ -403,7 +405,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
403
 
404
  echo '<td>'."\n";
405
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_amazon_cf_files_private_key_id" id="ws-plugin--s2member-amazon-cf-files-private-key-id" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key_id"]).'" data-s-prev-config-value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key_id"]).'" /><br />'."\n";
406
- echo 'See: <code>Amazon Web Services Account -› Security Credentials -› CloudFront Key Pairs</code>'."\n";
407
  echo '</td>'."\n";
408
 
409
  echo '</tr>'."\n";
@@ -421,7 +423,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
421
  echo '<td>'."\n";
422
  echo '<input type="hidden" name="ws_plugin__s2member_amazon_cf_files_private_key" id="ws-plugin--s2member-amazon-cf-files-private-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"]).'" data-s-prev-config-value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"]).'" />'."\n";
423
  echo '<textarea name="ws_plugin__s2member_amazon_cf_files_private_key_entry" id="ws-plugin--s2member-amazon-cf-files-private-key-entry" rows="3" wrap="off" spellcheck="false">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"]).'</textarea><br />'."\n";
424
- echo 'See: <code>Amazon Web Services Account -› Security Credentials -› CloudFront Key Pairs</code><br />'."\n";
425
  echo '<em>* Note, s2Member needs your <strong>Private Key file</strong>, NOT your Public Key file.</em>'."\n";
426
  echo '</td>'."\n";
427
 
@@ -508,7 +510,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
508
  echo '<div style="float:right; margin:0 0 0 25px;">'."\n";
509
  echo ' <button type="button" onclick="if(confirm(\'Are you sure?\')) location.href = \''.c_ws_plugin__s2member_utils_strings::esc_js_sq(add_query_arg(urlencode_deep(array('ws_plugin__s2member_cf_options_reset' => wp_create_nonce('ws-plugin--s2member-cf-options-reset'))))).'\';">Reset CloudFront Configuration</button>'."\n";
510
  echo '</div>'."\n";
511
- echo '<p>If you need to start all over again, you can click this button to reset your existing s2Member/CloudFront configuration. <em><strong>However, please note:</strong> you will still need to log into your AWS CloudFront Console (at some point) and remove any existing CloudFront Distributions and/or Origin Access Identities that were previously generated with s2Member; i.e. resetting your configuration here will allow you to start over with s2Member using a new set of CF Distros, but it does NOT delete anything on the AWS side.</em></p>'."\n";
512
  echo '</td>'."\n";
513
 
514
  echo '</tr>'."\n";
@@ -524,7 +526,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
524
  {
525
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_before_amazon_s3_comp", get_defined_vars());
526
 
527
- echo '<div class="ws-menu-page-group" title="S3-Compatible Content Delivery (e.g. DreamObjects, etc.)">'."\n";
528
 
529
  echo '<div class="ws-menu-page-section ws-plugin--s2member-amazon-s3-comp-section">'."\n";
530
  echo '<h3>S3-Compatible Content Delivery (optional)</h3>'."\n";
@@ -619,7 +621,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
619
 
620
  echo '<div class="ws-menu-page-section ws-plugin--s2member-rtmp-streaming-section">'."\n";
621
  echo '<h3>JW Player v6 &amp; RTMP Protocol Examples</h3>'."\n";
622
- echo '<a href="http://www.s2member.com/r/jw-player-download/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/jwplayer-logo.png" class="ws-menu-page-right" style="width:179px; height:58px; border:0; border-radius:3px; background:#FFFFFF; padding:15px;" alt="." /></a>'."\n";
623
  echo '<p>While it is possible to serve audio/video files protected by s2Member, without needing to integrate Amazon S3 or CloudFront; we DO highly recommend that you integrate both Amazon S3 and Amazon CloudFront in order to maximize speed and compatibility across various viewing platforms. That being said, there are code samples below that will serve audio/video files both with and without Amazon S3/CloudFront. You can also check the <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Forum URI")).'" target="_blank" rel="external">s2Member Support Forums</a> for tips/tricks if you like.</p>'."\n";
624
  echo '<p><strong>One of the great things about Amazon CloudFront</strong>, is its ability to <strong>stream/seek media files</strong> in the truest sense of the word. For sites delivering protected <em>FLV/MP4/OGG/WEBM</em> and other streaming audio/video file types over the <em>RTMP</em> protocol, Amazon CloudFront is our recommendation. Once you\'ve successfully configured s2Member to use both Amazon S3 and Amazon CloudFront together, please review the code samples below. s2Member can automatically serve your protected files over the <em>RTMP</em> protocol using an Amazon CloudFront Streaming Distribution.</p>'."\n";
625
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/" target="_blank" rel="external">JW Player w/ <code>[s2Stream /]</code> Shortcodes</a>.</p>'."\n";
@@ -629,7 +631,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
629
 
630
  echo '<div class="ws-menu-page-hr"></div>'."\n";
631
 
632
- echo '<h3><code>[s2Stream /]</code> Video Shortcode Examples (recommended it\'s the easiest way)</h3>'."\n";
633
 
634
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp4\').toggle(); return false;" class="ws-dotted-link">JW Player (MP4 file, via Rewrite URLs. Amazon S3/CloudFront NOT required)</a></p>'."\n";
635
  echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp4" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />Works with any audio/video file. This does NOT require s2Member to be integrated with Amazon S3/CloudFront.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-s2stream-mp4.x-php")).'</p>'."\n";
@@ -642,7 +644,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
642
 
643
  echo '<div class="ws-menu-page-hr"></div>'."\n";
644
 
645
- echo '<h3><code>[s2Stream /]</code> Audio Shortcode Examples (recommended it\'s the easiest way)</h3>'."\n";
646
 
647
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp3\').toggle(); return false;" class="ws-dotted-link">JW Player (MP3 file, via Rewrite URLs. Amazon S3/CloudFront NOT required)</a></p>'."\n";
648
  echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp3" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />Works with any audio/video file. This does NOT require s2Member to be integrated with Amazon S3/CloudFront.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-s2stream-mp3.x-php")).'</p>'."\n";
@@ -655,19 +657,19 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
655
 
656
  echo '<div class="ws-menu-page-hr"></div>'."\n";
657
 
658
- echo '<h3>PHP Code Examples (for more advanced integrations via PHP in WordPress themes)</h3>'."\n";
659
 
660
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-standard-mp4\').toggle(); return false;" class="ws-dotted-link">JW Player (MP4 file, via Rewrite URLs. Amazon S3/CloudFront NOT required)</a></p>'."\n";
661
- echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-standard-mp4" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This does NOT require s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <code>s2Member -› Download Options -› Advanced Mod Rewrite Linkage</code>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-standard-mp4.x-php")).'</p>'."\n";
662
 
663
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4\').toggle(); return false;" class="ws-dotted-link">JW Player (RTMP streaming MP4, via s2Member\'s Amazon S3/CloudFront integration)</a></p>'."\n";
664
- echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This requires s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <a href="http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/" target="_blank" rel="external">s2Member Codex -› API Functions</a>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-streaming-mp4.x-php")).'</p>'."\n";
665
 
666
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-sca\').toggle(); return false;" class="ws-dotted-link">JW Player (RTMP streaming MP4, via s2Member\'s JSON/Shortcode alternative)</a></p>'."\n";
667
- echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-sca" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This requires s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <a href="http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/" target="_blank" rel="external">s2Member Codex -› API Functions</a>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-streaming-mp4-sca.x-php")).'</p>'."\n";
668
 
669
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-webm\').toggle(); return false;" class="ws-dotted-link">JW Player (RTMP streaming MP4, advanced w/ multiple fallbacks)</a></p>'."\n";
670
- echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-webm" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This requires s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <a href="http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/" target="_blank" rel="external">s2Member Codex -› API Functions</a>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-streaming-mp4-webm.x-php")).'</p>'."\n";
671
 
672
  echo '</div>'."\n";
673
 
@@ -683,7 +685,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
683
 
684
  echo '<div class="ws-menu-page-section ws-plugin--s2member-rewrite-linkage-section">'."\n";
685
  echo '<h3>Advanced Mod-Rewrite Linkage</h3>'."\n";
686
- echo '<p>s2Member automatically creates <code>mod_rewrite</code> rules inside your <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory, which provide additional flexibility in the way protected files can be served to your Customers. With s2Member\'s <code>mod_rewrite</code> rules, it is now possible to link directly to a protected file, avoiding the use of query string variables <em>(it\'s completely optional though, i.e. NOT required)</em>.</p>'."\n";
687
  echo '<p>This new flexibility may come in handy for site owners serving files through media playback devices that have issues with query string variables. For instance, it is now possible to link to an s2Member-protected file directly, like this: <code>... /wp-content/plugins/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/example-file.zip</code> instead of <code>... /?s2member_file_download=example-file.zip</code>. Either way works, but the direct link might be easier for some.</p>'."\n";
688
  echo '<p>It is also possible to pass query string parameters through a direct link:<br /><code>... /wp-content/plugins/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/example-file.zip?s2member_file_inline=yes&amp;s2member_file_download_key=[key]</code>.</p>'."\n";
689
  echo '<p>That being said, s2Member\'s <code>mod_rewrite</code> rules allow for more advanced control over s2Member-specific parameters.</p>'."\n";
@@ -721,17 +723,17 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
721
  echo '<td style="padding-top:0;">'."\n";
722
  echo '<ul class="ws-menu-page-li-margins">'."\n";
723
  echo '<li><code>download="file.zip"</code> Location of the file, relative to the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory; or, relative to the root of your Amazon S3 Bucket, when applicable.</li>'."\n";
724
- echo '<li><code>download_key="no"</code> Defaults to <code>no</code>. If <code>download_key="1|on|yes|true|ip-forever|universal"</code>, s2Member will return a URL with an s2Member-generated File Download Key. You don\'t need to generate the File Download Key yourself, s2Member does it for you. If you set <code>download_key="ip-forever"</code>, the File Download Key that s2Member generates will last forever, for a specific IP Address; otherwise, by default, all File Download Keys expire after 24 hours automatically. If you set <code>download_key="universal"</code>, s2Member will generate a File Download Key that is good for anyone/everyone forever, with NO restrictions on who/where/when a file is accessed <em>(i.e. be careful with this one)</em>.</li>'."\n";
725
  echo '<li><code>stream="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>stream="1|on|yes|true"</code>, s2Member will return a URL containing a parameter/directive, which forces the File Download to take place over the RTMP protocol if at all possible. This ONLY works when/if s2Member is configured to run with both Amazon S3/CloudFront. Please note however, it\'s better to use the example code provided in the section above, regarding: <code>JW Player and the RTMP Protocol</code>. Also note, if <code>get_streamer_json="1|on|yes|true"</code>, s2Member will automatically force <code>stream="yes"</code> for you.</li>'."\n";
726
- echo '<li><code>inline=""</code> Defaults to <code>[empty]</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>inline="1|on|yes|true"</code>, s2Member will serve the file inline, instead of as an actual File Download. If empty, s2Member will look at your <code>Inline File Extensions</code> configuration above, and serve the file inline; if, and only if, its extension matches one found in your configuration. By default, s2Member serves all files as attachments <em>(i.e. downloads)</em>, except in the case of the <code>[s2Stream /]</code> Shortcode where this defaults to <code>yes</code>. Please read the section above regarding <code>Inline File Extensions</code> for further details. Also note, this Shortcode Attribute does NOTHING for files served via Amazon CloudFront. See the tech-notes listed in the Amazon CloudFront section for further details and workarounds.</li>'."\n";
727
  echo '<li><code>storage=""</code> Defaults to <code>[empty]</code>. If <code>storage="local|s3|cf"</code>, s2Member will serve the file from a specific source location, based on the value of this Shortcode Attribute. For example, if you\'ve configured Amazon S3 and/or CloudFront; but, there are a few files that you want to upload locally to the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory; you can force s2Member to serve a file from local storage by setting <code>storage="local"</code> explicitly.</li>'."\n";
728
  echo '<li><code>remote="no"</code> Defaults to <code>no</code>. If <code>remote="1|on|yes|true"</code>, s2Member will authenticate access to the File Download via Remote Header Authorization, instead of through your web site. This is similar to <code>.htaccess</code> protection routines of yester-year</code>. Please check the <code>Remote Authorization and Podcasting</code> section for further details about how this works.</li>'."\n";
729
- echo '<li><code>ssl=""</code> Defaults to <code>[empty]</code>. If <code>ssl="1|on|yes|true"</code>, s2Member will generate a File Download URL with an SSL protocol <em>(i.e. the URL will start with <code>https://</code> or <code>rtmpe://</code>)</em>. If empty, s2Member will only generate a File Download URL with an SSL protocol, when/if the Post/Page/URL firing the Shortcode itself, is also being viewed over SSL. Otherwise, s2Member will use a non-SSL protocol by default.</li>'."\n";
730
  echo '<li><code>rewrite="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>rewrite="1|on|yes|true"</code>, s2Member will generate a File Download URL that takes full advantage of s2Member\'s Advanced Mod Rewrite functionality. If you\'re running an Apache web server, or another server that supports <code>mod_rewrite</code>, we highly recommend turning this on. s2Member\'s <code>mod_rewrite</code> URLs do NOT contain query string parameters, making them more portable/compatible with other software applications and/or plugins for WordPress. If you\'re integrating with JW Player, you MUST use <code>rewrite="yes"</code>.</li>'."\n";
731
  echo '<li><code>rewrite_base=""</code> Defaults to <code>[empty]</code>. If <code>rewrite_base="'.esc_attr(site_url("/")).'"</code>, s2Member will generate a File Download URL that takes full advantage of s2Member\'s Advanced Mod Rewrite functionality, and it will use the rewrite base URL as a prefix. This could be useful on some WordPress installations that use advanced directory structures. It could also be useful for site owners using virtual directories that point to <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code>. Note, if <code>rewrite_base</code> is set, s2Member will automatically force <code>rewrite="yes"</code> for you.</li>'."\n";
732
  echo '<li><code>skip_confirmation="no"</code> Defaults to <code>no</code>. If <code>skip_confirmation="1|on|yes|true"</code>, s2Member will generate a File Download URL which contains a directive, telling s2Member NOT to introduce any JavaScript confirmation prompts on your site, for this File Download URL. Please note, s2Member will automatically detect links, anywhere in your content, and/or anywhere in your theme files, that contain <code>s2member_file_download</code> or <code>s2member-files</code>. Whenever a logged-in Member clicks a link that contains <code>s2member_file_download</code> or <code>s2member-files</code>, the system will politely ask the User to confirm the download using a very intuitive JavaScript confirmation prompt, which contains specific details about your configured download limitations. This way your Members will be aware of how many files they\'ve downloaded in the current period; and they\'ll be able to make a conscious decision about whether to proceed with a specific download or not.</li>'."\n";
733
- echo '<li><code>url_to_storage_source="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>url_to_storage_source="1|on|yes|true"</code>, s2Member will generate a File Download URL which points directly to the storage source. This is only functional with Amazon S3 and/or CloudFront integrations. If you create a URL that points directly to the storage source <em>(i.e. points directly to Amazon S3 or CloudFront)</em>, s2Member will NOT be able to further authenticate the current User/Member; and, s2Member will NOT be able to count the File Download against the current User\'s account record, because the URL being generated does not pass back through s2Member at all, it points directly to the storage source. For this reason, if you set <code>url_to_storage_source="true"</code>, you should also set <code>check_user="true"</code> and <code>count_against_user="true"</code>, telling s2Member to authenticate the current User, and if authenticated, count this File Download URL against the current User\'s account record in real-time <em>(i.e. as the URL is being generated) </em>, while it still has a chance to do so. This Shortcode Attribute is useful when you stream files over the RTMP protocol; where an <code>http://</code> URL is not feasible. It also helps in situations where a 3rd-party software application will not work as intended, with s2Member\'s internal redirection to Amazon S3/CloudFront files. Important, when <code>check_user="true"</code> and/or <code>count_against_user="true"</code>, the Shortcode will return an empty and/or null object value in situations where the current User/Member does NOT have access to the file.</li>'."\n";
734
- echo '<li><code>count_against_user="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>count_against_user="1|on|yes|true"</code>, it will automatically force <code>check_user="true"</code> as well. In other words, s2Member will authenticate the current User, and if authenticated, count this File Download URL against the current User\'s account record in real-time <em>(i.e. as the URL is being generated) </em>. This is off by default with the <code>[s2File /]</code> Shortcode. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, count the File Download against their account record, and serve the File Download. In other words, under normal circumstances, there is no reason to set <code>check_user="true"</code> and/or <code>count_against_user="true"</code> when generating the URL itself. However, this is a useful Shortcode Attribute when <code>url_to_storage_source="true"</code>. Please note, when <code>check_user="true"</code> and/or <code>count_against_user="true"</code>, the Shortcode will return an empty and/or null object value in situations where the current User/Member does NOT have access to the file.</li>'."\n";
735
  echo '<li><code>check_user="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>check_user="1|on|yes|true"</code>, s2Member will authenticate the current User before allowing the File Download URL to be generated. This is off by default with the <code>[s2File /]</code> Shortcode. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, and serve the File Download to the User/Member. In other words, under normal circumstances, there is no reason to set <code>check_user="true"</code> and/or <code>count_against_user="true"</code> when generating the URL itself. However, this IS a useful Shortcode Attribute when <code>url_to_storage_source="true"</code>. Please note, when <code>check_user="true"</code> and/or <code>count_against_user="true"</code>, the Shortcode will return an empty and/or null object value in situations where the current User/Member does NOT have access to the file.</li>'."\n";
736
  echo '<li><code>get_streamer_json="no"</code> Defaults to <code>no</code>. N/A with <code>[s2Stream /]</code> Shortcode. If <code>get_streamer_json="1|on|yes|true"</code>, the <code>[s2File /]</code> Shortcode will return a JSON object for JavaScript notation, making it possible to integrate the <code>[s2File /]</code> Shortcode into JavaScript routines that configure streaming media players. For further details, please review the section above: <code>JW Player &amp; RTMP Protocol Examples</code>. Note, if you set <code>get_streamer_json="true"</code>, s2Member will automatically force <code>url_to_storage_source="true"</code> and <code>stream="true"</code>. For that reason, you should carefully review the details and warning above regarding <code>url_to_storage_source</code>. If you set <code>get_streamer_json="true"</code>, you should also set <code>check_user="true"</code> and <code>count_against_user="true"</code>.</li>'."\n";
737
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_shortcode_attrs_s2file_lis", get_defined_vars());
@@ -756,8 +758,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
756
  echo '<li><code>file_download="video.mp4"</code> Location of the audio/video file, relative to the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory; or, relative to the root of your Amazon S3 Bucket, when applicable.</li>'."\n";
757
  echo '<li><code>player="jwplayer-v6-rtmp"</code> Required. Current supported players in this Shortcode include: <code>jwplayer-v6</code> (works with any audio/video file, and you do NOT need to have Amazon S3 or CloudFront integrated for this to work), <code>jwplayer-v6-rtmp</code> (streams with the RTMP protocol, plus there is a full download fallback of the source file if streaming is not possible on a particular device; this requires both Amazon S3 and CloudFront integration), <code>jwplayer-v6-rtmp-only</code> (streams with the RTMP protocol only, with no access to the source file, only to the RTMP stream; this requires both Amazon S3 and CloudFront integration).</li>'."\n";
758
  echo '<li><code>player_id=""</code> Optional. HTML div ID for the audio/video player. Defaults to a unique ID generated by s2Member for each instance of your Shortcode.</li>'."\n";
759
- echo '<li><code>player_path="/jwplayer/jwplayer.js"</code> Required. Path to the player\'s JavaScript file (ex: <code>/jwplayer/jwplayer.js</code> — you should upload the <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">/jwplayer</a> folder to the root of your web directory).</li>'."\n";
760
- echo '<li><code>player_resolutions=""</code> Optional (requires s2Member Pro). This is a comma-delimited list of all available resolution options (should you decide to offer more than just one file download at a single resolution). Please review the full list of Shortcode Attributes (i.e. click the "Shortcode Attributes (Explained)" tab) in <a href="http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes" target="_blank" rel="external">this KB article</a> for further details, requirements, and an example of use.</li>'."\n";
761
  echo '<li><code>player_{setting}=""</code> Optional. Any additional configuration attributes supported by your audio/video player, prefixed with <code>player_</code>. For JW Player v6, see <a href="http://www.s2member.com/r/jw-player-config-options/" target="_blank" rel="external">this article please</a>. Examples: <code>player_width="480"</code>, <code>player_height="270"</code>, <code>player_title="My Video"</code>, <code>player_description="A video about something."</code>, <code>player_image="http://www.example.com/wp-content/uploads/video-preview.jpg"</code>, <code>player_mediaid="video_ei0wsx23"</code>, <code>player_autostart="true"</code>, <code>player_skin="/jwplayer/my-skin.xml"</code>, <code>player_key="my-license-key"</code>, <code>player_captions="{file:\'/assets/captions-en.vtt\',label:\'English\'}"</code> (<em>With <a href="http://www.s2member.com/r/jw-player-video-captions/" target="_blank" rel="external">Captions</a>, you can exclude the square array brackets to avoid Shortcode parsing issues. s2Member will automatically wrap your Caption objects with square array brackets.</em>). Please note that "Advanced Options Blocks" listed on <a href="http://www.s2member.com/r/jw-player-config-options/" target="_blank" rel="external">this page</a> are NOT supported here. For those, please use: <code>player_option_blocks=""</code> (see below).</li>'."\n";
762
  echo '<li><code>player_option_blocks=""</code> Optional. Any "Advanced Option Blocks" supported by your audio/video player. For JW Player v6, see <a href="http://www.s2member.com/r/jw-player-config-options/" target="_blank" rel="external">this article please</a>. Here are some examples: <code>player_option_blocks="sharing:{}"</code>, <code>player_option_blocks="sharing:{}, logo: {file: \'/logo.png\', link: \'http://example.com\'}"</code>. Or: <code>player_option_blocks="c2hhcmluZzoge30="</code> (base64 encoded version of <code>sharing:{}</code>). Please note that "Advanced Options Blocks" can be defined in plain text or with a <a href="http://www.base64encode.org/" target="_blank" rel="external">base64 encoded string</a>. Advanced Option Blocks are JavaScript objects with properties. If you have trouble defining JavaScript object properties inside a Shortcode Attribute, please use <a href="http://www.base64encode.org/" target="_blank" rel="external">this tool</a> to base64 encode your Advanced Option Blocks, so that you end up with a string that\'s compatible with Shortcode Attributes.</li>'."\n";
763
  echo '<li>Please check the <strong>Shortcode Attributes</strong> Tab in <a href="http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes" target="_blank" rel="external">this KB article</a> for further details on everything here.</li>'."\n";
@@ -802,7 +804,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
802
 
803
  echo '<div class="ws-menu-page-section ws-plugin--s2member-gzip-conflicts-section">'."\n";
804
  echo '<h3>Preventing GZIP Conflicts On Server (Instructions)</h3>'."\n";
805
- echo '<p>Protected files served by s2Member through PHP scripts, are already compressed. Therefore, <a href="http://code.google.com/speed/articles/gzip.html" target="_blank" rel="nofollow external xlink">GZIP compression</a> is not needed during protected file delivery. Some web servers (i.e. Apache, LiteSpeed, and similar) include GZIP compression rules through server-side extensions, like <code>mod_deflate</code> for example. While s2Member encourages the use of extensions like <code>mod_deflate</code>, it is best to disable GZIP automatically (i.e. temporarily) during s2Member\'s delivery of a protected file through a PHP script. This avoids conflicts on the server which might otherwise lead to corrupted file downloads. s2Member makes a valiant effort to accomplish this via PHP, all on its own. However, it never hurts to add this section of code to the root <code>.htaccess</code> file for your WordPress installation. Optional, but highly recommended.</p>'."\n";
806
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_gzip_conflicts", get_defined_vars());
807
 
808
  echo '<div class="ws-menu-page-hr"></div>'."\n";
@@ -853,7 +855,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
853
 
854
  echo '<div class="ws-menu-page-section ws-plugin--s2member-custom-capability-files-section">'."\n";
855
  echo '<h3>Restricting Files, Based On Custom Capabilities</h3>'."\n";
856
- echo '<p>If you\'re NOT familiar with Custom Capabilities yet, please read: <code>Dashboard -› s2Member -› API Scripting -› Custom Capability Packages</code>. Once you understand the basic concept of Custom Capabilities &amp; Protected File Downloads, you\'ll see that (by default) s2Member does NOT handle File Download Protection with respect to Custom Capabilities. That\'s where Custom Capability Sub-directories come in.</p>'."\n";
857
  echo '<p>You can create Custom Capability Sub-directories under: <code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'</code>. For instance, if you have a Custom Capability <code>music</code>, you can place protected files that should ONLY be accessible to Members with <code>access_s2member_ccap_music</code>, inside: <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/access-s2member-ccap-music/</code>. Some examples are provided below.</p>'."\n";
858
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_custom_capability_files", get_defined_vars());
859
 
@@ -882,8 +884,6 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_down_ops"))
882
  }
883
  do_action("ws_plugin__s2member_during_down_ops_page_after_left_sections", get_defined_vars());
884
 
885
- echo '<div class="ws-menu-page-hr"></div>'."\n";
886
-
887
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
888
 
889
  echo '</form>'."\n";
42
  echo '<tr class="ws-menu-page-table-tr">'."\n";
43
  echo '<td class="ws-menu-page-table-l">'."\n";
44
 
45
+ echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" action="'.esc_attr(remove_query_arg('ws_plugin__s2member_cf_options_reset')).'" autocomplete="off">'."\n";
46
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
  echo '<input type="hidden" name="ws_plugin__s2member_amazon_cf_files_distros_auto_config_status" id="ws-plugin--s2member-amazon-cf-files-distros-auto-config-status" value="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_distros_auto_config_status"]).'" />'."\n";
48
  echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
58
  echo '<div class="ws-menu-page-section ws-plugin--s2member-restrictions-section">'."\n";
59
  echo '<h3>File Download Restrictions (required, if providing access to protected files)</h3>'."\n";
60
  echo '<p>If your Membership offering allows access to restricted files, you\'ll want to configure these options.</p>'."\n";
61
+ echo '<p class="info"><strong>NOTE:</strong> If you intend to offer File Downloads in one way or another, you must configure at least one of the options below. For security purposes, s2Member\'s File Download functionality is disabled unless &amp; until at least one of the options below have been configured; i.e., s2Member expects you to configure Basic Downloads for at least one Membership Level before any sort of download-related functionality will work. This includes functionality associated with the <code>[s2File /]</code> and <code>[s2Stream /]</code> Shortcodes also.</p>'."\n";
62
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_restrictions", get_defined_vars());
63
 
64
  echo '<div class="ws-menu-page-hr"></div>'."\n";
94
  echo '<td>'."\n";
95
  echo '<input type="text" maxlength="9" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_file_downloads_allowed" id="ws-plugin--s2member-level'.$n.'-file-downloads-allowed" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_file_downloads_allowed"]).'" style="width:200px;" /> every <input type="text" maxlength="3" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_file_downloads_allowed_days" id="ws-plugin--s2member-level'.$n.'-file-downloads-allowed-days" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_file_downloads_allowed_days"]).'" style="width:200px;" onkeyup="if(this.value > 365){ alert(\'(365 days is the maximum).\\nThis keeps the logs optimized.\'); this.value = 365; }" /> day(s).<br />'."\n";
96
  echo 'Only this many unique downloads will be permitted every X day(s), at '.(($n === $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]) ? 'highest Level #'.$n : 'Level #'.$n.' or higher').'.<br />'."\n";
97
+ echo '<em>* To allow UNLIMITED downloads, use: <code>999999999</code> (i.e., <code>999999999</code> = unlimited).</em>'."\n";
98
  echo '</td>'."\n";
99
 
100
  echo '</tr>'."\n";
251
 
252
  echo '<div class="ws-menu-page-section ws-plugin--s2member-amazon-s3-section">'."\n";
253
  echo '<h3>Amazon S3/CDN Storage &amp; Delivery (optional)</h3>'."\n";
254
+ echo '<iframe width="560" height="315" src="https://www.youtube.com/embed/Gr87ZBJQE0I" frameborder="0" allowscriptaccess="always" allowfullscreen="true" style="float:right; margin:0 0 20px 20px; width:300px; height:200px;"></iframe>'."\n";
255
  echo '<a href="http://aws.amazon.com/s3/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/amazon-logo.png" class="ws-menu-page-right" style="width:250px; height:100px; border:0;" alt="." /></a>'."\n";
256
+ echo '<p>Please note, all of this is optional. s2Member can be configured here to ONLY use Amazon S3 <em>(i.e., without Amazon CloudFront)</em>. Or, s2Member can be configured to use BOTH Amazon S3 and Amazon CloudFront together. If you want to use Amazon S3 Storage, but you don\'t care about Amazon CloudFront, feel free to leave the entire Amazon CloudFront section empty. The configuration options in the Amazon CloudFront section are ONLY required if you are planning to use both Amazon S3 and Amazon CloudFront together.</p>'."\n";
257
  echo '<p>Amazon Simple Storage Service (<a href="http://aws.amazon.com/s3/" target="_blank" rel="external">Amazon S3</a>). Amazon S3 is storage for the Internet. It is designed to make web-scale computing easier for developers. Amazon S3 provides a simple web services interface that can be used to store and retrieve any amount of data, at any time, from anywhere on the web. It gives developers access to the same highly scalable, reliable, secure, fast, inexpensive infrastructure that Amazon uses to run its own global network of web sites. s2Member has been integrated with Amazon S3, so that <em>(if you wish)</em>, instead of using the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory, you can store all of your protected files inside an Amazon S3 Bucket.</p>'."\n";
258
+ echo '<p>If you configure the options below, s2Member will assume all protected files are inside your Amazon S3 Bucket; and the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory is no longer used at all. That being said, all other aspects of s2Member\'s File Download protection remain the same. The only thing that changes, is the location of your protected files. In other words, Basic Download Restrictions, Download Keys, Inline Extensions, Custom Capability and/or Membership Level Files will all continue to work just as before. The only difference is that s2Member will use your Amazon S3 Bucket as a CDN <em>(i.e., Content Delivery Network)</em> instead of the local <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory.</p>'."\n";
259
+ echo '<p>s2Member assumes that you\'re creating a new Amazon S3 Bucket, specifically for this installation; and that your Bucket is NOT available publicly. In other words, if you type this URL into your browser <em>(i.e., <code>http://your-bucket-name.s3.amazonaws.com/</code>)</em>, you should get an error that says: <code>Access Denied</code>. That\'s good, that\'s exactly what you want. You can create your Amazon S3 Bucket using the <a href="https://console.aws.amazon.com/s3/home" target="_blank" rel="external">Amazon interface</a>. Or, some people prefer to use this popular Firefox extension (<a href="http://www.s3fox.net/" target="_blank" rel="external">S3 Fox Organizer</a>).</p>'."\n";
260
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_amazon_s3", get_defined_vars());
261
 
262
  echo '<div class="ws-menu-page-hr"></div>'."\n";
263
 
264
  echo '<p><em><strong>Dev Note w/Technical Details:</strong> s2Member uses "Digitally Signed URLs", authenticated by the Amazon S3 API. Documented for developers <a href="http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html" target="_blank" rel="external">here</a>. To put it simply, s2Member will generate Amazon S3 URLs (internally); which allow Customers temporary access to specific files inside your S3 Bucket. s2Member\'s Digitally Signed URLs leading to Amazon S3, give a Customer 24 hours to connect to the file inside your S3 Bucket. This connection period of 24 hours is largely irrelevant when used in combination with s2Member, because access is renewed for another 24 hours each time you make a file available to a User/Member, and they are authenticated by your configuration of s2Member. This connection period of 24 hours is just a secondary line of defense to further prevent the possibility of link sharing. If you need to change this connection timeout of <code>24 hours</code> for some reason (not likely), you can use this WordPress Filter: <code>ws_plugin__s2member_amazon_s3_file_expires_time</code>.</em></p>'."\n";
265
+ echo '<p><em><strong>Linking To Protected Files:</strong> Nothing changes. s2Member\'s integration with Amazon S3 serves protected files through the same links that all s2Member site owners use. For example, you might use: <code>'.esc_html(home_url("/?s2member_file_download=example-file.zip")).'</code>, where <strong>s2member_file_download</strong> = the file, relative to the root of your Amazon S3 Bucket. In other words, just the file name in most cases. s2Member will redirect Users/Members to a digitally signed Amazon S3 URL, which allows them access to a particular file via Amazon S3. For further details, please review this section of your Dashboard: <strong>s2Member Download Options Basic Download Restrictions</strong>. Also see: <strong>s2Member Download Options Advanced Mod-Rewrite Linkage</strong>.</em></p>'."\n";
266
+ echo '<p><em><strong>Content Type, Disposition &amp; Inline Files:</strong> The query string parameter <code>&amp;s2member_file_inline=yes</code> DOES work for files served directly through Amazon S3. s2Member DOES have control over the <code>Content-Type</code> and <code>Content-Disposition</code> headers for files being served through Amazon S3. However, Amazon CloudFront servers do NOT automatically determine the MIME type for the objects they serve. If you integrate both Amazon S3 and CloudFront, s2Member will NOT have control over headers. Therefore, when you upload a file to your Amazon S3 Bucket, you should set its Content-Type header. Again, with the Amazon S3/CloudFront combination, you MUST configure headers yourself (such as <code>Content-Type: video/webm</code>, or <code>Content-Disposition: inline|attachment</code>) that you want Amazon CloudFront to send for a particular file. It\'s quite easy. You do this by setting <strong>Properties Metadata (i.e., headers)</strong> on a per-file basis, from inside your Amazon S3 Management Console. In short, when you upload a file to your Amazon S3 Bucket, if you want that file to be served a certain way, be sure to configure its <strong>Properties Metadata</strong> accordingly.</em></p>'."\n";
267
 
268
  echo '<div class="ws-menu-page-hr"></div>'."\n";
269
 
327
 
328
  echo '<td>'."\n";
329
  echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_amazon_s3_files_access_key" id="ws-plugin--s2member-amazon-s3-files-access-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"]).'" /><br />'."\n";
330
+ echo 'See: <strong>Amazon Web Services Account Security Credentials Access Keys</strong><br />'."\n";
331
  echo '<em><small>Amazon suggests creating a new IAM user. Use the Keys for that IAM user here.</small></em>'."\n";
332
  echo '</td>'."\n";
333
 
345
 
346
  echo '<td>'."\n";
347
  echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_amazon_s3_files_secret_key" id="ws-plugin--s2member-amazon-s3-files-secret-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"]).'" /><br />'."\n";
348
+ echo 'See: <strong>Amazon Web Services Account Security Credentials Access Keys</strong><br />'."\n";
349
  echo '</td>'."\n";
350
 
351
  echo '</tr>'."\n";
365
 
366
  echo '<div class="ws-menu-page-section ws-plugin--s2member-amazon-cf-section">'."\n";
367
  echo '<h3>Amazon S3/CloudFront CDN Storage &amp; Delivery (optional)</h3>'."\n";
368
+ echo '<iframe width="560" height="315" src="https://www.youtube.com/embed/Gr87ZBJQE0I" frameborder="0" allowscriptaccess="always" allowfullscreen="true" style="float:right; margin:0 0 20px 20px; width:300px; height:200px;"></iframe>'."\n";
369
  echo '<a href="http://aws.amazon.com/cloudfront/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/amazon-logo.png" class="ws-menu-page-right" style="width:250px; height:100px; border:0;" alt="." /></a>'."\n";
370
+ echo '<p>Please note, all of this is optional. s2Member can be configured to ONLY use Amazon S3 <em>(i.e., without Amazon CloudFront)</em>. Or, s2Member can be configured to use BOTH Amazon S3 and Amazon CloudFront together. If you don\'t want to use Amazon CloudFront, please leave this entire section empty. The configuration options in this section are ONLY required if you are planning to use both Amazon S3 and Amazon CloudFront together.</p>'."\n";
371
+ echo '<p>Amazon Simple Storage Service (<a href="http://aws.amazon.com/s3/" target="_blank" rel="external">Amazon S3</a>) combined with <a href="http://aws.amazon.com/cloudfront/" target="_blank" rel="external">Amazon CloudFront</a>. Amazon CloudFront is a web service for content delivery. It integrates with other Amazon Web Services <em>(i.e., Amazon S3 Storage)</em> to give developers and businesses an easy way to distribute content to end users with low latency, and with high data transfer speeds. Amazon CloudFront delivers your static and streaming content using a global network of edge locations. Requests for your Amazon S3 Bucket Objects <em>(i.e., your protected files)</em> are automatically routed to the nearest edge location, so content is delivered with the best possible performance. s2Member has been integrated with both Amazon S3 and with Amazon CloudFront. So <em>(if you wish)</em>, instead of using the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory, you can store all of your protected files inside an Amazon S3 Bucket and serve them via Amazon CloudFront. But again, please understand, the configuration options in this section are ONLY required if you\'re going to use both Amazon S3 &amp; CloudFront together.</p>'."\n";
372
  echo '<p><strong>One of the great things about Amazon CloudFront</strong>, is its ability to <strong>stream/seek media files</strong> in the truest sense of the word. For sites delivering protected <em>FLV/MP4/OGG/WEBM</em> and other streaming audio/video file types over the <em>RTMP</em> protocol, Amazon CloudFront is our recommendation. Once you\'ve successfully configured s2Member to use both Amazon S3 and Amazon CloudFront together, please review the section below regarding <code>JW Player &amp; RTMP Protocol Examples</code>. s2Member will automatically serve your protected files over the <em>RTMP</em> protocol using an Amazon CloudFront Streaming Distribution.</p>'."\n";
373
+ echo '<p>If you configure the options below, s2Member will assume all protected files are inside your Amazon S3 Bucket; and the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory is no longer used at all. That being said, all other aspects of s2Member\'s File Download protection remain the same. The only thing that changes, is the location of your protected files. In other words, Basic Download Restrictions, Download Keys, Custom Capability and/or Membership Level Files will all continue to work just as before. The only difference is that s2Member will use your Amazon S3 Bucket, automatically connecting it to both of the Amazon CloudFront Distributions, which s2Member auto-configures for you <em>(see below)</em>. In this way, s2Member uses Amazon CloudFront as a CDN <em>(i.e., Content Delivery Network)</em> for your protected files.</p>'."\n";
374
+ echo '<p>s2Member assumes that you\'re creating a new Amazon S3 Bucket, specifically for this installation; and that your Bucket is NOT available publicly. In other words, if you type this URL into your browser <em>(i.e., <code>http://your-bucket-name.s3.amazonaws.com/</code>)</em>, you should get an error that says: <code>Access Denied</code>. That\'s good, that\'s exactly what you want. You can create your Amazon S3 Bucket using the <a href="https://console.aws.amazon.com/s3/home" target="_blank" rel="external">Amazon interface</a>. Or, some people prefer to use this popular Firefox extension (<a href="http://www.s3fox.net/" target="_blank" rel="external">S3 Fox Organizer</a>). You will also need to enable CloudFront inside your Web Services account at Amazon. Don\'t worry about creating or configuring any CloudFront Distributions, s2Member will auto-create and auto-configure those for you, allowing you to serve protected files.</p>'."\n";
375
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_amazon_cf", get_defined_vars());
376
 
377
  echo '<div class="ws-menu-page-hr"></div>'."\n";
378
 
379
+ echo '<p><em><strong>Dev Note w/Technical Details:</strong> s2Member\'s auto-configuration routines for Amazon CloudFront (below), are designed to create &amp; configure various components on your Amazon Web Services account, which are all requirements for you to <a href="http://docs.amazonwebservices.com/AmazonCloudFront/2010-11-01/DeveloperGuide/index.html?HowToPrivateContent.html" target="_blank" rel="external">serve protected files through the Amazon S3/CloudFront combination</a>. These components include: an Origin Access Identity, read permissions for the Origin Access Identity, and two private content Distributions. One private content Distribution for file downloads, and another private content Distribution for streaming media files; both connected to and sourced by your Amazon S3 Bucket. In addition, s2Member will automatically configure an ACL &amp; Policy (i.e., permissions) on your Amazon S3 Bucket to make sure your protected object/files are NOT available to the public.</em></p>'."\n";
380
+ echo '<p><em><strong>Dev Note w/Technical Details:</strong> s2Member uses "Digitally Signed URLs", authenticated by the Amazon CloudFront API. Documented for developers <a href="http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html" target="_blank" rel="external">here</a>. To put it simply, s2Member will generate Amazon CloudFront URLs (internally); which allow Customers temporary access to specific files inside your S3 Bucket—via CloudFront Distributions. s2Member\'s Digitally Signed URLs leading to Amazon S3/CloudFront, give a Customer 24 hours to connect to the file inside your S3 Bucket. This connection period of 24 hours is largely irrelevant when used in combination with s2Member, because access is renewed for another 24 hours each time you make a file available to a User/Member, and they are authenticated by your configuration of s2Member. This connection period of 24 hours is just a secondary line of defense to further prevent the possibility of link sharing. If you need to change this connection timeout of <code>24 hours</code> for some reason (not likely), you can use this WordPress Filter: <code>ws_plugin__s2member_amazon_cf_file_expires_time</code>.</em></p>'."\n";
381
+ echo '<p><em><strong>Linking To Protected Files:</strong> RTMP streams are special, but nothing else changes. s2Member\'s integration with Amazon S3/CloudFront serves protected files through the same links that all s2Member site owners use. For example, you might use: <code>'.esc_html(home_url("/?s2member_file_download=example-file.zip")).'</code>, where <strong>s2member_file_download</strong> = the file, relative to the root of your Amazon S3 Bucket. In other words, just the file name in most cases. s2Member will redirect Users/Members to a digitally signed Amazon CloudFront URL, which allows them access to a particular file via Amazon CloudFront. For further details, please review this section of your Dashboard: <strong>s2Member Download Options Basic Download Restrictions</strong>. Also see: <strong>s2Member Download Options Advanced Mod-Rewrite Linkage</strong>. If you\'re streaming audio/video files over the RTMP protocol, please review the section below: <code>JW Player &amp; RTMP Protocol Examples</code>.</em></p>'."\n";
382
+ echo '<p><em><strong>Content Type, Disposition &amp; Inline Files:</strong> An IMPORTANT issue. The query string parameter <code>&amp;s2member_file_inline=yes</code> does NOTHING for files served via Amazon CloudFront. s2Member has NO control over the <code>Content-Type</code> and/or <code>Content-Disposition</code> headers for a file being served through Amazon CloudFront, and CloudFront servers do NOT automatically determine the MIME type for the objects they serve. Therefore, when you upload a file to your Amazon S3 Bucket, you should set its Content-Type header. That is, you MUST configure headers yourself (such as <code>Content-Type: video/webm</code>, or <code>Content-Disposition: inline|attachment</code>) that you want Amazon CloudFront to send for a particular file. It\'s quite easy. You do this by setting <strong>Properties Metadata (i.e., headers)</strong> on a per-file basis, from inside your Amazon S3 Management Console. In short, when you upload a file to your Amazon S3 Bucket, if you want that file to be served a certain way, be sure to configure its <strong>Properties Metadata</strong> accordingly.</em></p>'."\n";
383
  echo (stripos(PHP_OS, "win") === 0 && c_ws_plugin__s2member_utils_conds::is_localhost()) ? '<p><em><strong>Localhost Developers:</strong> s2Member\'s Amazon CloudFront integration requires the <a href="http://php.net/manual/en/function.openssl-sign.php" target="_blank" rel="external">openssl_sign()</a> function in PHP so it can digitially sign CloudFront URLs. This function is sometimes problematic on localhost servers such as WAMP &amp; EasyPHP. We recommend installing <a href="http://www.slproweb.com/products/Win32OpenSSL.html" target="_blank" rel="external">this lightweight alternative for Windows</a> while you\'re developing. s2Member will automatically find it here: <code>C:\OpenSSL-Win[32/64]\bin\openssl.exe</code>.'.((file_exists("c:\openssl-win32\bin\openssl.exe") || file_exists("c:\openssl-win64\bin\openssl.exe")) ? ' <strong class="ws-menu-page-hilite">( s2Member has detected that OpenSSL-Win[32/64] IS installed in the correct location, thank you! )</strong>' : ' <strong class="ws-menu-page-hilite">(s2Member has detected that OpenSSL-Win[32/64] is NOT currently available)</strong>').'</em></p>'."\n" : '';
384
 
385
  if($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_distros_auto_config_status"] === "configured")
405
 
406
  echo '<td>'."\n";
407
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_amazon_cf_files_private_key_id" id="ws-plugin--s2member-amazon-cf-files-private-key-id" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key_id"]).'" data-s-prev-config-value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key_id"]).'" /><br />'."\n";
408
+ echo 'See: <strong>Amazon Web Services Account Security Credentials CloudFront Key Pairs</strong>'."\n";
409
  echo '</td>'."\n";
410
 
411
  echo '</tr>'."\n";
423
  echo '<td>'."\n";
424
  echo '<input type="hidden" name="ws_plugin__s2member_amazon_cf_files_private_key" id="ws-plugin--s2member-amazon-cf-files-private-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"]).'" data-s-prev-config-value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"]).'" />'."\n";
425
  echo '<textarea name="ws_plugin__s2member_amazon_cf_files_private_key_entry" id="ws-plugin--s2member-amazon-cf-files-private-key-entry" rows="3" wrap="off" spellcheck="false">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"]).'</textarea><br />'."\n";
426
+ echo 'See: <strong>Amazon Web Services Account Security Credentials CloudFront Key Pairs</strong><br />'."\n";
427
  echo '<em>* Note, s2Member needs your <strong>Private Key file</strong>, NOT your Public Key file.</em>'."\n";
428
  echo '</td>'."\n";
429
 
510
  echo '<div style="float:right; margin:0 0 0 25px;">'."\n";
511
  echo ' <button type="button" onclick="if(confirm(\'Are you sure?\')) location.href = \''.c_ws_plugin__s2member_utils_strings::esc_js_sq(add_query_arg(urlencode_deep(array('ws_plugin__s2member_cf_options_reset' => wp_create_nonce('ws-plugin--s2member-cf-options-reset'))))).'\';">Reset CloudFront Configuration</button>'."\n";
512
  echo '</div>'."\n";
513
+ echo '<p>If you need to start all over again, you can click this button to reset your existing s2Member/CloudFront configuration. <em><strong>However, please note:</strong> you will still need to log into your AWS CloudFront Console (at some point) and remove any existing CloudFront Distributions and/or Origin Access Identities that were previously generated with s2Member; i.e., resetting your configuration here will allow you to start over with s2Member using a new set of CF Distros, but it does NOT delete anything on the AWS side.</em></p>'."\n";
514
  echo '</td>'."\n";
515
 
516
  echo '</tr>'."\n";
526
  {
527
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_before_amazon_s3_comp", get_defined_vars());
528
 
529
+ echo '<div class="ws-menu-page-group" title="S3-Compatible Content Delivery (e.g., DreamObjects, etc.)">'."\n";
530
 
531
  echo '<div class="ws-menu-page-section ws-plugin--s2member-amazon-s3-comp-section">'."\n";
532
  echo '<h3>S3-Compatible Content Delivery (optional)</h3>'."\n";
621
 
622
  echo '<div class="ws-menu-page-section ws-plugin--s2member-rtmp-streaming-section">'."\n";
623
  echo '<h3>JW Player v6 &amp; RTMP Protocol Examples</h3>'."\n";
624
+ echo '<iframe width="560" height="315" src="https://www.youtube.com/embed/ZTopRQQAELw" frameborder="0" allowscriptaccess="always" allowfullscreen="true" style="float:right; margin:0 0 20px 20px; width:300px; height:200px;"></iframe>'."\n";
625
  echo '<p>While it is possible to serve audio/video files protected by s2Member, without needing to integrate Amazon S3 or CloudFront; we DO highly recommend that you integrate both Amazon S3 and Amazon CloudFront in order to maximize speed and compatibility across various viewing platforms. That being said, there are code samples below that will serve audio/video files both with and without Amazon S3/CloudFront. You can also check the <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Forum URI")).'" target="_blank" rel="external">s2Member Support Forums</a> for tips/tricks if you like.</p>'."\n";
626
  echo '<p><strong>One of the great things about Amazon CloudFront</strong>, is its ability to <strong>stream/seek media files</strong> in the truest sense of the word. For sites delivering protected <em>FLV/MP4/OGG/WEBM</em> and other streaming audio/video file types over the <em>RTMP</em> protocol, Amazon CloudFront is our recommendation. Once you\'ve successfully configured s2Member to use both Amazon S3 and Amazon CloudFront together, please review the code samples below. s2Member can automatically serve your protected files over the <em>RTMP</em> protocol using an Amazon CloudFront Streaming Distribution.</p>'."\n";
627
  echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/" target="_blank" rel="external">JW Player w/ <code>[s2Stream /]</code> Shortcodes</a>.</p>'."\n";
631
 
632
  echo '<div class="ws-menu-page-hr"></div>'."\n";
633
 
634
+ echo '<h3><code>[s2Stream /]</code> Video Shortcode Examples (recommended—it\'s the easiest way)</h3>'."\n";
635
 
636
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp4\').toggle(); return false;" class="ws-dotted-link">JW Player (MP4 file, via Rewrite URLs. Amazon S3/CloudFront NOT required)</a></p>'."\n";
637
  echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp4" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />Works with any audio/video file. This does NOT require s2Member to be integrated with Amazon S3/CloudFront.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-s2stream-mp4.x-php")).'</p>'."\n";
644
 
645
  echo '<div class="ws-menu-page-hr"></div>'."\n";
646
 
647
+ echo '<h3><code>[s2Stream /]</code> Audio Shortcode Examples (recommended—it\'s the easiest way)</h3>'."\n";
648
 
649
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp3\').toggle(); return false;" class="ws-dotted-link">JW Player (MP3 file, via Rewrite URLs. Amazon S3/CloudFront NOT required)</a></p>'."\n";
650
  echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-s2stream-mp3" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />Works with any audio/video file. This does NOT require s2Member to be integrated with Amazon S3/CloudFront.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-s2stream-mp3.x-php")).'</p>'."\n";
657
 
658
  echo '<div class="ws-menu-page-hr"></div>'."\n";
659
 
660
+ echo '<h3>PHP Code Examples (for more advanced integrations via PHP—in WordPress themes)</h3>'."\n";
661
 
662
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-standard-mp4\').toggle(); return false;" class="ws-dotted-link">JW Player (MP4 file, via Rewrite URLs. Amazon S3/CloudFront NOT required)</a></p>'."\n";
663
+ echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-standard-mp4" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This does NOT require s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <strong>s2Member Download Options Advanced Mod Rewrite Linkage</strong>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-standard-mp4.x-php")).'</p>'."\n";
664
 
665
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4\').toggle(); return false;" class="ws-dotted-link">JW Player (RTMP streaming MP4, via s2Member\'s Amazon S3/CloudFront integration)</a></p>'."\n";
666
+ echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This requires s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <a href="http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/" target="_blank" rel="external">s2Member Codex API Functions</a>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-streaming-mp4.x-php")).'</p>'."\n";
667
 
668
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-sca\').toggle(); return false;" class="ws-dotted-link">JW Player (RTMP streaming MP4, via s2Member\'s JSON/Shortcode alternative)</a></p>'."\n";
669
+ echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-sca" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This requires s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <a href="http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/" target="_blank" rel="external">s2Member Codex API Functions</a>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-streaming-mp4-sca.x-php")).'</p>'."\n";
670
 
671
  echo '<p style="font-size:110%;"><a href="#" onclick="jQuery(\'p#ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-webm\').toggle(); return false;" class="ws-dotted-link">JW Player (RTMP streaming MP4, advanced w/ multiple fallbacks)</a></p>'."\n";
672
+ echo '<p id="ws-plugin--s2member-rtmp-streaming-details-jwplayer-streaming-mp4-webm" style="display:none;">Download <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">JW Player here</a>, and upload <code>/jwplayer/</code> to your website\'s root directory.<br />This requires s2Member to be integrated with Amazon S3/CloudFront.<br />Also see: <a href="http://www.s2member.com/codex/stable/s2member/api_functions/package-summary/" target="_blank" rel="external">s2Member Codex API Functions</a>.<br /><br />'.c_ws_plugin__s2member_utils_strings::highlight_php(file_get_contents(dirname(__FILE__)."/code-samples/jwplayer-streaming-mp4-webm.x-php")).'</p>'."\n";
673
 
674
  echo '</div>'."\n";
675
 
685
 
686
  echo '<div class="ws-menu-page-section ws-plugin--s2member-rewrite-linkage-section">'."\n";
687
  echo '<h3>Advanced Mod-Rewrite Linkage</h3>'."\n";
688
+ echo '<p>s2Member automatically creates <code>mod_rewrite</code> rules inside your <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory, which provide additional flexibility in the way protected files can be served to your Customers. With s2Member\'s <code>mod_rewrite</code> rules, it is now possible to link directly to a protected file, avoiding the use of query string variables <em>(it\'s completely optional though, i.e., NOT required)</em>.</p>'."\n";
689
  echo '<p>This new flexibility may come in handy for site owners serving files through media playback devices that have issues with query string variables. For instance, it is now possible to link to an s2Member-protected file directly, like this: <code>... /wp-content/plugins/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/example-file.zip</code> instead of <code>... /?s2member_file_download=example-file.zip</code>. Either way works, but the direct link might be easier for some.</p>'."\n";
690
  echo '<p>It is also possible to pass query string parameters through a direct link:<br /><code>... /wp-content/plugins/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/example-file.zip?s2member_file_inline=yes&amp;s2member_file_download_key=[key]</code>.</p>'."\n";
691
  echo '<p>That being said, s2Member\'s <code>mod_rewrite</code> rules allow for more advanced control over s2Member-specific parameters.</p>'."\n";
723
  echo '<td style="padding-top:0;">'."\n";
724
  echo '<ul class="ws-menu-page-li-margins">'."\n";
725
  echo '<li><code>download="file.zip"</code> Location of the file, relative to the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory; or, relative to the root of your Amazon S3 Bucket, when applicable.</li>'."\n";
726
+ echo '<li><code>download_key="no"</code> Defaults to <code>no</code>. If <code>download_key="1|on|yes|true|ip-forever|universal"</code>, s2Member will return a URL with an s2Member-generated File Download Key. You don\'t need to generate the File Download Key yourself, s2Member does it for you. If you set <code>download_key="ip-forever"</code>, the File Download Key that s2Member generates will last forever, for a specific IP Address; otherwise, by default, all File Download Keys expire after 24 hours automatically. If you set <code>download_key="universal"</code>, s2Member will generate a File Download Key that is good for anyone/everyone forever, with NO restrictions on who/where/when a file is accessed <em>(i.e., be careful with this one)</em>.</li>'."\n";
727
  echo '<li><code>stream="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>stream="1|on|yes|true"</code>, s2Member will return a URL containing a parameter/directive, which forces the File Download to take place over the RTMP protocol if at all possible. This ONLY works when/if s2Member is configured to run with both Amazon S3/CloudFront. Please note however, it\'s better to use the example code provided in the section above, regarding: <code>JW Player and the RTMP Protocol</code>. Also note, if <code>get_streamer_json="1|on|yes|true"</code>, s2Member will automatically force <code>stream="yes"</code> for you.</li>'."\n";
728
+ echo '<li><code>inline=""</code> Defaults to <code>[empty]</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>inline="1|on|yes|true"</code>, s2Member will serve the file inline, instead of as an actual File Download. If empty, s2Member will look at your <code>Inline File Extensions</code> configuration above, and serve the file inline; if, and only if, its extension matches one found in your configuration. By default, s2Member serves all files as attachments <em>(i.e., downloads)</em>, except in the case of the <code>[s2Stream /]</code> Shortcode where this defaults to <code>yes</code>. Please read the section above regarding <code>Inline File Extensions</code> for further details. Also note, this Shortcode Attribute does NOTHING for files served via Amazon CloudFront. See the tech-notes listed in the Amazon CloudFront section for further details and workarounds.</li>'."\n";
729
  echo '<li><code>storage=""</code> Defaults to <code>[empty]</code>. If <code>storage="local|s3|cf"</code>, s2Member will serve the file from a specific source location, based on the value of this Shortcode Attribute. For example, if you\'ve configured Amazon S3 and/or CloudFront; but, there are a few files that you want to upload locally to the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory; you can force s2Member to serve a file from local storage by setting <code>storage="local"</code> explicitly.</li>'."\n";
730
  echo '<li><code>remote="no"</code> Defaults to <code>no</code>. If <code>remote="1|on|yes|true"</code>, s2Member will authenticate access to the File Download via Remote Header Authorization, instead of through your web site. This is similar to <code>.htaccess</code> protection routines of yester-year</code>. Please check the <code>Remote Authorization and Podcasting</code> section for further details about how this works.</li>'."\n";
731
+ echo '<li><code>ssl=""</code> Defaults to <code>[empty]</code>. If <code>ssl="1|on|yes|true"</code>, s2Member will generate a File Download URL with an SSL protocol <em>(i.e., the URL will start with <code>https://</code> or <code>rtmpe://</code>)</em>. If empty, s2Member will only generate a File Download URL with an SSL protocol, when/if the Post/Page/URL firing the Shortcode itself, is also being viewed over SSL. Otherwise, s2Member will use a non-SSL protocol by default.</li>'."\n";
732
  echo '<li><code>rewrite="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>rewrite="1|on|yes|true"</code>, s2Member will generate a File Download URL that takes full advantage of s2Member\'s Advanced Mod Rewrite functionality. If you\'re running an Apache web server, or another server that supports <code>mod_rewrite</code>, we highly recommend turning this on. s2Member\'s <code>mod_rewrite</code> URLs do NOT contain query string parameters, making them more portable/compatible with other software applications and/or plugins for WordPress. If you\'re integrating with JW Player, you MUST use <code>rewrite="yes"</code>.</li>'."\n";
733
  echo '<li><code>rewrite_base=""</code> Defaults to <code>[empty]</code>. If <code>rewrite_base="'.esc_attr(site_url("/")).'"</code>, s2Member will generate a File Download URL that takes full advantage of s2Member\'s Advanced Mod Rewrite functionality, and it will use the rewrite base URL as a prefix. This could be useful on some WordPress installations that use advanced directory structures. It could also be useful for site owners using virtual directories that point to <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code>. Note, if <code>rewrite_base</code> is set, s2Member will automatically force <code>rewrite="yes"</code> for you.</li>'."\n";
734
  echo '<li><code>skip_confirmation="no"</code> Defaults to <code>no</code>. If <code>skip_confirmation="1|on|yes|true"</code>, s2Member will generate a File Download URL which contains a directive, telling s2Member NOT to introduce any JavaScript confirmation prompts on your site, for this File Download URL. Please note, s2Member will automatically detect links, anywhere in your content, and/or anywhere in your theme files, that contain <code>s2member_file_download</code> or <code>s2member-files</code>. Whenever a logged-in Member clicks a link that contains <code>s2member_file_download</code> or <code>s2member-files</code>, the system will politely ask the User to confirm the download using a very intuitive JavaScript confirmation prompt, which contains specific details about your configured download limitations. This way your Members will be aware of how many files they\'ve downloaded in the current period; and they\'ll be able to make a conscious decision about whether to proceed with a specific download or not.</li>'."\n";
735
+ echo '<li><code>url_to_storage_source="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>url_to_storage_source="1|on|yes|true"</code>, s2Member will generate a File Download URL which points directly to the storage source. This is only functional with Amazon S3 and/or CloudFront integrations. If you create a URL that points directly to the storage source <em>(i.e., points directly to Amazon S3 or CloudFront)</em>, s2Member will NOT be able to further authenticate the current User/Member; and, s2Member will NOT be able to count the File Download against the current User\'s account record, because the URL being generated does not pass back through s2Member at all, it points directly to the storage source. For this reason, if you set <code>url_to_storage_source="true"</code>, you should also set <code>check_user="true"</code> and <code>count_against_user="true"</code>, telling s2Member to authenticate the current User, and if authenticated, count this File Download URL against the current User\'s account record in real-time <em>(i.e., as the URL is being generated) </em>, while it still has a chance to do so. This Shortcode Attribute is useful when you stream files over the RTMP protocol; where an <code>http://</code> URL is not feasible. It also helps in situations where a 3rd-party software application will not work as intended, with s2Member\'s internal redirection to Amazon S3/CloudFront files. Important, when <code>check_user="true"</code> and/or <code>count_against_user="true"</code>, the Shortcode will return an empty and/or null object value in situations where the current User/Member does NOT have access to the file.</li>'."\n";
736
+ echo '<li><code>count_against_user="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>count_against_user="1|on|yes|true"</code>, it will automatically force <code>check_user="true"</code> as well. In other words, s2Member will authenticate the current User, and if authenticated, count this File Download URL against the current User\'s account record in real-time <em>(i.e., as the URL is being generated) </em>. This is off by default with the <code>[s2File /]</code> Shortcode. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, count the File Download against their account record, and serve the File Download. In other words, under normal circumstances, there is no reason to set <code>check_user="true"</code> and/or <code>count_against_user="true"</code> when generating the URL itself. However, this is a useful Shortcode Attribute when <code>url_to_storage_source="true"</code>. Please note, when <code>check_user="true"</code> and/or <code>count_against_user="true"</code>, the Shortcode will return an empty and/or null object value in situations where the current User/Member does NOT have access to the file.</li>'."\n";
737
  echo '<li><code>check_user="no"</code> Defaults to <code>no</code> with <code>[s2File /]</code> Shortcode. Defaults to <code>yes</code> with <code>[s2Stream /]</code> Shortcode. If <code>check_user="1|on|yes|true"</code>, s2Member will authenticate the current User before allowing the File Download URL to be generated. This is off by default with the <code>[s2File /]</code> Shortcode. By default, s2Member will simply generate a File Download URL, and upon a User/Member clicking the URL, s2Member will authenticate the User/Member at that time, and serve the File Download to the User/Member. In other words, under normal circumstances, there is no reason to set <code>check_user="true"</code> and/or <code>count_against_user="true"</code> when generating the URL itself. However, this IS a useful Shortcode Attribute when <code>url_to_storage_source="true"</code>. Please note, when <code>check_user="true"</code> and/or <code>count_against_user="true"</code>, the Shortcode will return an empty and/or null object value in situations where the current User/Member does NOT have access to the file.</li>'."\n";
738
  echo '<li><code>get_streamer_json="no"</code> Defaults to <code>no</code>. N/A with <code>[s2Stream /]</code> Shortcode. If <code>get_streamer_json="1|on|yes|true"</code>, the <code>[s2File /]</code> Shortcode will return a JSON object for JavaScript notation, making it possible to integrate the <code>[s2File /]</code> Shortcode into JavaScript routines that configure streaming media players. For further details, please review the section above: <code>JW Player &amp; RTMP Protocol Examples</code>. Note, if you set <code>get_streamer_json="true"</code>, s2Member will automatically force <code>url_to_storage_source="true"</code> and <code>stream="true"</code>. For that reason, you should carefully review the details and warning above regarding <code>url_to_storage_source</code>. If you set <code>get_streamer_json="true"</code>, you should also set <code>check_user="true"</code> and <code>count_against_user="true"</code>.</li>'."\n";
739
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_shortcode_attrs_s2file_lis", get_defined_vars());
758
  echo '<li><code>file_download="video.mp4"</code> Location of the audio/video file, relative to the <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/</code> directory; or, relative to the root of your Amazon S3 Bucket, when applicable.</li>'."\n";
759
  echo '<li><code>player="jwplayer-v6-rtmp"</code> Required. Current supported players in this Shortcode include: <code>jwplayer-v6</code> (works with any audio/video file, and you do NOT need to have Amazon S3 or CloudFront integrated for this to work), <code>jwplayer-v6-rtmp</code> (streams with the RTMP protocol, plus there is a full download fallback of the source file if streaming is not possible on a particular device; this requires both Amazon S3 and CloudFront integration), <code>jwplayer-v6-rtmp-only</code> (streams with the RTMP protocol only, with no access to the source file, only to the RTMP stream; this requires both Amazon S3 and CloudFront integration).</li>'."\n";
760
  echo '<li><code>player_id=""</code> Optional. HTML div ID for the audio/video player. Defaults to a unique ID generated by s2Member for each instance of your Shortcode.</li>'."\n";
761
+ echo '<li><code>player_path="/jwplayer/jwplayer.js"</code> Required. Path to the player\'s JavaScript file (ex: <code>/jwplayer/jwplayer.js</code>—you should upload the <a href="http://www.s2member.com/r/jw-player-download/" target="_blank" rel="external">/jwplayer</a> folder to the root of your web directory).</li>'."\n";
762
+ echo '<li><code>player_resolutions=""</code> Optional (requires s2Member Pro). This is a comma-delimited list of all available resolution options (should you decide to offer more than just one file download at a single resolution). Please review the full list of Shortcode Attributes (i.e., click the "Shortcode Attributes (Explained)" tab) in <a href="http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes" target="_blank" rel="external">this KB article</a> for further details, requirements, and an example of use.</li>'."\n";
763
  echo '<li><code>player_{setting}=""</code> Optional. Any additional configuration attributes supported by your audio/video player, prefixed with <code>player_</code>. For JW Player v6, see <a href="http://www.s2member.com/r/jw-player-config-options/" target="_blank" rel="external">this article please</a>. Examples: <code>player_width="480"</code>, <code>player_height="270"</code>, <code>player_title="My Video"</code>, <code>player_description="A video about something."</code>, <code>player_image="http://www.example.com/wp-content/uploads/video-preview.jpg"</code>, <code>player_mediaid="video_ei0wsx23"</code>, <code>player_autostart="true"</code>, <code>player_skin="/jwplayer/my-skin.xml"</code>, <code>player_key="my-license-key"</code>, <code>player_captions="{file:\'/assets/captions-en.vtt\',label:\'English\'}"</code> (<em>With <a href="http://www.s2member.com/r/jw-player-video-captions/" target="_blank" rel="external">Captions</a>, you can exclude the square array brackets to avoid Shortcode parsing issues. s2Member will automatically wrap your Caption objects with square array brackets.</em>). Please note that "Advanced Options Blocks" listed on <a href="http://www.s2member.com/r/jw-player-config-options/" target="_blank" rel="external">this page</a> are NOT supported here. For those, please use: <code>player_option_blocks=""</code> (see below).</li>'."\n";
764
  echo '<li><code>player_option_blocks=""</code> Optional. Any "Advanced Option Blocks" supported by your audio/video player. For JW Player v6, see <a href="http://www.s2member.com/r/jw-player-config-options/" target="_blank" rel="external">this article please</a>. Here are some examples: <code>player_option_blocks="sharing:{}"</code>, <code>player_option_blocks="sharing:{}, logo: {file: \'/logo.png\', link: \'http://example.com\'}"</code>. Or: <code>player_option_blocks="c2hhcmluZzoge30="</code> (base64 encoded version of <code>sharing:{}</code>). Please note that "Advanced Options Blocks" can be defined in plain text or with a <a href="http://www.base64encode.org/" target="_blank" rel="external">base64 encoded string</a>. Advanced Option Blocks are JavaScript objects with properties. If you have trouble defining JavaScript object properties inside a Shortcode Attribute, please use <a href="http://www.base64encode.org/" target="_blank" rel="external">this tool</a> to base64 encode your Advanced Option Blocks, so that you end up with a string that\'s compatible with Shortcode Attributes.</li>'."\n";
765
  echo '<li>Please check the <strong>Shortcode Attributes</strong> Tab in <a href="http://www.s2member.com/kb/jwplayer-s2stream-shortcodes/#using-s2stream-shortcodes" target="_blank" rel="external">this KB article</a> for further details on everything here.</li>'."\n";
804
 
805
  echo '<div class="ws-menu-page-section ws-plugin--s2member-gzip-conflicts-section">'."\n";
806
  echo '<h3>Preventing GZIP Conflicts On Server (Instructions)</h3>'."\n";
807
+ echo '<p>Protected files served by s2Member through PHP scripts, are already compressed. Therefore, <a href="http://code.google.com/speed/articles/gzip.html" target="_blank" rel="nofollow external xlink">GZIP compression</a> is not needed during protected file delivery. Some web servers (i.e., Apache, LiteSpeed, and similar) include GZIP compression rules through server-side extensions, like <code>mod_deflate</code> for example. While s2Member encourages the use of extensions like <code>mod_deflate</code>, it is best to disable GZIP automatically (i.e., temporarily) during s2Member\'s delivery of a protected file through a PHP script. This avoids conflicts on the server which might otherwise lead to corrupted file downloads. s2Member makes a valiant effort to accomplish this via PHP, all on its own. However, it never hurts to add this section of code to the root <code>.htaccess</code> file for your WordPress installation. Optional, but highly recommended.</p>'."\n";
808
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_gzip_conflicts", get_defined_vars());
809
 
810
  echo '<div class="ws-menu-page-hr"></div>'."\n";
855
 
856
  echo '<div class="ws-menu-page-section ws-plugin--s2member-custom-capability-files-section">'."\n";
857
  echo '<h3>Restricting Files, Based On Custom Capabilities</h3>'."\n";
858
+ echo '<p>If you\'re NOT familiar with Custom Capabilities yet, please read: <strong>Dashboard s2Member API Scripting Custom Capability Packages</strong>. Once you understand the basic concept of Custom Capabilities &amp; Protected File Downloads, you\'ll see that (by default) s2Member does NOT handle File Download Protection with respect to Custom Capabilities. That\'s where Custom Capability Sub-directories come in.</p>'."\n";
859
  echo '<p>You can create Custom Capability Sub-directories under: <code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'</code>. For instance, if you have a Custom Capability <code>music</code>, you can place protected files that should ONLY be accessible to Members with <code>access_s2member_ccap_music</code>, inside: <code>/'.esc_html(c_ws_plugin__s2member_utils_dirs::basename_dir_app_data($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"])).'/access-s2member-ccap-music/</code>. Some examples are provided below.</p>'."\n";
860
  do_action("ws_plugin__s2member_during_down_ops_page_during_left_sections_during_custom_capability_files", get_defined_vars());
861
 
884
  }
885
  do_action("ws_plugin__s2member_during_down_ops_page_after_left_sections", get_defined_vars());
886
 
 
 
887
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
888
 
889
  echo '</form>'."\n";
includes/menu-pages/els-ops.inc.php CHANGED
@@ -42,7 +42,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
42
  echo '<tr class="ws-menu-page-table-tr">'."\n";
43
  echo '<td class="ws-menu-page-table-l">'."\n";
44
 
45
- echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form">'."\n";
46
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
 
48
  do_action("ws_plugin__s2member_during_els_ops_page_before_left_sections", get_defined_vars());
@@ -57,7 +57,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
57
  echo '<a href="http://www.s2member.com/mailchimp" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/mailchimp-stamp.png" class="ws-menu-page-right" style="width:125px; height:125px; border:0;" alt="." /></a>'."\n";
58
  echo '<h3>MailChimp List Server Integration (optional)</h3>'."\n";
59
  echo '<p>s2Member can be integrated with MailChimp. MailChimp is an email marketing service. MailChimp makes it easy to send email newsletters to your Customers, manage your MailChimp subscriber lists, and track campaign performance. Although s2Member can be integrated with almost ANY list server, we highly recommend MailChimp; because of their <a href="http://www.s2member.com/mailchimp-api-docs" target="_blank" rel="external">powerful API for MailChimp services</a>. In future versions of s2Member, we plan to build additional features into s2Member that work with, and extend MailChimp services.</p>'."\n";
60
- echo '<p>For now, we\'ve covered the basics. You can have your Members automatically subscribed to your MailChimp marketing lists <em>(i.e. newsletters / auto-responders)</em>. You\'ll need a <a href="http://www.s2member.com/mailchimp" target="_blank" rel="external">MailChimp account</a>, a <a href="http://www.s2member.com/mailchimp-api-key" target="_blank" rel="external">MailChimp API Key</a>, and your <a href="#" onclick="alert(\'To obtain your MailChimp List ID(s), log into your MailChimp account and click the Lists tab. Now click the (View) button, for the List(s) you want to integrate with s2Member. Then, click the (Settings) link. At the bottom of the (Settings) page, for each list; you\\\'ll find a Unique List ID.\'); return false;">MailChimp List IDs</a>.</p>'."\n";
61
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_mailchimp", get_defined_vars());
62
 
63
  echo '<table class="form-table">'."\n";
@@ -115,8 +115,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
115
  echo '<div class="ws-menu-page-hr"></div>'."\n";
116
 
117
  echo '<div class="info" style="margin-bottom:0;">'."\n";
118
- echo '<p style="font-size:110%; margin-top:0;"><span>We HIGHLY recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems during MailChimp processing. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Files (Debug)</a>.</span></p>'."\n";
119
- echo '<p style="font-size:110%; margin-bottom:0;"><span class="ws-menu-page-error">However, it is VERY IMPORTANT to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We STRONGLY suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
120
  echo '</div>'."\n";
121
 
122
  echo '</div>'."\n";
@@ -135,7 +135,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
135
  echo '<a href="http://www.s2member.com/r/getresponse/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/getresponse-logo.png" class="ws-menu-page-right" style="width:256px; height:89px; border:0;" alt="." /></a>'."\n";
136
  echo '<h3>GetResponse List Server Integration (optional)</h3>'."\n";
137
  echo '<p>s2Member can be integrated with GetResponse. GetResponse is a complete email marketing solution. It provides turnkey newsletter publishing and hosting features, as well as unlimited autoresponders to deliver information to your subscribers and convert them to paying customers.</p>'."\n";
138
- echo '<p>You can have your Members automatically subscribed to your GetResponse marketing lists <em>(i.e. newsletters / auto-responders)</em>. You\'ll need a <a href="http://www.s2member.com/r/getresponse" target="_blank" rel="external">GetResponse account</a>, a <a href="http://www.s2member.com/r/getresponse-api-key" target="_blank" rel="external">GetResponse API Key</a>, and your <a href="http://www.s2member.com/r/getresponse-campaigns-list" target="_blank" rel="external" onclick="alert(\'To obtain your GetResponse Campaign Token(s), log into your GetResponse account and navigate to your entire list of Campaigns. In the left-hand column you\\\'ll find a list of Unique Campaign Tokens.\\n\\nPlease click OK and we\\\'ll take you there now :-)\');">GetResponse Campaign Tokens</a>.</p>'."\n";
139
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_getresponse", get_defined_vars());
140
 
141
  echo '<table class="form-table">'."\n";
@@ -192,8 +192,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
192
  echo '<div class="ws-menu-page-hr"></div>'."\n";
193
 
194
  echo '<div class="info" style="margin-bottom:0;">'."\n";
195
- echo '<p style="font-size:110%; margin-top:0;"><span>We HIGHLY recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems during GetResponse processing. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Files (Debug)</a>.</span></p>'."\n";
196
- echo '<p style="font-size:110%; margin-bottom:0;"><span class="ws-menu-page-error">However, it is VERY IMPORTANT to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We STRONGLY suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
197
  echo '</div>'."\n";
198
 
199
  echo '</div>'."\n";
@@ -211,9 +211,9 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
211
  echo '<div class="ws-menu-page-section ws-plugin--s2member-aweber-section">'."\n";
212
  echo '<a href="http://www.s2member.com/aweber" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/aweber-logo.png" class="ws-menu-page-right ws-menu-page-bordered" style="width:125px; height:125px; border:0;" alt="." /></a>'."\n";
213
  echo '<h3>AWeber List Server Integration (optional)</h3>'."\n";
214
- echo '<p>s2Member can be integrated with AWeber. AWeber is an email marketing service. Whether you\'re looking to get your first email campaign off the ground, or you\'re a seasoned veteran who wants to dig into advanced tools like detailed email web analytics, activity based segmentation, geo-targeting and broadcast split-testing, AWeber\'s got just what you need to make email marketing work for you. You can have your Members automatically subscribed to your AWeber marketing lists <em>(i.e. newsletters / auto-responders)</em>. You\'ll need an <a href="http://www.s2member.com/aweber" target="_blank" rel="external">AWeber account</a> and your <a href="#" onclick="alert(\'To obtain your AWeber List ID(s), log into your AWeber account. Click on the Lists tab. On that page you\\\'ll find a Unique List ID associated with each of your lists. AWeber sometimes refers to this as a List Name instead of a List ID.\'); return false;">AWeber List IDs</a>. You will ALSO need either an API Authorization Code (if you choose the API option below); or a <a href="http://www.s2member.com/kb/aweber-email-parser-for-s2member/" target="_blank" rel="external">Custom Email Parser</a> for the s2Member application.</p>'."\n";
215
  echo '<p><em><strong>AWeber Tip:</strong> If you want your Members to be subscribed to multiple AWeber List IDs at the same time, instead of comma-delimiting those List IDs here; we suggest a single List ID in your s2Member integration; then use <a href="https://help.aweber.com/entries/21696463-Can-I-Add-Subscribers-to-More-Than-One-List-At-Once-" target="_blank" rel="external">AWeber Automation Rules</a> for this. Automation Rules can also reduce the number of email confirmation notices that Members receive.</em></p>'."\n";
216
- echo '<p><em><strong>AWeber Tip:</strong> This company is known to have a policy of rejecting role-based email addresses like: <code>admin@</code> or <code>webmaster@</code>. Therefore, if you integrate AWeber it is suggested that you configure s2Member to Force Personal Emails. Please see: <code>s2Member ⥱ General Options ⥱ Registration/Profile Fields &amp; Options</code>.</em></p>'."\n";
217
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_aweber", get_defined_vars());
218
 
219
  echo '<table class="form-table">'."\n";
@@ -280,7 +280,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
280
  echo '<td>'."\n";
281
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_aweber_list_ids" id="ws-plugin--s2member-level'.$n.'-aweber-list-ids" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_aweber_list_ids"]).'" /><br />'."\n";
282
  echo 'New '.(($n === 0) ? 'Free Subscribers' : 'Level #'.$n.' Members').' will be subscribed to these List IDs.<br />'."\n";
283
- echo 'Ex: <code>mylist, anotherlist</code> — See also: <a href="https://help.aweber.com/entries/21696463-Can-I-Add-Subscribers-to-More-Than-One-List-At-Once-" target="_blank" rel="external">Automation Rules</a>'."\n";
284
  echo '</td>'."\n";
285
 
286
  echo '</tr>'."\n";
@@ -291,8 +291,8 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
291
  echo '<div class="ws-menu-page-hr"></div>'."\n";
292
 
293
  echo '<div class="info" style="margin-bottom:0;">'."\n";
294
- echo '<p style="font-size:110%; margin-top:0;"><span>We HIGHLY recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems during AWeber processing. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Files (Debug)</a>.</span></p>'."\n";
295
- echo '<p style="font-size:110%; margin-bottom:0;"><span class="ws-menu-page-error">However, it is VERY IMPORTANT to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We STRONGLY suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
296
  echo '</div>'."\n";
297
 
298
  echo '</div>'."\n";
@@ -344,9 +344,9 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
344
 
345
  echo '<td>'."\n";
346
  echo '<select name="ws_plugin__s2member_custom_reg_opt_in" id="ws-plugin--s2member-custom-reg-opt-in">'."\n";
347
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_opt_in"] == 1) ? ' selected="selected"' : '').'>Yes (the Box MUST be checked checked by default)</option>'."\n";
348
- echo '<option value="2"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_opt_in"] == 2) ? ' selected="selected"' : '').'>Yes (the Box MUST be checked unchecked by default)</option>'."\n";
349
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_opt_in"]) ? ' selected="selected"' : '').'>No (disable do NOT display or require the Checkbox)</option>'."\n";
350
  echo '</select><br />'."\n";
351
  echo 'An email confirmation will NOT be sent to the User, unless the Box is checked, or you\'ve disabled the Box; by choosing <code>No</code>.'."\n";
352
  echo '</td>'."\n";
@@ -369,7 +369,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
369
  echo '<div class="ws-menu-page-section ws-plugin--s2member-opt-out-section">'."\n";
370
  echo '<h3>Automate Un-Subscribe/Opt-Out Removals (optional)</h3>'."\n";
371
  echo '<p>s2Member can automatically <em>(and silently)</em> remove Users/Members from the List Servers you\'ve configured above. s2Member is also capable of automating this, based on your own personal configuration preferences. Below, you can choose which Events you consider grounds for List Removal. It is also important to point out that s2Member will ONLY remove Users/Members from the Lists you\'ve configured at the Level the User/Member is or was at during the time of the Event. For example, if a Level #1 Member is deleted, they will ONLY be removed from the List(s) you\'ve configured at Level #1. If an account is upgraded from Level #1 to Level #2, they will ONLY be removed from the List(s) you\'ve configured at Level #1. Of course, all of this is based on the configuration below.</p>'."\n";
372
- echo '<p><em><strong>*Regarding AWeber Email Parser Integration:*</strong> these will NOT work for AWeber until you <a href="http://www.s2member.com/aweber-notification-email" target="_blank" rel="external">add a Notification Email</a> to your AWeber account matching the "EMail From Address" configured in <code>s2Member -› General Options -› EMail Configuration</code>. Which is currently set to: <code>'.esc_html($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_from_email"]).'</code>. This is a required step if you want s2Member to be authenticated when it emails List Removal requests to AWeber. Please note, this only applies to AWeber integration via "email". If you choose to integrate via the AWeber API instead (recommended) this is not necessary/applicable.</em></p>'."\n";
373
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_opt_out", get_defined_vars());
374
 
375
  echo '<table class="form-table">'."\n";
@@ -411,9 +411,9 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
411
  echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_auto_opt_out_transitions"] === "1") ? ' selected="selected"' : '').'>Yes (automatically transition, if able to remove from a previous list)</option>'."\n";
412
  echo '<option value="2"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_auto_opt_out_transitions"] === "2") ? ' selected="selected"' : '').'>Yes (always automatically transition, even if NOT removed from a previous list)</option>'."\n";
413
  echo '</select><br />'."\n";
414
- echo '<em><strong>*Transitions*</strong> When/if s2Member automatically removes a Member from Lists at their current Level# (based on your configuration in the previous section), this setting tells s2Member that it should <strong>also</strong> transition the Member to any Lists you\'ve configured at the new Access Level# (i.e. Role) they are being changed to. For example, if a Member is demoted from Level #1 to Level #0, do you want s2Member to add them to the Level #0 List(s) after it removes them from the Level #1 List(s)?</em><br /><br />'."\n";
415
- echo '<em><strong>*If removed from a previous list, or NOT?*</strong> You can choose your preference above. When/if s2Member automatically transitions a mailing list subscriber, it will first try to remove the subscriber from a previous mailing list. If s2Member is able to remove the subscriber from a previous list before the transition takes place, s2Member will then make an attempt (based on your configuration) to transition the subscriber to a new/different list silently (i.e. without a new confirmation email being sent out). If s2Member is NOT able to remove a subscriber from a previous list, it can (if configured to do so) still transition a subscriber to a new list, by sending the subscriber a new email confirmation letter (i.e. this is NOT silent, because you absolutely NEED the subscriber\'s permission in this case).</em><br /><br />'."\n";
416
- echo '<em><strong>*Seamless with MailChimp*</strong> If enabled, Automatic List Transitions work seamlessly with MailChimp. Automatic List Transitions also work with GetResponse/AWeber, but GetResponse/AWeber may send the User/Member a new confirmation email, asking them to confirm changes to their mailing list subscription with you. Work is underway to improve this aspect of s2Member\'s integration with GetResponse/AWeber in a future release. Ideally, a Customer would be transitioned silently behind the scene with GetResponse/AWeber too, when appropriate.</em>'."\n";
417
  echo '</td>'."\n";
418
 
419
  echo '</tr>'."\n";
@@ -443,8 +443,6 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_els_ops"))
443
  }
444
  do_action("ws_plugin__s2member_during_els_ops_page_after_left_sections", get_defined_vars());
445
 
446
- echo '<div class="ws-menu-page-hr"></div>'."\n";
447
-
448
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
449
 
450
  echo '</form>'."\n";
42
  echo '<tr class="ws-menu-page-table-tr">'."\n";
43
  echo '<td class="ws-menu-page-table-l">'."\n";
44
 
45
+ echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" autocomplete="off">'."\n";
46
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
 
48
  do_action("ws_plugin__s2member_during_els_ops_page_before_left_sections", get_defined_vars());
57
  echo '<a href="http://www.s2member.com/mailchimp" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/mailchimp-stamp.png" class="ws-menu-page-right" style="width:125px; height:125px; border:0;" alt="." /></a>'."\n";
58
  echo '<h3>MailChimp List Server Integration (optional)</h3>'."\n";
59
  echo '<p>s2Member can be integrated with MailChimp. MailChimp is an email marketing service. MailChimp makes it easy to send email newsletters to your Customers, manage your MailChimp subscriber lists, and track campaign performance. Although s2Member can be integrated with almost ANY list server, we highly recommend MailChimp; because of their <a href="http://www.s2member.com/mailchimp-api-docs" target="_blank" rel="external">powerful API for MailChimp services</a>. In future versions of s2Member, we plan to build additional features into s2Member that work with, and extend MailChimp services.</p>'."\n";
60
+ echo '<p>For now, we\'ve covered the basics. You can have your Members automatically subscribed to your MailChimp marketing lists <em>(i.e., newsletters / auto-responders)</em>. You\'ll need a <a href="http://www.s2member.com/mailchimp" target="_blank" rel="external">MailChimp account</a>, a <a href="http://www.s2member.com/mailchimp-api-key" target="_blank" rel="external">MailChimp API Key</a>, and your <a href="#" onclick="alert(\'To obtain your MailChimp List ID(s), log into your MailChimp account and click the Lists tab. Now click the (View) button, for the List(s) you want to integrate with s2Member. Then, click the (Settings) link. At the bottom of the (Settings) page, for each list; you\\\'ll find a Unique List ID.\'); return false;">MailChimp List IDs</a>.</p>'."\n";
61
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_mailchimp", get_defined_vars());
62
 
63
  echo '<table class="form-table">'."\n";
115
  echo '<div class="ws-menu-page-hr"></div>'."\n";
116
 
117
  echo '<div class="info" style="margin-bottom:0;">'."\n";
118
+ echo '<p style="margin-top:0;"><span>We highly recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems during MailChimp processing. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Files (Debug)</a>.</span></p>'."\n";
119
+ echo '<p style="margin-bottom:0;"><span class="ws-menu-page-error">However, it is very important to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We strongly suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
120
  echo '</div>'."\n";
121
 
122
  echo '</div>'."\n";
135
  echo '<a href="http://www.s2member.com/r/getresponse/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/getresponse-logo.png" class="ws-menu-page-right" style="width:256px; height:89px; border:0;" alt="." /></a>'."\n";
136
  echo '<h3>GetResponse List Server Integration (optional)</h3>'."\n";
137
  echo '<p>s2Member can be integrated with GetResponse. GetResponse is a complete email marketing solution. It provides turnkey newsletter publishing and hosting features, as well as unlimited autoresponders to deliver information to your subscribers and convert them to paying customers.</p>'."\n";
138
+ echo '<p>You can have your Members automatically subscribed to your GetResponse marketing lists <em>(i.e., newsletters / auto-responders)</em>. You\'ll need a <a href="http://www.s2member.com/r/getresponse" target="_blank" rel="external">GetResponse account</a>, a <a href="http://www.s2member.com/r/getresponse-api-key" target="_blank" rel="external">GetResponse API Key</a>, and your <a href="http://www.s2member.com/r/getresponse-campaigns-list" target="_blank" rel="external" onclick="alert(\'To obtain your GetResponse Campaign Token(s), log into your GetResponse account and navigate to your entire list of Campaigns. In the left-hand column you\\\'ll find a list of Unique Campaign Tokens.\\n\\nPlease click OK and we\\\'ll take you there now :-)\');">GetResponse Campaign Tokens</a>.</p>'."\n";
139
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_getresponse", get_defined_vars());
140
 
141
  echo '<table class="form-table">'."\n";
192
  echo '<div class="ws-menu-page-hr"></div>'."\n";
193
 
194
  echo '<div class="info" style="margin-bottom:0;">'."\n";
195
+ echo '<p style="margin-top:0;"><span>We highly recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems during GetResponse processing. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Files (Debug)</a>.</span></p>'."\n";
196
+ echo '<p style="margin-bottom:0;"><span class="ws-menu-page-error">However, it is very important to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We strongly suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
197
  echo '</div>'."\n";
198
 
199
  echo '</div>'."\n";
211
  echo '<div class="ws-menu-page-section ws-plugin--s2member-aweber-section">'."\n";
212
  echo '<a href="http://www.s2member.com/aweber" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/aweber-logo.png" class="ws-menu-page-right ws-menu-page-bordered" style="width:125px; height:125px; border:0;" alt="." /></a>'."\n";
213
  echo '<h3>AWeber List Server Integration (optional)</h3>'."\n";
214
+ echo '<p>s2Member can be integrated with AWeber. AWeber is an email marketing service. Whether you\'re looking to get your first email campaign off the ground, or you\'re a seasoned veteran who wants to dig into advanced tools like detailed email web analytics, activity based segmentation, geo-targeting and broadcast split-testing, AWeber\'s got just what you need to make email marketing work for you. You can have your Members automatically subscribed to your AWeber marketing lists <em>(i.e., newsletters / auto-responders)</em>. You\'ll need an <a href="http://www.s2member.com/aweber" target="_blank" rel="external">AWeber account</a> and your <a href="#" onclick="alert(\'To obtain your AWeber List ID(s), log into your AWeber account. Click on the Lists tab. On that page you\\\'ll find a Unique List ID associated with each of your lists. AWeber sometimes refers to this as a List Name instead of a List ID.\'); return false;">AWeber List IDs</a>. You will ALSO need either an API Authorization Code (if you choose the API option below); or a <a href="http://www.s2member.com/kb/aweber-email-parser-for-s2member/" target="_blank" rel="external">Custom Email Parser</a> for the s2Member application.</p>'."\n";
215
  echo '<p><em><strong>AWeber Tip:</strong> If you want your Members to be subscribed to multiple AWeber List IDs at the same time, instead of comma-delimiting those List IDs here; we suggest a single List ID in your s2Member integration; then use <a href="https://help.aweber.com/entries/21696463-Can-I-Add-Subscribers-to-More-Than-One-List-At-Once-" target="_blank" rel="external">AWeber Automation Rules</a> for this. Automation Rules can also reduce the number of email confirmation notices that Members receive.</em></p>'."\n";
216
+ echo '<p><em><strong>AWeber Tip:</strong> This company is known to have a policy of rejecting role-based email addresses like: <code>admin@</code> or <code>webmaster@</code>. Therefore, if you integrate AWeber it is suggested that you configure s2Member to Force Personal Emails. Please see: <strong>s2Member ⥱ General Options ⥱ Registration/Profile Fields &amp; Options</strong>.</em></p>'."\n";
217
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_aweber", get_defined_vars());
218
 
219
  echo '<table class="form-table">'."\n";
280
  echo '<td>'."\n";
281
  echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_aweber_list_ids" id="ws-plugin--s2member-level'.$n.'-aweber-list-ids" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_aweber_list_ids"]).'" /><br />'."\n";
282
  echo 'New '.(($n === 0) ? 'Free Subscribers' : 'Level #'.$n.' Members').' will be subscribed to these List IDs.<br />'."\n";
283
+ echo 'Ex: <code>mylist, anotherlist</code>—See also: <a href="https://help.aweber.com/entries/21696463-Can-I-Add-Subscribers-to-More-Than-One-List-At-Once-" target="_blank" rel="external">Automation Rules</a>'."\n";
284
  echo '</td>'."\n";
285
 
286
  echo '</tr>'."\n";
291
  echo '<div class="ws-menu-page-hr"></div>'."\n";
292
 
293
  echo '<div class="info" style="margin-bottom:0;">'."\n";
294
+ echo '<p style="margin-top:0;"><span>We highly recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems during AWeber processing. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Files (Debug)</a>.</span></p>'."\n";
295
+ echo '<p style="margin-bottom:0;"><span class="ws-menu-page-error">However, it is very important to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We strongly suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
296
  echo '</div>'."\n";
297
 
298
  echo '</div>'."\n";
344
 
345
  echo '<td>'."\n";
346
  echo '<select name="ws_plugin__s2member_custom_reg_opt_in" id="ws-plugin--s2member-custom-reg-opt-in">'."\n";
347
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_opt_in"] == 1) ? ' selected="selected"' : '').'>Yes (the Box MUST be checked—checked by default)</option>'."\n";
348
+ echo '<option value="2"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_opt_in"] == 2) ? ' selected="selected"' : '').'>Yes (the Box MUST be checked—unchecked by default)</option>'."\n";
349
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_opt_in"]) ? ' selected="selected"' : '').'>No (disable—do NOT display or require the Checkbox)</option>'."\n";
350
  echo '</select><br />'."\n";
351
  echo 'An email confirmation will NOT be sent to the User, unless the Box is checked, or you\'ve disabled the Box; by choosing <code>No</code>.'."\n";
352
  echo '</td>'."\n";
369
  echo '<div class="ws-menu-page-section ws-plugin--s2member-opt-out-section">'."\n";
370
  echo '<h3>Automate Un-Subscribe/Opt-Out Removals (optional)</h3>'."\n";
371
  echo '<p>s2Member can automatically <em>(and silently)</em> remove Users/Members from the List Servers you\'ve configured above. s2Member is also capable of automating this, based on your own personal configuration preferences. Below, you can choose which Events you consider grounds for List Removal. It is also important to point out that s2Member will ONLY remove Users/Members from the Lists you\'ve configured at the Level the User/Member is or was at during the time of the Event. For example, if a Level #1 Member is deleted, they will ONLY be removed from the List(s) you\'ve configured at Level #1. If an account is upgraded from Level #1 to Level #2, they will ONLY be removed from the List(s) you\'ve configured at Level #1. Of course, all of this is based on the configuration below.</p>'."\n";
372
+ echo '<p><em><strong>Regarding AWeber Email Parser Integration::</strong> these will NOT work for AWeber until you <a href="http://www.s2member.com/aweber-notification-email" target="_blank" rel="external">add a Notification Email</a> to your AWeber account matching the "EMail From Address" configured in <strong>s2Member General Options EMail Configuration</strong>. Which is currently set to: <code>'.esc_html($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_from_email"]).'</code>. This is a required step if you want s2Member to be authenticated when it emails List Removal requests to AWeber. Please note, this only applies to AWeber integration via "email". If you choose to integrate via the AWeber API instead (recommended) this is not necessary/applicable.</em></p>'."\n";
373
  do_action("ws_plugin__s2member_during_els_ops_page_during_left_sections_during_opt_out", get_defined_vars());
374
 
375
  echo '<table class="form-table">'."\n";
411
  echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_auto_opt_out_transitions"] === "1") ? ' selected="selected"' : '').'>Yes (automatically transition, if able to remove from a previous list)</option>'."\n";
412
  echo '<option value="2"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_auto_opt_out_transitions"] === "2") ? ' selected="selected"' : '').'>Yes (always automatically transition, even if NOT removed from a previous list)</option>'."\n";
413
  echo '</select><br />'."\n";
414
+ echo '<em><strong>Transitions:</strong> When/if s2Member automatically removes a Member from Lists at their current Level# (based on your configuration in the previous section), this setting tells s2Member that it should <strong>also</strong> transition the Member to any Lists you\'ve configured at the new Access Level# (i.e., Role) they are being changed to. For example, if a Member is demoted from Level #1 to Level #0, do you want s2Member to add them to the Level #0 List(s) after it removes them from the Level #1 List(s)?</em><br /><br />'."\n";
415
+ echo '<em><strong>If removed from a previous list, or NOT?:</strong> You can choose your preference above. When/if s2Member automatically transitions a mailing list subscriber, it will first try to remove the subscriber from a previous mailing list. If s2Member is able to remove the subscriber from a previous list before the transition takes place, s2Member will then make an attempt (based on your configuration) to transition the subscriber to a new/different list silently (i.e., without a new confirmation email being sent out). If s2Member is NOT able to remove a subscriber from a previous list, it can (if configured to do so) still transition a subscriber to a new list, by sending the subscriber a new email confirmation letter (i.e., this is NOT silent, because you absolutely NEED the subscriber\'s permission in this case).</em><br /><br />'."\n";
416
+ echo '<em><strong>Seamless with MailChimp:</strong> If enabled, Automatic List Transitions work seamlessly with MailChimp. Automatic List Transitions also work with GetResponse/AWeber, but GetResponse/AWeber may send the User/Member a new confirmation email, asking them to confirm changes to their mailing list subscription with you. Work is underway to improve this aspect of s2Member\'s integration with GetResponse/AWeber in a future release. Ideally, a Customer would be transitioned silently behind the scene with GetResponse/AWeber too, when appropriate.</em>'."\n";
417
  echo '</td>'."\n";
418
 
419
  echo '</tr>'."\n";
443
  }
444
  do_action("ws_plugin__s2member_during_els_ops_page_after_left_sections", get_defined_vars());
445
 
 
 
446
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
447
 
448
  echo '</form>'."\n";
includes/menu-pages/gen-ops.inc.php CHANGED
@@ -18,1507 +18,1487 @@ if(!defined('WPINC')) // MUST have WordPress.
18
  exit("Do not access this file directly.");
19
 
20
  if(!class_exists("c_ws_plugin__s2member_menu_page_gen_ops"))
 
 
 
 
 
 
 
 
21
  {
22
- /**
23
- * Menu page for the s2Member plugin (General Options page).
24
- *
25
- * @package s2Member\Menu_Pages
26
- * @since 110531
27
- */
28
- class c_ws_plugin__s2member_menu_page_gen_ops
29
  {
30
- public function __construct()
31
- {
32
- echo '<div class="wrap ws-menu-page">'."\n";
33
-
34
- echo '<div class="ws-menu-page-toolbox">'."\n";
35
- c_ws_plugin__s2member_menu_pages_tb::display();
36
- echo '</div>'."\n";
37
-
38
- echo '<h2>General Options</h2>'."\n";
39
-
40
- echo '<table class="ws-menu-page-table">'."\n";
41
- echo '<tbody class="ws-menu-page-table-tbody">'."\n";
42
- echo '<tr class="ws-menu-page-table-tr">'."\n";
43
- echo '<td class="ws-menu-page-table-l">'."\n";
44
-
45
- echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form">'."\n";
46
- echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
- echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
48
-
49
- do_action("ws_plugin__s2member_during_gen_ops_page_before_left_sections", get_defined_vars());
50
-
51
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_uninstall", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars()))
52
- {
53
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_uninstall", get_defined_vars());
54
-
55
- echo '<div class="ws-menu-page-group" title="Plugin Deletion Safeguards"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["run_uninstall_routines"]) ? ' default-state="open"' : '').'>'."\n";
56
-
57
- echo '<div class="ws-menu-page-section ws-plugin--s2member-uninstall-section">'."\n";
58
- echo '<h3>Plugin Deletion Safeguards (highly recommended)</h3>'."\n";
59
- echo '<p>By default, s2Member will retain all of it\'s Roles, Capabilities, and your Configuration Options when/if you delete s2Member from the Plugins Menu in WordPress. However, if you would like for s2Member to erase itself completely, please choose: <code>No (upon deletion, erase all data/options)</code>.</p>';
60
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_uninstall", get_defined_vars());
61
-
62
- echo '<table class="form-table">'."\n";
63
- echo '<tbody>'."\n";
64
- echo '<tr>'."\n";
65
-
66
- echo '<th>'."\n";
67
- echo '<label for="ws-plugin--s2member-run-uninstall-routines">'."\n";
68
- echo 'Safeguard s2Member Data/Options?'."\n";
69
- echo '</label>'."\n";
70
- echo '</th>'."\n";
71
 
72
- echo '</tr>'."\n";
73
- echo '<tr>'."\n";
 
74
 
75
- echo '<td>'."\n";
76
- echo '<select name="ws_plugin__s2member_run_uninstall_routines" id="ws-plugin--s2member-run-uninstall-routines">'."\n";
77
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["run_uninstall_routines"]) ? ' selected="selected"' : '').'>Yes (safeguard all data/options)</option>'."\n";
78
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["run_uninstall_routines"]) ? ' selected="selected"' : '').'>No (upon deletion, erase all data/options)</option>'."\n";
79
- echo '</select><br />'."\n";
80
- echo 'Recommended setting: (<code>Yes, safeguard all data/options</code>)'."\n";
81
- echo '</td>'."\n";
82
 
83
- echo '</tr>'."\n";
84
- echo '</tbody>'."\n";
85
- echo '</table>'."\n";
86
- echo '</div>'."\n";
87
 
88
- echo '</div>'."\n";
 
 
89
 
90
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_uninstall", get_defined_vars());
91
- }
92
 
93
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_security", TRUE, get_defined_vars()))
94
- {
95
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_security", get_defined_vars());
96
 
97
- echo '<div class="ws-menu-page-group" title="Security Encryption Key">'."\n";
98
 
99
- echo '<div class="ws-menu-page-section ws-plugin--s2member-security-section">'."\n";
100
- echo '<img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/large-icon.png" title="s2Member (a Membership management system for WordPress)" alt="" style="float:right; margin:0 0 0 25px; border:0;" />'."\n";
101
- echo '<h3>Security Encryption Key (optional, for tighter security)</h3>'."\n";
102
- echo '<p>Just like WordPress, s2Member is open-source software. Which is wonderful. However, this also makes it possible for anyone to grab a copy of the software, and try to learn their way around its security measures. In order to keep your installation of s2Member unique/secure, you should configure a Security Encryption Key. s2Member will use your Security Encryption Key to protect itself against hackers. It does this by encrypting all sensitive information with your Key. A Security Encryption Key is unique to your installation.</p>'."\n";
103
- echo '<p>Once you configure this, you do NOT want to change it; not ever. In fact, it is a VERY good idea to keep this backed up in a safe place, just in case you need to move your site, or re-install s2Member in the future. Some of the sensitive data that s2Member stores, will be encrypted with this Key. If you change it, that data can no longer be read, even by s2Member itself. In other words, don\'t use s2Member for six months, then decide to change your Key. That would break your installation.</p>'."\n";
104
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_security", get_defined_vars());
105
 
106
- echo '<table class="form-table">'."\n";
107
- echo '<tbody>'."\n";
108
- echo '<tr>'."\n";
109
 
110
- echo '<th>'."\n";
111
- echo '<label for="ws-plugin--s2member-sec-encryption-key">'."\n";
112
- echo 'Security Encryption Key (at least 60 chars)'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]) ? ' <a href="#" onclick="ws_plugin__s2member_enableSecurityKey(); return false;" title="(not recommended)">edit key</a>' : ' <a href="#" onclick="ws_plugin__s2member_generateSecurityKey(); return false;" title="Insert an auto-generated Key. (recommended)">auto-generate</a>')."\n";
113
- echo '</label>'."\n";
114
- echo '</th>'."\n";
 
 
 
115
 
116
- echo '</tr>'."\n";
117
- echo '<tr>'."\n";
 
 
 
 
 
118
 
119
- echo '<td>'."\n";
120
- echo '<input type="text" maxlength="256" autocomplete="off" name="ws_plugin__s2member_sec_encryption_key" id="ws-plugin--s2member-sec-encryption-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]).'"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]) ? ' disabled="disabled"' : '').' />'."\n";
121
- echo (!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]) ? '<br />This may contain letters, numbers, spaces; even punctuation. Up to 256 characters.<br /><em>Ex: <code>'.esc_html(strtoupper(c_ws_plugin__s2member_utils_strings::random_str_gen(64))).'</code></em>'."\n" : '';
122
- echo (count($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key_history"]) > 1) ? '<br /><a href="#" onclick="ws_plugin__s2member_securityKeyHistory(); return false;">Click here</a> for a history of your last 10 Encryption Keys.<div id="ws-plugin--s2member-sec-encryption-key-history" style="display:none;"><code>'.implode('</code><br /><code>', $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key_history"]).'</code></div>'."\n" : '';
123
- echo '</td>'."\n";
124
-
125
- echo '</tr>'."\n";
126
- echo '</tbody>'."\n";
127
- echo '</table>'."\n";
128
-
129
- echo '<div class="ws-menu-page-hr"></div>'."\n";
130
-
131
- echo '<h3>Additional Details Regarding this Key:</h3>'."\n";
132
- echo '<p>Your Security Encryption Key is used throughout s2Member\'s source code for many different things. However, MOST (not all, but most) uses of this Key are related to transactional processing within a particular session; so changing the Key won\'t really impact these scenarios in any significant way. Your Security Encryption Key is simply there to enhance security of data that is being transmitted in these cases.</p>'."\n";
133
- echo '<p>That said, there are a few scenarios where use of your Security Encryption Key is more long-term. These include: Specific Post/Page Access Links, Registration Access Links, and it can also have a long-term impact on IPN communication because some data analyzed by s2Member includes a checksum that depends on your Key. If the Key changes, it could cause future IPN data (i.e. data from your payment gateway) to fail validation.</p>'."\n";
134
-
135
- echo '</div>'."\n";
136
-
137
- echo '</div>'."\n";
138
-
139
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_security", get_defined_vars());
140
- }
141
-
142
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_localhost_info", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars()))
143
- {
144
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_localhost_info", get_defined_vars());
145
-
146
- echo '<div class="ws-menu-page-group" title="Localhost WAMP/MAMP Developers">'."\n";
147
-
148
- echo '<div class="ws-menu-page-section ws-plugin--s2member-localhost-info-section">'."\n";
149
- echo '<h3>Localhost WAMP/MAMP Installs (are you a developer?)</h3>'."\n";
150
- echo '<p>If you\'re developing your site in a <code>localhost</code> environment, running something like WAMP/MAMP, or <a href="http://www.easyphp.org/" target="_blank" rel="external">EasyPHP</a>, please add this line to your <code>/wp-config.php</code> file: <code><span style="color:#0000BB;">define</span><span style="color:#007700;">(</span><span style="color:#DD0000;">"LOCALHOST"</span>, <span style="color:#0000BB;">true</span><span style="color:#007700;">);</span></code>.</p>'."\n";
151
- echo '<p>This lets s2Member know definitively that your site is in a <code>localhost</code> environment. s2Member will adjust itself accordingly, maximizing functionality during your developement. s2Member can usually auto-detect this, but in cases where your <code>localhost</code> installation runs on something other than <code>127.0.0.1/localhost</code>, you need to tell s2Member definitively, by adding that line to your <code>/wp-config.php</code> file. For instance, s2Member needs to know when your server IP is the same as all User IPs.</p>'."\n";
152
- echo '<p><em>Once your site goes live, please remove the line. If you\'re already on a live server connected to the web, please ignore this section.</em></p>'."\n";
153
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_localhost_info", get_defined_vars());
154
- echo '</div>'."\n";
155
-
156
- echo '</div>'."\n";
157
-
158
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_localhost_info", get_defined_vars());
159
- }
160
-
161
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_lazy_load", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars()))
162
- {
163
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_lazy_load", get_defined_vars());
164
-
165
- echo '<div class="ws-menu-page-group" title="CSS/JS Lazy Loading">'."\n";
166
-
167
- echo '<div class="ws-menu-page-section ws-plugin--s2member-lazy-load-section">'."\n";
168
- echo '<h3>CSS/JS Lazy Loading (Client-Side Libraries)</h3>'."\n";
169
- echo '<p>By default, s2Member will load it\'s CSS/JS libraries on every page of your site. However, you may wish to enable lazy-loading here (i.e. only load when absolutely necessary).</p>'."\n";
170
- echo '<p><em><strong>Tip:</strong> Do you need s2Member\'s CSS/JS on every page? If not, you can turn lazy-loading on. If you need s2Member\'s CSS/JS on a given Post/Page, you can insert an HTML comment into the Post/Page content like this: <code>&lt;!--s2member--&gt;</code>. If a Post/Page contains the word <code>s2member</code> or an <code>[s2*</code> Shortcode, this will automatically trigger s2Member\'s lazy-load routine (no matter what you configure here). Thus, it\'s an easy way to force s2Member to load it\'s CSS/JS on specific Posts/Pages where you deem this necessary. There is also a WordPress filter available: <code>add_filter("ws_plugin__s2member_lazy_load_css_js", "__return_true");</code> for developers; this could be incorporated into more dynamic scenarios.</em></p>'."\n";
171
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_lazy_load", get_defined_vars());
172
-
173
- echo '<table class="form-table">'."\n";
174
- echo '<tbody>'."\n";
175
- echo '<tr>'."\n";
176
-
177
- echo '<th>'."\n";
178
- echo '<label for="ws-plugin--s2member-lazy-load-css-js">'."\n";
179
- echo 'Lazy-Load s2Member\'s CSS/JS Libraries?'."\n";
180
- echo '</label>'."\n";
181
- echo '</th>'."\n";
182
-
183
- echo '</tr>'."\n";
184
- echo '<tr>'."\n";
185
-
186
- echo '<td>'."\n";
187
- echo '<select name="ws_plugin__s2member_lazy_load_css_js" id="ws-plugin--s2member-lazy-load-css-js">'."\n";
188
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["lazy_load_css_js"]) ? ' selected="selected"' : '').'>No (always load the CSS/JS libraries; i.e. on every page of the site)</option>'."\n";
189
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["lazy_load_css_js"]) ? ' selected="selected"' : '').'>Yes (lazy-load CSS/JS libraries; i.e. load only when absolutely necessary)</option>'."\n";
190
- echo '</select>'."\n";
191
- echo '</td>'."\n";
192
-
193
- echo '</tr>'."\n";
194
- echo '</tbody>'."\n";
195
- echo '</table>'."\n";
196
- echo '</div>'."\n";
197
-
198
- echo '</div>'."\n";
199
-
200
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_lazy_load", get_defined_vars());
201
- }
202
-
203
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_s_badge_wp_footer_code", TRUE, get_defined_vars()))
204
- {
205
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_s_badge_wp_footer_code", get_defined_vars());
206
-
207
- echo '<div class="ws-menu-page-group" title="s2Member Security Badge">'."\n";
208
-
209
- echo '<div class="ws-menu-page-section ws-plugin--s2member-s-badge-wp-footer-code-section">'."\n";
210
- echo '<h3>Security Badge &amp; Footer Configuration (optional)</h3>'."\n";
211
- echo '<div class="ws-menu-page-right">'.c_ws_plugin__s2member_utilities::s_badge_gen("1", TRUE, TRUE).'</div>'."\n";
212
- echo '<p>An s2Member Security Badge <em>(optional)</em>, can be used to express your site\'s concern for security; demonstrating to all Users/Members that your site <em>(and the s2Member software)</em>, takes security seriously. However, in order to qualify your site, you MUST generate a Security Encryption Key <em>(previous section)</em>, and then click "Save All Changes". Only then, will s2Member officially verify your installation <em>(verification occurs automatically)</em>.</p>'."\n";
213
- echo '<p>Once you\'ve <a href="http://www.s2member.com/kb/security-badges/" target="_blank" rel="external">properly configured all security aspects of s2Member</a>, your s2Member Security Badge will be verified. To see the "verified" version of your Security Badge, you might need to refresh your browser after saving all changes <em>(i.e. after you create a Security Encryption Key)</em>. Also, s2Member will NOT "verify" your site if you turn off Unique IP Restrictions, Brute Force Login Protection, or if your <code>/wp-config.php</code> file lacks <a href="http://codex.wordpress.org/Editing_wp-config.php#Security_Keys" target="_blank" rel="external">Security Keys</a> <em>(each at least 60 chars in length)</em>. In addition, it\'s NOT possible for s2Member to verify your Security Badge, if your site is in a <code>localhost</code> environment; i.e. not connected to the web.</p>'."\n";
214
- echo '<p><strong>How does s2Member know when my site is secure?</strong><br />If enabled below, an API call for "Security Badge Status", will allow web service connections to determine your status. Clicking <a href="'.esc_attr(home_url("/?s2member_s_badge_status=1")).'" target="_blank" rel="external">this link</a> will report <code>1</code> <em>(secure)</em>, <code>0</code> <em>(at risk)</em>, or <code>-</code> <em>(API disabled)</em>. Once all security considerations are satisfied, s2Member will report <code>1</code> <em>(secure)</em> for your installation. *Note, this simple API will NOT, and should not, report any other information. It will ONLY report the current status of your Security Badge, as determined by your installation of s2Member. When/if you install the s2Member Security Badge, s2Member will make a connection to your site "once per day", to test your status.</p>'."\n";
215
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_s_badge_wp_footer_code", get_defined_vars());
216
-
217
- echo '<table class="form-table">'."\n";
218
- echo '<tbody>'."\n";
219
- echo '<tr>'."\n";
220
-
221
- echo '<th>'."\n";
222
- echo '<label for="ws-plugin--s2member-s-badge-status-enabled">'."\n";
223
- echo 'Enable Security Badge Status API?'."\n";
224
- echo '</label>'."\n";
225
- echo '</th>'."\n";
226
-
227
- echo '</tr>'."\n";
228
- echo '<tr>'."\n";
229
-
230
- echo '<td>'."\n";
231
- echo '<select name="ws_plugin__s2member_s_badge_status_enabled" id="ws-plugin--s2member-s-badge-status-enabled">'."\n";
232
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["s_badge_status_enabled"]) ? ' selected="selected"' : '').'>No (default, Badge Status API is disabled)</option>'."\n";
233
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["s_badge_status_enabled"]) ? ' selected="selected"' : '').'>Yes (enable Badge Status API for verification)</option>'."\n";
234
- echo '</select><br />'."\n";
235
- echo 'This must be enabled if you want s2Member to verify your Security Badge.'."\n";
236
- echo '</td>'."\n";
237
-
238
- echo '</tr>'."\n";
239
- echo '<tr>'."\n";
240
-
241
- echo '<th>'."\n";
242
- echo '<label for="ws-plugin--s2member-wp-footer-code">'."\n";
243
- echo 'Customize WordPress Footer:<br />'."\n";
244
- echo '<small>[ <a href="#" onclick="this.$code = jQuery(\'textarea#ws-plugin--s2member-wp-footer-code\'); this.$code.val(jQuery.trim(unescape(\''.rawurlencode('[s2Member-Security-Badge v="1" /]').'\')+\'\n\'+this.$code.val())); return false;">Click HERE to insert your Security Badge</a> ],<br />or use Shortcode <code>[s2Member-Security-Badge v="1" /]</code> in a Post/Page/Widget.<br />The <code>v="1"</code> attribute is a Security Badge style/variation. Try variations <code>1|2|3</code>.</small>'."\n";
245
- echo '</label>'."\n";
246
- echo '</th>'."\n";
247
-
248
- echo '</tr>'."\n";
249
- echo '<tr>'."\n";
250
-
251
- echo '<td>'."\n";
252
- echo '<textarea name="ws_plugin__s2member_wp_footer_code" id="ws-plugin--s2member-wp-footer-code" rows="8" wrap="off" spellcheck="false">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["wp_footer_code"]).'</textarea><br />'."\n";
253
- echo 'Any valid XHTML / JavaScript'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' (or even PHP)').' code will work just fine here.'."\n";
254
- echo '</td>'."\n";
255
-
256
- echo '</tr>'."\n";
257
- echo '</tbody>'."\n";
258
- echo '</table>'."\n";
259
- echo '</div>'."\n";
260
-
261
- echo '</div>'."\n";
262
-
263
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_s_badge_wp_footer_code", get_defined_vars());
264
- }
265
-
266
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_email_config", TRUE, get_defined_vars()))
267
- {
268
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_email_config", get_defined_vars());
269
-
270
- echo '<div class="ws-menu-page-group" title="Email Configuration">'."\n";
271
-
272
- echo '<div class="ws-menu-page-section ws-plugin--s2member-email-section">'."\n";
273
- echo '<h3 style="margin:0;">Email From: '.esc_html('"Name" <address>').'</h3>'."\n";
274
- echo '<p style="margin:0;">This is the name/address that will appear in outgoing email notifications sent by the s2Member plugin.</p>'."\n";
275
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_email_from_name_config", get_defined_vars());
276
-
277
- echo '<table class="form-table">'."\n";
278
- echo '<tbody>'."\n";
279
- echo '<tr>'."\n";
280
-
281
- echo '<th>'."\n";
282
- echo '<label for="ws-plugin--s2member-reg-email-from-name">'."\n";
283
- echo 'Email From Name:'."\n";
284
- echo '</label>'."\n";
285
- echo '</th>'."\n";
286
-
287
- echo '</tr>'."\n";
288
- echo '<tr>'."\n";
289
-
290
- echo '<td>'."\n";
291
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_reg_email_from_name" id="ws-plugin--s2member-reg-email-from-name" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_from_name"]).'" /><br />'."\n";
292
- echo 'We recommend that you use the name of your site here.'."\n";
293
- echo '</td>'."\n";
294
-
295
- echo '</tr>'."\n";
296
- echo '<tr>'."\n";
297
-
298
- echo '<th>'."\n";
299
- echo '<label for="ws-plugin--s2member-reg-email-from-email">'."\n";
300
- echo 'Email From Address:'."\n";
301
- echo '</label>'."\n";
302
- echo '</th>'."\n";
303
-
304
- echo '</tr>'."\n";
305
- echo '<tr>'."\n";
306
-
307
- echo '<td>'."\n";
308
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_reg_email_from_email" id="ws-plugin--s2member-reg-email-from-email" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_from_email"]).'" /><br />'."\n";
309
- echo 'Example: support@your-domain.com. <em class="ws-menu-page-hilite">Please read <a href="#" onclick="alert(\'Running WordPress with an SMTP mail plugin?\\n\\nPlease be advised. If you run an SMTP mail plugin with WordPress, be sure to configure s2Member with a valid `From:` address (i.e. one matching your SMTP configuration perhaps). Most free SMTP servers, such as Gmail and Yahoo, require that your `From:` header match the email address associated with your account. Please check with your SMTP service provider before attempting to configure plugins like s2Member to use a different `From:` address when sending email messages.\'); return false;">this IMPORTANT note</a></em>.'."\n";
310
- echo '</td>'."\n";
311
-
312
- echo '</tr>'."\n";
313
- echo '<tr>'."\n";
314
-
315
- echo '<th>'."\n";
316
- echo '<label for="ws-plugin--s2member-reg-email-support-link">'."\n";
317
- echo 'Email Support/Contact Link:'."\n";
318
- echo '</label>'."\n";
319
- echo '</th>'."\n";
320
-
321
- echo '</tr>'."\n";
322
- echo '<tr>'."\n";
323
-
324
- echo '<td>'."\n";
325
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_reg_email_support_link" id="ws-plugin--s2member-reg-email-support-link" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_support_link"]).'" /><br />'."\n";
326
- echo 'Ex: <code>mailto:support@your-domain.com</code> (<em>mailto link</em>).<br />'."\n";
327
- echo 'Or: <code>'.esc_html(home_url("/contact-us/")).'</code>.'."\n";
328
- echo '</td>'."\n";
329
-
330
- echo '</tr>'."\n";
331
- echo '</tbody>'."\n";
332
- echo '</table>'."\n";
333
-
334
- echo '<div class="ws-menu-page-hr"></div>'."\n";
335
-
336
- echo '<h3 style="margin:0;">New User Email Configuration</h3>'."\n";
337
- echo '<input type="hidden" id="ws-plugin--s2member-pluggables-wp-new-user-notification" value="'.esc_attr((empty($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["pluggables"]["wp_new_user_notification"])) ? '0' : '1').'" />'."\n";
338
- echo (empty($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["pluggables"]["wp_new_user_notification"])) ? '<p class="ws-menu-page-error" style="margin:0;"><em><strong>Conflict warning:</strong> You have another theme or plugin installed that is preventing s2Member from controlling this aspect of your installation. When the pluggable function <code><a href="http://codex.wordpress.org/Function_Reference/wp_new_user_notification" target="_blank" rel="external">wp_new_user_notification()</a></code> is handled by another plugin, it\'s not possible for s2Member to allow customization of New User Emails. This is NOT a major issue. In fact, in some cases, it might be desirable. That being said, if you DO want to use s2Member\'s customization of New User Emails, you will need to deactivate one plugin at a time until this conflict warning goes away.</em></p>'."\n" : '';
339
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_emails", get_defined_vars());
340
-
341
- echo '<table class="form-table">'."\n";
342
- echo '<tbody>'."\n";
343
- echo '<tr>'."\n";
344
-
345
- echo '<td>'."\n";
346
- echo '<select name="ws_plugin__s2member_new_user_emails_enabled" id="ws-plugin--s2member-new-user-emails-enabled">'."\n";
347
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_emails_enabled"]) ? ' selected="selected"' : '').'>No (default, use WordPress defaults)</option>'."\n";
348
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_emails_enabled"]) ? ' selected="selected"' : '').'>Yes (customize New User Emails with s2Member)</option>'."\n";
349
- echo '</select>'."\n";
350
- echo '</td>'."\n";
351
-
352
- echo '</tr>'."\n";
353
- echo '</tbody>'."\n";
354
- echo '</table>'."\n";
355
-
356
- echo '<div id="ws-plugin--s2member-new-user-emails">'."\n";
357
-
358
- echo '<div class="ws-menu-page-hr"></div>'."\n";
359
-
360
- echo '<h3 style="margin:0;">New User Email Message (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-new-user-email-details\').toggle(); return false;" class="ws-dotted-link">click to customize</a>)</h3>'."\n";
361
- echo '<p style="margin:0;">This email is sent to all new Users/Members. It should always contain their Username/Password. In addition to this email, s2Member will also send new paying Customers a Signup Confirmation Email, which you can customize from your Dashboard, under: <code>s2Member -› PayPal Options</code>. You may wish to customize these emails further, by providing details that are specifically geared to your site.</p>'."\n";
362
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_email", get_defined_vars());
363
-
364
- echo '<div id="ws-plugin--s2member-new-user-email-details" style="display:none;">'."\n";
365
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> please note that BuddyPress does NOT send this email to Users that register through the BuddyPress registration system. This is because BuddyPress sends each User an activation link; eliminating the need for this email all together. However, you CAN still customize s2Member\'s separate email to paying Members. See: <code>s2Member -› PayPal Options -› Signup Confirmation Email</code>.</em></p>'."\n" : '';
366
- echo '<table class="form-table">'."\n";
367
- echo '<tbody>'."\n";
368
- echo '<tr>'."\n";
369
-
370
- echo '<th>'."\n";
371
- echo '<label for="ws-plugin--s2member-new-user-email-subject">'."\n";
372
- echo 'New User Email Subject:'."\n";
373
- echo '</label>'."\n";
374
- echo '</th>'."\n";
375
-
376
- echo '</tr>'."\n";
377
- echo '<tr>'."\n";
378
-
379
- echo '<td>'."\n";
380
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_new_user_email_subject" id="ws-plugin--s2member-new-user-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_email_subject"]).'" /><br />'."\n";
381
- echo 'Subject Line used in the email sent to new Users/Members.'."\n";
382
- echo '</td>'."\n";
383
-
384
- echo '</tr>'."\n";
385
- echo '<tr>'."\n";
386
-
387
- echo '<th>'."\n";
388
- echo '<label for="ws-plugin--s2member-new-user-email-message">'."\n";
389
- echo 'New User Email Message:'."\n";
390
- echo '</label>'."\n";
391
- echo '</th>'."\n";
392
-
393
- echo '</tr>'."\n";
394
- echo '<tr>'."\n";
395
-
396
- echo '<td>'."\n";
397
- echo '<textarea name="ws_plugin__s2member_new_user_email_message" id="ws-plugin--s2member-new-user-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_email_message"]).'</textarea><br />'."\n";
398
- echo 'Message Body used in the email sent to new Users/Members.<br /><br />'."\n";
399
- echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
400
- echo '<ul>'."\n";
401
- echo '<li><code>%%role%%</code> = The Role ID <code>(subscriber, s2member_level[0-9]+, administrator, editor, author, contributor)</code>.</li>'."\n";
402
- echo '<li><code>%%label%%</code> = The Role ID Label <code>(Subscriber, s2Member Level 1, s2Member Level 2; or your own custom Labels — if configured)</code>.</li>'."\n";
403
- echo '<li><code>%%level%%</code> = The Level number <code>(0, 1, 2, 3, 4)</code>. (<em>deprecated, no longer recommended; use <code>%%role%%</code></em>)</li>'."\n";
404
- echo '<li><code>%%ccaps%%</code> = Custom Capabilities. Ex: <code>music,videos,free_gift</code> (<em>in comma-delimited format</em>).</li>'."\n";
405
- echo '<li><code>%%user_first_name%%</code> = The First Name of the Member who registered their Username.</li>'."\n";
406
- echo '<li><code>%%user_last_name%%</code> = The Last Name of the Member who registered their Username.</li>'."\n";
407
- echo '<li><code>%%user_full_name%%</code> = The Full Name (First &amp; Last) of the Member who registered their Username.</li>'."\n";
408
- echo '<li><code>%%user_email%%</code> = The Email Address of the Member who registered their Username.</li>'."\n";
409
- echo '<li><code>%%user_login%%</code> = The Username the Member selected during registration.</li>'."\n";
410
- echo '<li><code>%%user_pass%%</code> = The Password selected or generated during registration.</li>'."\n";
411
- echo '<li><code>%%user_ip%%</code> = The User\'s IP Address, detected via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
412
- echo '<li><code>%%user_id%%</code> = A unique WordPress User ID generated during registration.</li>'."\n";
413
- echo '<li><code>%%wp_login_url%%</code> = The full URL where Users can get logged into your site.</li>'."\n";
414
- echo '</ul>'."\n";
415
-
416
- echo '<strong>Custom Registration/Profile Fields are also supported in this email:</strong>'."\n";
417
- echo '<ul>'."\n";
418
- echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
419
- echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
420
- echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
421
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
422
- echo '</ul>'."\n";
423
-
424
- echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
425
- echo '<ul>'."\n";
426
- echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
427
- echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
428
- echo '</ul>'."\n";
429
- echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
430
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
431
- echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
432
-
433
- echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
434
- '<div class="ws-menu-page-hr"></div>'."\n".
435
- '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags — optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$user</code> variable is the most important one. It\'s an instance of the <a href="http://codex.wordpress.org/Class_Reference/WP_User" target="_blank" rel="external"><code>WP_User</code></a> class (e.g. <code>$user->ID</code>, <code>$user->has_cap()</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
436
- : '';
437
- echo '</td>'."\n";
438
-
439
- echo '</tr>'."\n";
440
- echo '</tbody>'."\n";
441
- echo '</table>'."\n";
442
- echo '</div>'."\n";
443
-
444
- echo '<div class="ws-menu-page-hr"></div>'."\n";
445
-
446
- echo '<h3 style="margin:0;">Administrative: New User Notification (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-new-user-admin-email-details\').toggle(); return false;" class="ws-dotted-link">click to customize</a>)</h3>'."\n";
447
- echo '<p style="margin:0;">This email notification is sent to you, each time a new User/Member registers.</p>'."\n";
448
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_admin_email", get_defined_vars());
449
-
450
- echo '<div id="ws-plugin--s2member-new-user-admin-email-details" style="display:none;">'."\n";
451
- echo '<table class="form-table">'."\n";
452
- echo '<tbody>'."\n";
453
- echo '<tr>'."\n";
454
-
455
- echo '<th>'."\n";
456
- echo '<label for="ws-plugin--s2member-new-user-admin-email-recipients">'."\n";
457
- echo 'New User Notification Recipients:'."\n";
458
- echo '</label>'."\n";
459
- echo '</th>'."\n";
460
-
461
- echo '</tr>'."\n";
462
- echo '<tr>'."\n";
463
-
464
- echo '<td>'."\n";
465
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_new_user_admin_email_recipients" id="ws-plugin--s2member-new-user-admin-email-recipients" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_admin_email_recipients"]).'" /><br />'."\n";
466
- echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:<br />'."\n";
467
- echo '<code>"Name" &lt;user@example.com&gt;; admin@example.com; "Webmaster" &lt;webmaster@example.com&gt;</code>'."\n";
468
- echo '</td>'."\n";
469
-
470
- echo '</tr>'."\n";
471
- echo '<tr>'."\n";
472
-
473
- echo '<th>'."\n";
474
- echo '<label for="ws-plugin--s2member-new-user-admin-email-subject">'."\n";
475
- echo 'New User Notification Subject:'."\n";
476
- echo '</label>'."\n";
477
- echo '</th>'."\n";
478
-
479
- echo '</tr>'."\n";
480
- echo '<tr>'."\n";
481
-
482
- echo '<td>'."\n";
483
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_new_user_admin_email_subject" id="ws-plugin--s2member-new-user-admin-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_admin_email_subject"]).'" /><br />'."\n";
484
- echo 'Subject Line used in the email notification sent to Administrator.'."\n";
485
- echo '</td>'."\n";
486
-
487
- echo '</tr>'."\n";
488
- echo '<tr>'."\n";
489
-
490
- echo '<th>'."\n";
491
- echo '<label for="ws-plugin--s2member-new-user-admin-email-message">'."\n";
492
- echo 'New User Notification Message:'."\n";
493
- echo '</label>'."\n";
494
- echo '</th>'."\n";
495
-
496
- echo '</tr>'."\n";
497
- echo '<tr>'."\n";
498
-
499
- echo '<td>'."\n";
500
- echo '<textarea name="ws_plugin__s2member_new_user_admin_email_message" id="ws-plugin--s2member-new-user-admin-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_admin_email_message"]).'</textarea><br />'."\n";
501
- echo 'Message Body used in the email notification sent to Administrator.<br /><br />'."\n";
502
- echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
503
- echo '<ul>'."\n";
504
- echo '<li><code>%%role%%</code> = The Role ID <code>(subscriber, s2member_level[0-9]+, administrator, editor, author, contributor)</code>.</li>'."\n";
505
- echo '<li><code>%%label%%</code> = The Role ID Label <code>(Subscriber, s2Member Level 1, s2Member Level 2; or your own custom Labels — if configured)</code>.</li>'."\n";
506
- echo '<li><code>%%level%%</code> = The Level number <code>(0, 1, 2, 3, 4)</code>. (<em>deprecated, no longer recommended; use <code>%%role%%</code></em>)</li>'."\n";
507
- echo '<li><code>%%ccaps%%</code> = Custom Capabilities. Ex: <code>music,videos,free_gift</code> (<em>in comma-delimited format</em>).</li>'."\n";
508
- echo '<li><code>%%user_first_name%%</code> = The First Name of the Member who registered their Username.</li>'."\n";
509
- echo '<li><code>%%user_last_name%%</code> = The Last Name of the Member who registered their Username.</li>'."\n";
510
- echo '<li><code>%%user_full_name%%</code> = The Full Name (First &amp; Last) of the Member who registered their Username.</li>'."\n";
511
- echo '<li><code>%%user_email%%</code> = The Email Address of the Member who registered their Username.</li>'."\n";
512
- echo '<li><code>%%user_login%%</code> = The Username the Member selected during registration.</li>'."\n";
513
- echo '<li><code>%%user_pass%%</code> = The Password selected or generated during registration.</li>'."\n";
514
- echo '<li><code>%%user_ip%%</code> = The User\'s IP Address, detected via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
515
- echo '<li><code>%%user_id%%</code> = A unique WordPress User ID generated during registration.</li>'."\n";
516
- echo '<li><code>%%wp_login_url%%</code> = The full URL where Users can get logged into your site.</li>'."\n";
517
- echo '</ul>'."\n";
518
-
519
- echo '<strong>Custom Registration/Profile Fields are also supported in this email:</strong>'."\n";
520
- echo '<ul>'."\n";
521
- echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
522
- echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
523
- echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
524
- echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member -› General Options -› Registration/Profile Fields</em>.</li>'."\n";
525
- echo '</ul>'."\n";
526
-
527
- echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
528
- echo '<ul>'."\n";
529
- echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
530
- echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
531
- echo '</ul>'."\n";
532
- echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
533
- echo '<em>(The campaign (i.e. christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
534
- echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
535
-
536
- echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
537
- '<div class="ws-menu-page-hr"></div>'."\n".
538
- '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags — optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$user</code> variable is the most important one. It\'s an instance of the <a href="http://codex.wordpress.org/Class_Reference/WP_User" target="_blank" rel="external"><code>WP_User</code></a> class (e.g. <code>$user->ID</code>, <code>$user->has_cap()</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
539
- : '';
540
- echo '</td>'."\n";
541
-
542
- echo '</tr>'."\n";
543
- echo '</tbody>'."\n";
544
- echo '</table>'."\n";
545
- echo '</div>'."\n";
546
- echo '</div>'."\n";
547
-
548
- echo '</div>'."\n";
549
-
550
- echo '</div>'."\n";
551
-
552
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_email_config", get_defined_vars());
553
- }
554
-
555
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_open_registration", TRUE, get_defined_vars()))
556
- {
557
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_open_registration", get_defined_vars());
558
-
559
- if(is_multisite() && is_main_site()) // A Multisite Network, and we're on the Main Site?
560
- {
561
- echo '<div class="ws-menu-page-group" title="Open Registration">'."\n";
562
-
563
- echo '<div class="ws-menu-page-section ws-plugin--s2member-open-registration-section">'."\n";
564
- echo '<h3>Open Registration / Free Subscribers (optional)</h3>'."\n";
565
- echo '<p>On the Main Site of a Multisite Network, the settings for Open Registration are consolidated into the <code>s2Member -› Multisite (Config)</code> panel.</p>'."\n";
566
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_open_registration", get_defined_vars());
567
- echo '</div>'."\n";
568
-
569
- echo '</div>'."\n";
570
- }
571
- else // Else we display this section normally. No special considerations are required in this case.
572
- {
573
- echo '<div class="ws-menu-page-group" title="Open Registration">'."\n";
574
-
575
- echo '<div class="ws-menu-page-section ws-plugin--s2member-open-registration-section">'."\n";
576
- echo '<h3>Open Registration / Free Subscribers (optional)</h3>'."\n";
577
- echo '<p>s2Member supports Free Subscribers (at Level #0), along with four Primary Levels [1-4] of paid Membership. If you want your visitors to be capable of registering absolutely free, you will want to "allow" Open Registration. Whenever a visitor registers without paying, they\'ll automatically become a Free Subscriber, at Level #0.</p>'."\n";
578
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_open_registration", get_defined_vars());
579
-
580
- echo '<table class="form-table">'."\n";
581
- echo '<tbody>'."\n";
582
- echo '<tr>'."\n";
583
-
584
- echo '<th>'."\n";
585
- echo '<label for="ws-plugin--s2member-allow-subscribers-in">'."\n";
586
- echo 'Allow Open Registration? (Free Subscribers)'."\n";
587
- echo '</label>'."\n";
588
- echo '</th>'."\n";
589
-
590
- echo '</tr>'."\n";
591
- echo '<tr>'."\n";
592
-
593
- echo '<td>'."\n";
594
- echo '<select name="ws_plugin__s2member_allow_subscribers_in" id="ws-plugin--s2member-allow-subscribers-in">'."\n";
595
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["allow_subscribers_in"]) ? ' selected="selected"' : '').'>No (do NOT allow Open Registration)</option>'."\n";
596
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["allow_subscribers_in"]) ? ' selected="selected"' : '').'>Yes (allow Open Registration; Free Subscribers at Level #0)</option>'."\n";
597
- echo '</select><br />'."\n";
598
- echo 'If you set this to <code>Yes</code>, you\'re unlocking <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">/wp-login.php?action=register</a>. When a visitor registers without paying, they\'ll automatically become a Free Subscriber, at Level #0. The s2Member software reserves Level #0; to be used ONLY for Free Subscribers. All other Membership Levels [1-4] require payment.'."\n";
599
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><br /><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.\');">here</a>.<br />s2Member integrates with BuddyPress, and the above setting will control Open Regisration for BuddyPress too.</em>'."\n" : '';
600
- echo '</td>'."\n";
601
-
602
- echo '</tr>'."\n";
603
- echo '</tbody>'."\n";
604
- echo '</table>'."\n";
605
- echo '</div>'."\n";
606
-
607
- echo '</div>'."\n";
608
- }
609
-
610
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_open_registration", get_defined_vars());
611
- }
612
-
613
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_membership_levels", TRUE, get_defined_vars()))
614
- {
615
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_membership_levels", get_defined_vars());
616
-
617
- echo '<div class="ws-menu-page-group" title="Membership Levels/Labels">'."\n";
618
-
619
- echo '<div class="ws-menu-page-section ws-plugin--s2member-membership-levels-section">'."\n";
620
- echo '<h3>Membership Levels (required, please customize these)</h3>'."\n";
621
- echo '<p>The default Membership Levels are labeled generically; feel free to modify them as needed. s2Member supports Free Subscribers <em>(at Level #0)</em>, along with several Primary Roles for paid Membership <em>(i.e. Levels 1-4)</em>, created by the s2Member plugin.'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' s2Member also supports unlimited Custom Capability Packages <em>(see <code>s2Member -› API Scripting -› Custom Capabilities</code>)</em>' : '').'. That being said, you don\'t have to use all of the Membership Levels if you don\'t want to. To use only 1 or 2 of these Levels, just design your Membership Options Page, so it only includes Payment Buttons for the Levels being used.</p>'."\n";
622
- echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '<p><em><strong>TIP:</strong> <strong>Unlimited Membership Levels</strong> are only possible with <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Module / Prices")).'" target="_blank" rel="external">s2Member Pro</a>. However, Custom Capabilities are possible in all versions of s2Member, including the free version. Custom Capabilities are a great way to extend s2Member in creative ways. If you\'re an advanced site owner, a theme designer, or a web developer integrating s2Member for a client, please check your Dashboard, under: <code>s2Member -› API Scripting -› Custom Capabilities</code>. We also recommend <a href="http://www.s2member.com/videos/A2C07377CF60025E/" target="_blank" rel="external">this video tutorial</a>.</em></p>'."\n" : '';
623
- echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '<p><strong>See also:</strong> These KB articles: <a href="http://www.s2member.com/kb/roles-caps/" target="_blank" rel="external">s2Member Roles/Capabilities</a> and <a href="http://www.s2member.com/kb/simple-shortcode-conditionals/" target="_blank" rel="external">Simple Shortcode Conditionals</a>.</p>'."\n" : '';
624
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_membership_levels", get_defined_vars());
625
-
626
- echo '<table class="form-table">'."\n";
627
- echo '<tbody>'."\n";
628
-
629
- for($n = 0; $n <= $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; $n++)
630
- {
631
- echo '<tr>'."\n";
632
-
633
- echo '<th>'."\n";
634
- echo '<label for="ws-plugin--s2member-level'.$n.'-label">'."\n";
635
- echo ($n === 0) ? 'Level #'.$n.' <em>(Free Subscribers)</em>:'."\n" : 'Level #'.$n.' Members:'."\n";
636
- echo '</label>'."\n";
637
- echo '</th>'."\n";
638
-
639
- echo '</tr>'."\n";
640
- echo '<tr>'."\n";
641
-
642
- echo '<td>'."\n";
643
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_label" id="ws-plugin--s2member-level'.$n.'-label" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_label"]).'" /><br />'."\n";
644
- echo 'This is the Label for Level #'.$n.(($n === 0) ? ' (Free Subscribers)' : ' Members').'.<br />'."\n";
645
- echo '</td>'."\n";
646
-
647
- echo '</tr>'."\n";
648
- }
649
-
650
- echo '</tbody>'."\n";
651
- echo '</table>'."\n";
652
-
653
- echo '<div class="ws-menu-page-hr"></div>'."\n";
654
-
655
- echo '<table class="form-table" style="margin-top:0;">'."\n";
656
- echo '<tbody>'."\n";
657
- echo '<tr>'."\n";
658
-
659
- echo '<th style="padding-top:0;">'."\n";
660
- echo '<label for="ws-plugin--s2member-apply-label-translations">'."\n";
661
- echo 'Force WordPress to use your Labels?'."\n";
662
- echo '</label>'."\n";
663
- echo '</th>'."\n";
664
-
665
- echo '</tr>'."\n";
666
- echo '<tr>'."\n";
667
 
668
- echo '<td>'."\n";
669
- echo '<input type="radio" name="ws_plugin__s2member_apply_label_translations" id="ws-plugin--s2member-apply-label-translations-0" value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["apply_label_translations"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-apply-label-translations-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_apply_label_translations" id="ws-plugin--s2member-apply-label-translations-1" value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["apply_label_translations"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-apply-label-translations-1">Yes, force WordPress to use my Labels.</label><br />'."\n";
670
- echo 'This affects your administrative Dashboard only <em>(i.e. your list of Users)</em>.<br />s2Member can force WordPress to use your Labels instead of referencing Roles by `s2Member Level #`. If this is your first installation of s2Member, we suggest leaving this set to <code>no</code> until you\'ve had a chance to get acclimated with s2Member\'s functionality. In fact, many site owners choose to leave this off, because they find it less confusing when Roles are referred to by their s2Member Level #.'."\n";
671
- echo '</td>'."\n";
672
-
673
- echo '</tr>'."\n";
674
- echo '</tbody>'."\n";
675
- echo '</table>'."\n";
676
 
677
- echo '<div class="ws-menu-page-hr"></div>'."\n";
 
 
 
 
678
 
679
- echo '<input type="button" value="Reset Roles/Capabilities" class="ws-menu-page-right ws-plugin--s2member-reset-roles-button" style="min-width:175px;" />'."\n";
680
- echo '<p>The button to the right, is a nifty tool, which allows you to reset s2Member\'s internal Roles and Capabilities that integrate with WordPress. If you, or a developer working with you, has made attempts to alter the default <em>internal</em> Role/Capability sets that come with s2Member, and you need to reset them back to the way s2Member expects them to be, please use this tool. <em>Attn Developers: it is also possible lock-in your modified Roles/Capabilities with an s2Member Filter. Please see <a href="http://www.s2member.com/kb/roles-caps/#modifying-roles-caps" target="_blank" rel="external">this KB article for details</a>.</em></p>'."\n";
 
 
681
 
682
- echo '</div>'."\n";
 
 
683
 
684
- echo '</div>'."\n";
 
 
 
 
685
 
686
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_membership_levels", get_defined_vars());
687
- }
 
 
 
 
 
 
 
 
 
688
 
689
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_login_registration", TRUE, get_defined_vars()))
690
- {
691
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_login_registration", get_defined_vars());
 
692
 
693
- echo '<div class="ws-menu-page-group" title="Login/Registration Design">'."\n";
 
 
 
 
 
 
694
 
695
- echo '<div class="ws-menu-page-section ws-plugin--s2member-login-registration-section">'."\n";
696
- echo '<h3>Login/Registration Page Customization (optional)</h3>'."\n";
697
- echo '<p>These settings customize your Standard Login/Registration Pages:<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_register_url()).'</a>)</p>'."\n";
698
- echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '<p><em>The Main Site of a Multisite Blog Farm uses this Form instead, powered by your theme.<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Multisite Registration Form.\\n* s2Member makes this form available to logged-in Super Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'</a>)</em></p>'."\n" : '';
699
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form, powered by your theme.<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.\');">'.esc_html(c_ws_plugin__s2member_utils_urls::bp_register_url()).'</a>)</em></p>'."\n" : '';
700
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_login_registration", get_defined_vars());
701
 
702
- echo '<table class="form-table">'."\n";
703
- echo '<tbody>'."\n";
704
- echo '<tr>'."\n";
 
 
 
705
 
706
- echo '<td>'."\n";
707
- echo '<h3 style="margin:0;">Enable This Functionality?</h3>'."\n";
708
- echo '<select name="ws_plugin__s2member_login_reg_design_enabled" id="ws-plugin--s2member-login-reg-design-enabled">'."\n";
709
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_design_enabled"]) ? ' selected="selected"' : '').'>No (default, use WordPress defaults)</option>'."\n";
710
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_design_enabled"]) ? ' selected="selected"' : '').'>Yes (customize Login/Registration with s2Member)</option>'."\n";
711
- echo '</select>'."\n";
712
- echo '</td>'."\n";
713
 
714
- echo '</tr>'."\n";
715
- echo '</tbody>'."\n";
716
- echo '</table>'."\n";
717
 
718
- echo '<div id="ws-plugin--s2member-login-reg-design"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_design_enabled"]) ? ' style="display:none;"' : '').'>'."\n";
 
 
 
 
719
 
720
- echo '<div class="ws-menu-page-hr"></div>'."\n";
 
721
 
722
- echo '<table class="form-table">'."\n";
723
- echo '<tbody>'."\n";
724
- echo '<tr>'."\n";
 
725
 
726
- echo '<td>'."\n";
727
- echo '<h3 style="margin:0;">Overall Font/Size Configuration</h3>'."\n";
728
- echo '<p style="margin:0;">These settings are all focused on your Login/Registration Fonts.</p>'."\n";
729
- echo '</td>'."\n";
730
 
731
- echo '</tr>'."\n";
732
- echo '<tr>'."\n";
733
 
734
- echo '<th>'."\n";
735
- echo '<label for="ws-plugin--s2member-login-reg-font-size">'."\n";
736
- echo 'Overall Font Size:'."\n";
737
- echo '</label>'."\n";
738
- echo '</th>'."\n";
739
 
740
- echo '</tr>'."\n";
741
- echo '<tr>'."\n";
 
 
 
742
 
743
- echo '<td>'."\n";
744
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_font_size" id="ws-plugin--s2member-login-reg-font-size" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_font_size"]).'" /><br />'."\n";
745
- echo 'Set this to a numeric value, calculated in pixels.'."\n";
746
- echo '</td>'."\n";
747
 
748
- echo '</tr>'."\n";
749
- echo '<tr>'."\n";
750
-
751
- echo '<th>'."\n";
752
- echo '<label for="ws-plugin--s2member-login-reg-font-family">'."\n";
753
- echo 'Overall Font Family:'."\n";
754
- echo '</label>'."\n";
755
- echo '</th>'."\n";
756
-
757
- echo '</tr>'."\n";
758
- echo '<tr>'."\n";
759
-
760
- echo '<td>'."\n";
761
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_font_family" id="ws-plugin--s2member-login-reg-font-family" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_font_family"]).'" /><br />'."\n";
762
- echo 'Set this to a web-safe font family.'."\n";
763
- echo '</td>'."\n";
764
-
765
- echo '</tr>'."\n";
766
- echo '<tr>'."\n";
767
-
768
- echo '<th>'."\n";
769
- echo '<label for="ws-plugin--s2member-login-reg-font-field-size">'."\n";
770
- echo 'Form Field Font Size:'."\n";
771
- echo '</label>'."\n";
772
- echo '</th>'."\n";
773
-
774
- echo '</tr>'."\n";
775
- echo '<tr>'."\n";
776
-
777
- echo '<td>'."\n";
778
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_font_field_size" id="ws-plugin--s2member-login-reg-font-field-size" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_font_field_size"]).'" /><br />'."\n";
779
- echo 'Set this to a numeric value, calculated in pixels.'."\n";
780
- echo '</td>'."\n";
781
-
782
- echo '</tr>'."\n";
783
- echo '</tbody>'."\n";
784
- echo '</table>'."\n";
785
-
786
- echo '<div class="ws-menu-page-hr"></div>'."\n";
787
-
788
- echo '<table class="form-table" style="margin-top:0;">'."\n";
789
- echo '<tbody>'."\n";
790
- echo '<tr>'."\n";
791
-
792
- echo '<td>'."\n";
793
- echo '<h3 style="margin:0;">Background Configuration</h3>'."\n";
794
- echo '<p style="margin:0;">These settings are all focused on your Login/Registration Background.</p>'."\n";
795
- echo '</td>'."\n";
796
-
797
- echo '</tr>'."\n";
798
- echo '<tr>'."\n";
799
-
800
- echo '<th>'."\n";
801
- echo '<label for="ws-plugin--s2member-login-reg-background-color">'."\n";
802
- echo 'Background Color:'."\n";
803
- echo '</label>'."\n";
804
- echo '</th>'."\n";
805
-
806
- echo '</tr>'."\n";
807
- echo '<tr>'."\n";
808
-
809
- echo '<td>'."\n";
810
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_color" id="ws-plugin--s2member-login-reg-background-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_color"]).'" /><br />'."\n";
811
- echo 'Set this to a 6-digit hex color code.'."\n";
812
- echo '</td>'."\n";
813
-
814
- echo '</tr>'."\n";
815
- echo '<tr>'."\n";
816
-
817
- echo '<th>'."\n";
818
- echo '<label for="ws-plugin--s2member-login-reg-background-image">'."\n";
819
- echo 'Background Image:'."\n";
820
- echo '</label>'."\n";
821
- echo '</th>'."\n";
822
-
823
- echo '</tr>'."\n";
824
- echo '<tr>'."\n";
825
-
826
- echo '<td>'."\n";
827
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_image" id="ws-plugin--s2member-login-reg-background-image" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image"]).'" /><br />'."\n";
828
- echo '<input type="button" id="ws-plugin--s2member-login-reg-background-image-media-btn" value="Open Media Library" class="ws-menu-page-media-btn" rel="ws-plugin--s2member-login-reg-background-image" />'."\n";
829
- echo 'Set this to the URL of your Background Image. (this is optional)<br />';
830
- echo 'If supplied, your Background Image will be tiled.'."\n";
831
- echo '</td>'."\n";
832
-
833
- echo '</tr>'."\n";
834
- echo '<tr>'."\n";
835
-
836
- echo '<th>'."\n";
837
- echo '<label for="ws-plugin--s2member-login-reg-background-image-repeat">'."\n";
838
- echo 'Background Image Tile:'."\n";
839
- echo '</label>'."\n";
840
- echo '</th>'."\n";
841
-
842
- echo '</tr>'."\n";
843
- echo '<tr>'."\n";
844
-
845
- echo '<td>'."\n";
846
- echo '<select name="ws_plugin__s2member_login_reg_background_image_repeat" id="ws-plugin--s2member-login-reg-background-image-repeat">'."\n";
847
- echo '<option value="repeat"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "repeat") ? ' selected="selected"' : '').'>Seamless Tile ( background-repeat: repeat; )</option>'."\n";
848
- echo '<option value="repeat-x"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "repeat-x") ? ' selected="selected"' : '').'>Tile Horizontally ( background-repeat: repeat-x; )</option>'."\n";
849
- echo '<option value="repeat-y"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "repeat-y") ? ' selected="selected"' : '').'>Tile Vertically ( background-repeat: repeat-y; )</option>'."\n";
850
- echo '<option value="no-repeat"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "no-repeat") ? ' selected="selected"' : '').'>No Tiles ( background-repeat: no-repeat; )</option>'."\n";
851
- echo '</select><br />'."\n";
852
- echo 'This controls the way your Background Image is styled with CSS. [ <a href="http://www.w3schools.com/css/pr_background-repeat.asp" target="_blank" rel="external">learn more</a> ]'."\n";
853
- echo '</td>'."\n";
854
-
855
- echo '</tr>'."\n";
856
- echo '<tr>'."\n";
857
-
858
- echo '<th>'."\n";
859
- echo '<label for="ws-plugin--s2member-login-reg-background-text-color">'."\n";
860
- echo 'Color of Text on top of your Background:'."\n";
861
- echo '</label>'."\n";
862
- echo '</th>'."\n";
863
-
864
- echo '</tr>'."\n";
865
- echo '<tr>'."\n";
866
-
867
- echo '<td>'."\n";
868
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_text_color" id="ws-plugin--s2member-login-reg-background-text-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_text_color"]).'" /><br />'."\n";
869
- echo 'Set this to a 6-digit hex color code.'."\n";
870
- echo '</td>'."\n";
871
-
872
- echo '</tr>'."\n";
873
- echo '<tr>'."\n";
874
-
875
- echo '<th>'."\n";
876
- echo '<label for="ws-plugin--s2member-login-reg-background-text-shadow-color">'."\n";
877
- echo 'Shadow Color for Text on top of your Background:'."\n";
878
- echo '</label>'."\n";
879
- echo '</th>'."\n";
880
-
881
- echo '</tr>'."\n";
882
- echo '<tr>'."\n";
883
-
884
- echo '<td>'."\n";
885
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_text_shadow_color" id="ws-plugin--s2member-login-reg-background-text-shadow-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_text_shadow_color"]).'" /><br />'."\n";
886
- echo 'Set this to a 6-digit hex color code.'."\n";
887
- echo '</td>'."\n";
888
-
889
- echo '</tr>'."\n";
890
- echo '<tr>'."\n";
891
-
892
- echo '<th>'."\n";
893
- echo '<label for="ws-plugin--s2member-login-reg-background-box-shadow-color">'."\n";
894
- echo 'Shadow Color for Boxes on top of your Background:'."\n";
895
- echo '</label>'."\n";
896
- echo '</th>'."\n";
897
-
898
- echo '</tr>'."\n";
899
- echo '<tr>'."\n";
900
-
901
- echo '<td>'."\n";
902
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_box_shadow_color" id="ws-plugin--s2member-login-reg-background-box-shadow-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_box_shadow_color"]).'" /><br />'."\n";
903
- echo 'Set this to a 6-digit hex color code.'."\n";
904
- echo '</td>'."\n";
905
-
906
- echo '</tr>'."\n";
907
- echo '</tbody>'."\n";
908
- echo '</table>'."\n";
909
-
910
- echo '<div class="ws-menu-page-hr"></div>'."\n";
911
-
912
- echo '<table class="form-table" style="margin-top:0;">'."\n";
913
- echo '<tbody>'."\n";
914
- echo '<tr>'."\n";
915
-
916
- echo '<td>'."\n";
917
- echo '<h3 style="margin:0;">Logo Image Configuration</h3>'."\n";
918
- echo '<p style="margin:0;">These settings are all focused on your Login/Registration Logo.</p>'."\n";
919
- echo '</td>'."\n";
920
-
921
- echo '</tr>'."\n";
922
- echo '<tr>'."\n";
923
-
924
- echo '<th>'."\n";
925
- echo '<label for="ws-plugin--s2member-login-reg-logo-src">'."\n";
926
- echo 'Logo Image Location:'."\n";
927
- echo '</label>'."\n";
928
- echo '</th>'."\n";
929
-
930
- echo '</tr>'."\n";
931
- echo '<tr>'."\n";
932
-
933
- echo '<td>'."\n";
934
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_src" id="ws-plugin--s2member-login-reg-logo-src" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_src"]).'" /><br />'."\n";
935
- echo '<input type="button" id="ws-plugin--s2member-login-reg-logo-src-media-btn" value="Open Media Library" class="ws-menu-page-media-btn" rel="ws-plugin--s2member-login-reg-logo-src" />'."\n";
936
- echo 'Set this to the URL of your Logo Image.<br />'."\n";
937
- echo 'Suggested size is around 500 x 100.'."\n";
938
- echo '</td>'."\n";
939
-
940
- echo '</tr>'."\n";
941
- echo '<tr>'."\n";
942
-
943
- echo '<th>'."\n";
944
- echo '<label for="ws-plugin--s2member-login-reg-logo-src-width">'."\n";
945
- echo 'Logo Image Width:'."\n";
946
- echo '</label>'."\n";
947
- echo '</th>'."\n";
948
-
949
- echo '</tr>'."\n";
950
- echo '<tr>'."\n";
951
-
952
- echo '<td>'."\n";
953
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_src_width" id="ws-plugin--s2member-login-reg-logo-src-width" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_src_width"]).'" /><br />'."\n";
954
- echo 'The pixel Width of your Logo Image. <em>* This ALSO affects the overall width of your Login/Registration forms. If you want wider form fields, use a wider Logo.</em>'."\n";
955
- echo '</td>'."\n";
956
-
957
- echo '</tr>'."\n";
958
- echo '<tr>'."\n";
959
-
960
- echo '<th>'."\n";
961
- echo '<label for="ws-plugin--s2member-login-reg-logo-src-height">'."\n";
962
- echo 'Logo Image Height:'."\n";
963
- echo '</label>'."\n";
964
- echo '</th>'."\n";
965
-
966
- echo '</tr>'."\n";
967
- echo '<tr>'."\n";
968
-
969
- echo '<td>'."\n";
970
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_src_height" id="ws-plugin--s2member-login-reg-logo-src-height" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_src_height"]).'" /><br />'."\n";
971
- echo 'The pixel Height of your Logo Image.'."\n";
972
- echo '</td>'."\n";
973
-
974
- echo '</tr>'."\n";
975
- echo '<tr>'."\n";
976
-
977
- echo '<th>'."\n";
978
- echo '<label for="ws-plugin--s2member-login-reg-logo-url">'."\n";
979
- echo 'Logo Image Click URL:'."\n";
980
- echo '</label>'."\n";
981
- echo '</th>'."\n";
982
-
983
- echo '</tr>'."\n";
984
- echo '<tr>'."\n";
985
-
986
- echo '<td>'."\n";
987
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_url" id="ws-plugin--s2member-login-reg-logo-url" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_url"]).'" /><br />'."\n";
988
- echo 'Set this to the Click URL for your Logo Image.'."\n";
989
- echo '</td>'."\n";
990
-
991
- echo '</tr>'."\n";
992
- echo '<tr>'."\n";
993
-
994
- echo '<th>'."\n";
995
- echo '<label for="ws-plugin--s2member-login-reg-logo-title">'."\n";
996
- echo 'Logo Image Title Attribute:'."\n";
997
- echo '</label>'."\n";
998
- echo '</th>'."\n";
999
-
1000
- echo '</tr>'."\n";
1001
- echo '<tr>'."\n";
1002
-
1003
- echo '<td>'."\n";
1004
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_title" id="ws-plugin--s2member-login-reg-logo-title" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_title"]).'" /><br />'."\n";
1005
- echo 'Used as the <code>title=""</code> attribute for your Logo Image.'."\n";
1006
- echo '</td>'."\n";
1007
-
1008
- echo '</tr>'."\n";
1009
- echo '</tbody>'."\n";
1010
- echo '</table>'."\n";
1011
-
1012
- echo '<div class="ws-menu-page-hr"></div>'."\n";
1013
-
1014
- echo '<table class="form-table" style="margin-top:0;">'."\n";
1015
- echo '<tbody>'."\n";
1016
- echo '<tr>'."\n";
1017
-
1018
- echo '<th>'."\n";
1019
- echo '<label for="ws-plugin--s2member-login-reg-footer-backtoblog">'."\n";
1020
- echo 'Display [Back to Home Page] Link At Bottom?'."\n";
1021
- echo '</label>'."\n";
1022
- echo '</th>'."\n";
1023
-
1024
- echo '</tr>'."\n";
1025
- echo '<tr>'."\n";
1026
-
1027
- echo '<td>'."\n";
1028
- echo '<select name="ws_plugin__s2member_login_reg_footer_backtoblog" id="ws-plugin--s2member-login-reg-footer-backtoblog">'."\n";
1029
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_footer_backtoblog"]) ? ' selected="selected"' : '').'>Yes, display link at bottom pointing visitors back to the home page</option>'."\n";
1030
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_footer_backtoblog"]) ? ' selected="selected"' : '').'>No, hide this link (I\'ll create my own custom footer w/ the details I prefer)</option>'."\n";
1031
- echo '</select>'."\n";
1032
- echo '</td>'."\n";
1033
-
1034
- echo '</tr>'."\n";
1035
- echo '<tr>'."\n";
1036
-
1037
- echo '<td>'."\n";
1038
- echo '<h3 style="margin:0;">Footer Design (i.e. Bottom)</h3>'."\n";
1039
- echo '<p style="margin:0;">This field accepts raw HTML'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' (and/or PHP)' : '').' code.</p>'."\n";
1040
- echo '</td>'."\n";
1041
-
1042
- echo '</tr>'."\n";
1043
- echo '<tr>'."\n";
1044
-
1045
- echo '<th>'."\n";
1046
- echo '<label for="ws-plugin--s2member-login-reg-footer-design">'."\n";
1047
- echo 'Login/Registration Footer Design (optional):'."\n";
1048
- echo '</label>'."\n";
1049
- echo '</th>'."\n";
1050
-
1051
- echo '</tr>'."\n";
1052
- echo '<tr>'."\n";
1053
-
1054
- echo '<td>'."\n";
1055
- echo '<textarea name="ws_plugin__s2member_login_reg_footer_design" id="ws-plugin--s2member-login-reg-footer-design" rows="3" wrap="off" spellcheck="false">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_footer_design"]).'</textarea><br />'."\n";
1056
- echo 'This optional HTML'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' (and/or PHP)' : '').' code will appear at the very bottom of your Login/Registration Forms.'."\n";
1057
- echo '</td>'."\n";
1058
-
1059
- echo '</tr>'."\n";
1060
- echo '</tbody>'."\n";
1061
- echo '</table>'."\n";
1062
- echo '</div>'."\n";
1063
-
1064
- echo '</div>'."\n";
1065
-
1066
- echo '</div>'."\n";
1067
-
1068
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_login_registration", get_defined_vars());
1069
- }
1070
-
1071
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_custom_reg_fields", TRUE, get_defined_vars()))
1072
- {
1073
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_custom_reg_fields", get_defined_vars());
1074
-
1075
- echo '<div class="ws-menu-page-group" title="Registration/Profile Fields &amp; Options">'."\n";
1076
-
1077
- echo '<div class="ws-menu-page-section ws-plugin--s2member-custom-reg-fields-section">'."\n";
1078
- echo '<h3>Custom Registration/Profile Fields (optional, for further customization)</h3>'."\n";
1079
- echo '<p>Some fields are already built-in by default. The defaults are: <code>*Username*, *Email*, *First Name*, *Last Name*</code>.</p>'."\n";
1080
-
1081
- echo '<p>Custom Fields will appear in your Standard Registration Form, and in User/Member Profiles:<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_register_url()).'</a>)</p>'."\n";
1082
- echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '<p><em>The Main Site of a Multisite Blog Farm uses this Form. s2Member supports Custom Fields here too.<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Multisite Registration Form.\\n* s2Member makes this form available to logged-in Super Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'</a>)</em></p>'."\n" : '';
1083
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.\');">here</a>.<br />s2Member can integrate your Custom Fields with BuddyPress too, please see options below.</em></p>'."\n" : '';
1084
- echo '<p><strong>Regarding registration...</strong> Custom Fields do NOT appear during repeat registration and/or checkout attempts (i.e. they do NOT appear for any user that is currently logged into the site). Please make sure that you test registration and/or checkout forms while NOT logged in (i.e. please test as a first-time customer). Existing users/members/customers may update Custom Fields by editing their Profile.</p>'."\n";
1085
-
1086
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_custom_reg_fields", get_defined_vars());
1087
-
1088
- echo '<table class="form-table">'."\n";
1089
- echo '<tbody>'."\n";
1090
- echo '<tr>'."\n";
1091
-
1092
- echo '<th>'."\n";
1093
- echo '<label>'."\n";
1094
- echo 'Custom Registration/Profile Fields:'."\n";
1095
- echo '</label>'."\n";
1096
- echo '</th>'."\n";
1097
-
1098
- echo '</tr>'."\n";
1099
- echo '<tr>'."\n";
1100
-
1101
- echo '<td>'."\n";
1102
- echo '<input type="hidden" name="ws_plugin__s2member_custom_reg_fields" id="ws-plugin--s2member-custom-reg-fields" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_fields"]).'" />'."\n";
1103
- echo '<div id="ws-plugin--s2member-custom-reg-field-configuration"></div>'."\n"; // This is filled by JavaScript routines.
1104
- echo '</td>'."\n";
1105
-
1106
- echo '</tr>'."\n";
1107
- echo '<tr>'."\n";
1108
-
1109
- echo '<th>'."\n";
1110
- echo '<label for="ws-plugin--s2member-custom-reg-names">'."\n";
1111
- echo 'Collect First/Last Names during Registration?'."\n";
1112
- echo '</label>'."\n";
1113
- echo '</th>'."\n";
1114
-
1115
- echo '</tr>'."\n";
1116
- echo '<tr>'."\n";
1117
-
1118
- echo '<td>'."\n";
1119
- echo '<select name="ws_plugin__s2member_custom_reg_names" id="ws-plugin--s2member-custom-reg-names">'."\n";
1120
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_password"]) ? ' selected="selected"' : '').'>Yes (always collect First/Last Names during registration)</option>'."\n";
1121
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_names"]) ? ' selected="selected"' : '').'>No (do NOT collect First/Last Names during registration)</option>'."\n";
1122
- echo '</select><br />'."\n";
1123
- echo 'Recommended setting (<code>Yes</code>). It\'s usually a good idea to leave this on.'."\n";
1124
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Has NO affect on BuddyPress registration form (BuddyPress always collects a full <code>Name</code> field).</em>'."\n" : '';
1125
- echo (c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? '<br /><em>* s2Member Pro (Checkout) Forms always require a First/Last Name for billing.</em>'."\n" : '';
1126
- echo '</td>'."\n";
1127
-
1128
- echo '</tr>'."\n";
1129
- echo '<tr>'."\n";
1130
-
1131
- echo '<th>'."\n";
1132
- echo '<label for="ws-plugin--s2member-custom-reg-display-name">'."\n";
1133
- echo 'Set "Display Name" during Registration?'."\n";
1134
- echo '</label>'."\n";
1135
- echo '</th>'."\n";
1136
-
1137
- echo '</tr>'."\n";
1138
- echo '<tr>'."\n";
1139
-
1140
- echo '<td>'."\n";
1141
- echo '<select name="ws_plugin__s2member_custom_reg_display_name" id="ws-plugin--s2member-custom-reg-display-name">'."\n";
1142
- echo '<option value="full"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "full") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s Full Name)</option>'."\n";
1143
- echo '<option value="first"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "first") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s First Name)</option>'."\n";
1144
- echo '<option value="last"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "last") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s Last Name)</option>'."\n";
1145
- echo '<option value="login"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "login") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s Username)</option>'."\n";
1146
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"]) ? ' selected="selected"' : '').'>No (leave Display Name at default WordPress value)</option>'."\n";
1147
- echo '</select>'."\n";
1148
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Has NO affect on BuddyPress registration form (BuddyPress always uses its full <code>Name</code> field).</em>'."\n" : '';
1149
- echo '</td>'."\n";
1150
-
1151
- echo '</tr>'."\n";
1152
- echo '<tr>'."\n";
1153
-
1154
- echo '<th>'."\n";
1155
- echo '<label for="ws-plugin--s2member-custom-reg-password">'."\n";
1156
- echo 'Allow Custom Passwords during Registration?'."\n";
1157
- echo '</label>'."\n";
1158
- echo '</th>'."\n";
1159
-
1160
- echo '</tr>'."\n";
1161
- echo '<tr>'."\n";
1162
-
1163
- echo '<td>'."\n";
1164
- echo '<select name="ws_plugin__s2member_custom_reg_password" id="ws-plugin--s2member-custom-reg-password"'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site() && !c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? ' disabled="disabled"' : '').'>'."\n";
1165
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_password"]) ? ' selected="selected"' : '').'>No (send auto-generated passwords via email; after registration)</option>'."\n";
1166
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_password"]) ? ' selected="selected"' : '').'>Yes (allow members to create their own password during registration)</option>'."\n";
1167
- echo '</select><br />'."\n";
1168
- echo 'Auto-generated Passwords are recommended for best security; because, this also serves as a form of email confirmation.'."\n";
1169
- echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '<br /><em>* For security purposes, Custom Passwords are NOT possible on the Main Site of a Blog Farm. <a href="#" onclick="alert(\'For security purposes, Custom Passwords are NOT possible on the Main Site of a Blog Farm. A User MUST wait for the activation/confirmation email; where a randomly generated Password will be assigned. Please note, this limitation only affects your Main Site, via `/wp-signup.php`. In other words, your Customers (i.e. other Blog Owners) will still have the ability to allow Custom Passwords with s2Member. YOU are affected by this limitation, NOT them.\\n\\n* NOTE: s2Member (Pro) removes this limitation.\\nIf you install the s2Member Pro Module, you WILL be able to allow Custom Passwords through s2Member Pro Forms; even on a Multisite Blog Farm.\'); return false;" tabindex="-1">[?]</a></em>'."\n" : '';
1170
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Does NOT affect BuddyPress registration form (always <code>yes</code> with BuddyPress registration).</em>'."\n" : '';
1171
- echo '</td>'."\n";
1172
-
1173
- echo '</tr>'."\n";
1174
- echo '<tr>'."\n";
1175
-
1176
- echo '<th>'."\n";
1177
- echo '<label for="ws-plugin--s2member-custom-reg-force-personal-emails">'."\n";
1178
- echo 'Force Personal Emails during Registration?'."\n";
1179
- echo '</label>'."\n";
1180
- echo '</th>'."\n";
1181
-
1182
- echo '</tr>'."\n";
1183
- echo '<tr>'."\n";
1184
-
1185
- echo '<td>'."\n";
1186
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_custom_reg_force_personal_emails" id="ws-plugin--s2member-custom-reg-force-personal-emails" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_force_personal_emails"]).'" /><br />'."\n";
1187
- echo 'To force personal email addresses, provide a comma-delimited list of email users to reject. <a href="#" onclick="alert(\'s2Member will reject [user]@ (based on your configuration here). A JavaScript alert message will be issued, asking the User to, `please use a personal email address`.\'); return false;" tabindex="-1">[?]</a><br />'."\n";
1188
- echo 'Ex: <code>info,help,admin,webmaster,hostmaster,sales,support,spam</code><br />'."\n";
1189
- echo 'See: <a href="http://kb.mailchimp.com/article/what-role-addresses-does-mailchimp-specifically-block-from-bulk-importing/" target="_blank" rel="external">this article</a> for a more complete list.'."\n";
1190
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Affects BuddyPress registration form too.</em>'."\n" : '';
1191
- echo '</td>'."\n";
1192
-
1193
- echo '</tr>'."\n";
1194
- echo '<tr>'."\n";
1195
-
1196
- echo '<th>'."\n";
1197
- echo '<label for="ws-plugin--s2member-custom-reg-fields-4bp">'."\n";
1198
- echo 'Integrate Custom Registration/Profile Fields with BuddyPress?'."\n";
1199
- echo '</label>'."\n";
1200
- echo '</th>'."\n";
1201
-
1202
- echo '</tr>'."\n";
1203
- echo '<tr>'."\n";
1204
-
1205
- echo '<td>'."\n";
1206
- echo '<div class="ws-menu-page-scrollbox" style="height:65px;">'."\n";
1207
- echo '<input type="hidden" name="ws_plugin__s2member_custom_reg_fields_4bp[]" value="update-signal"'.((!c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? ' disabled="disabled"' : '').' />'."\n";
1208
- foreach(array("profile-view" => "Yes, integrate with BuddyPress Public Profiles.", "registration" => "Yes, integrate with BuddyPress Registration Form.", "profile" => "Yes, integrate with BuddyPress Profile Editing Panel.") as $ws_plugin__s2member_temp_s_value => $ws_plugin__s2member_temp_s_label)
1209
- echo '<input type="checkbox" name="ws_plugin__s2member_custom_reg_fields_4bp[]" id="ws-plugin--s2member-custom-reg-fields-4bp-'.esc_attr(preg_replace("/[^a-z0-9_\-]/", "-", $ws_plugin__s2member_temp_s_value)).'" value="'.esc_attr($ws_plugin__s2member_temp_s_value).'"'.((in_array($ws_plugin__s2member_temp_s_value, $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_fields_4bp"])) ? ' checked="checked"' : '').((!c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? ' disabled="disabled"' : '').' /> <label for="ws-plugin--s2member-custom-reg-fields-4bp-'.esc_attr(preg_replace("/[^a-z0-9_\-]/", "-", $ws_plugin__s2member_temp_s_value)).'">'.$ws_plugin__s2member_temp_s_label.'</label><br />'."\n";
1210
- echo '</div>'."\n";
1211
- echo (!c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<em>* BuddyPress is NOT installed; which is perfectly OK. BuddyPress is NOT a requirement.</em>'."\n" : '<em>* The options above, make it possible to integrate Custom Registration/Profile Fields (i.e. those configured with s2Member) into BuddyPress as well. However, if you configure Profile Fields with BuddyPress, those will NOT be integrated with s2Member. Therefore, if you need Custom Registration/Profile Fields to work with both s2Member and with BuddyPress, please configure them with s2Member.</em>';
1212
- echo '</td>'."\n";
1213
-
1214
- echo '</tr>'."\n";
1215
- echo '</tbody>'."\n";
1216
- echo '</table>'."\n";
1217
- echo '</div>'."\n";
1218
-
1219
- echo '</div>'."\n";
1220
-
1221
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_custom_reg_fields", get_defined_vars());
1222
- }
1223
-
1224
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_login_welcome_page", TRUE, get_defined_vars()))
1225
- {
1226
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_login_welcome_page", get_defined_vars());
1227
-
1228
- echo '<div class="ws-menu-page-group" title="Login Welcome Page">'."\n";
1229
-
1230
- echo '<div class="ws-menu-page-section ws-plugin--s2member-login-welcome-page-section">'."\n";
1231
- echo '<h3>Login Welcome Page (required, please customize this)</h3>'."\n";
1232
- echo '<p>Please create and/or choose an existing Page to use as the first page Members will see after logging in.</p>'."\n";
1233
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> s2Member integrates with BuddyPress. Your Login Welcome Page affects BuddyPress too.</em></p>'."\n" : '';
1234
- echo '<p><em><strong>*Tips*</strong> This special Page will be protected from public access (automatically) by s2Member. Also, please remember this option CANNOT be set to your Front Page (i.e. your Home Page), or to your Posts Page (i.e. your main Blog page). Please create a separate Page in WordPress &amp; designate it here as your Login Welcome Page.</em></p>'."\n";
1235
- echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/customizing-your-lwp/" target="_blank" rel="external">Customizing Your Login Welcome Page</a>.</p>'."\n";
1236
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_login_welcome_page", get_defined_vars());
1237
-
1238
- echo '<table class="form-table">'."\n";
1239
- echo '<tbody>'."\n";
1240
- echo '<tr>'."\n";
1241
-
1242
- echo '<th>'."\n";
1243
- echo '<label for="ws-plugin--s2member-login-welcome-page">'."\n";
1244
- echo 'Login Welcome Page:'."\n";
1245
- echo '</label>'."\n";
1246
- echo '</th>'."\n";
1247
-
1248
- echo '</tr>'."\n";
1249
- echo '<tr>'."\n";
1250
-
1251
- echo '<td>'."\n";
1252
- echo '<select name="ws_plugin__s2member_login_welcome_page" id="ws-plugin--s2member-login-welcome-page">'."\n";
1253
- echo '<option value="">&mdash; Select &mdash;</option>'."\n";
1254
- foreach(($ws_plugin__s2member_temp_a = array_merge((array)get_pages())) as $ws_plugin__s2member_temp_o)
1255
- echo '<option value="'.esc_attr($ws_plugin__s2member_temp_o->ID).'"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_override"] && $ws_plugin__s2member_temp_o->ID == $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"]) ? ' selected="selected"' : '').'>'.esc_html($ws_plugin__s2member_temp_o->post_title).'</option>'."\n";
1256
- echo '</select><br />'."\n";
1257
- echo 'Please choose a Page to be used as the first page Members will see after logging in. This Page can contain anything you like. We recommend the following title: <code>Welcome To Our Members Area</code>.'."\n";
1258
- echo '</td>'."\n";
1259
-
1260
- echo '</tr>'."\n";
1261
- echo '<tr>'."\n";
1262
-
1263
- echo '<th>'."\n";
1264
- echo '<label for="ws-plugin--s2member-login-redirection-override">'."\n";
1265
- echo 'Or, a Special Redirection URL?'."\n";
1266
- echo '</label>'."\n";
1267
- echo '</th>'."\n";
1268
-
1269
- echo '</tr>'."\n";
1270
- echo '<tr>'."\n";
1271
-
1272
- echo '<td>'."\n";
1273
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_redirection_override" id="ws-plugin--s2member-login-redirection-override" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_override"]).'" /><br />'."\n";
1274
- echo 'Or, you may configure a Special Redirection URL, if you prefer. You\'ll need to type in the full URL, starting with: <code>http://</code>. <em>A few <a href="#" onclick="alert(\'Replacement Codes:\\n\\n%%current_user_login%% = The current User\\\'s Username, lowercase (deprecated, please use %%current_user_nicename%%).\\n\\n%%current_user_nicename%% = The current User\\\'s Nicename in lowercase format (i.e. a cleaner version of the username for URLs; recommended for best compatibility).\\n\\n%%current_user_id%% = The current User\\\'s ID.\\n\\n%%current_user_level%% = The current User\\\'s s2Member Level.\\n\\n%%current_user_role%% = The current User\\\'s WordPress Role.'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '\\n\\n%%current_user_ccaps%% = The current User\\\'s Custom Capabilities.' : '').'\\n\\n%%current_user_logins%% = Number of times the current User has logged in.\\n\\nFor example, if you\\\'re using BuddyPress, and you want to redirect Members to their BuddyPress Profile page after logging in, you would setup a Special Redirection URL, like this: '.home_url("/members/%%current_user_nicename%%/profile/").'\\n\\nOr ... using %%current_user_level%%, you could have a separate Login Welcome Page for each Membership Level that you plan to offer. BuddyPress not required.\'); return false;">Replacement Codes</a> are also supported here.</em>'."\n";
1275
- echo '</td>'."\n";
1276
-
1277
- echo '</tr>'."\n";
1278
- echo '</tbody>'."\n";
1279
- echo '</table>'."\n";
1280
-
1281
- echo '<div class="ws-menu-page-hr"></div>'."\n";
1282
-
1283
- echo '<table class="form-table" style="margin-top:0;">'."\n";
1284
- echo '<tbody>'."\n";
1285
- echo '<tr>'."\n";
1286
-
1287
- echo '<th>'."\n";
1288
- echo '<label for="ws-plugin--s2member-login-redirection-always-http">'."\n";
1289
- echo 'Always Redirect non-Administrative Users (after login) using HTTP?'."\n";
1290
- echo '</label>'."\n";
1291
- echo '</th>'."\n";
1292
-
1293
- echo '</tr>'."\n";
1294
- echo '<tr>'."\n";
1295
-
1296
- echo '<td>'."\n";
1297
- echo '<select name="ws_plugin__s2member_login_redirection_always_http" id="ws-plugin--s2member-login-redirection-always-http">'."\n";
1298
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_always_http"]) ? ' selected="selected"' : '').'>No, do NOT modify (use WordPress default behavior; i.e. detect URL scheme automatically)</option>'."\n";
1299
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_always_http"]) ? ' selected="selected"' : '').'>Yes, always redirect non-administrative users to non-SSL version (i.e. always use http://)</option>'."\n";
1300
- echo '</select><br />'."\n";
1301
- echo 'Recommended setting: <code>Yes</code>. This is compatible w/ <a href="http://codex.wordpress.org/Administration_Over_SSL" target="_blank" rel="external"><code>FORCE_SSL_LOGIN</code></a> and/or <a href="http://codex.wordpress.org/Administration_Over_SSL" target="_blank" rel="external"><code>FORCE_SSL_ADMIN</code></a>.'."\n";
1302
- echo '</td>'."\n";
1303
-
1304
- echo '</tr>'."\n";
1305
- echo '</tbody>'."\n";
1306
- echo '</table>'."\n";
1307
- echo '</div>'."\n";
1308
-
1309
- echo '</div>'."\n";
1310
-
1311
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_login_welcome_page", get_defined_vars());
1312
- }
1313
-
1314
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_membership_options_page", TRUE, get_defined_vars()))
1315
- {
1316
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_membership_options_page", get_defined_vars());
1317
-
1318
- echo '<div class="ws-menu-page-group" title="Membership Options Page">'."\n";
1319
-
1320
- echo '<div class="ws-menu-page-section ws-plugin--s2member-membership-options-page-section">'."\n";
1321
- echo '<h3>Membership Options Page (required, please customize this)</h3>'."\n";
1322
- echo '<p>Please create and/or choose an existing Page that showcases your Membership subscription options. This special Page is where you will insert the Payment Button(s) generated for you by s2Member. This Page serves as your lead-in signup page <em>(i.e. you\'ll give visitors one or more registration options here, and they\'ll be redirected to your Payment Gateway, to pay for the option they choose)</em>.</p>'."\n";
1323
- echo '<p>Your Membership Options Page should detail all of the features that come with Membership to your site, and provide a Payment Button for each Level of access you plan to offer. This is also the Page that anyone could be redirected to <em>(by s2Member)</em>, should they attempt to access an area of your site, which may require access to something they\'re NOT currenty allowed to view.'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' For more on this advanced topic, please check your Dashboard here: <code>s2Member -› API Scripting -› Membership Options Page / Variables</code>.' : '').'</p>'."\n";
1324
- echo '<p><em><strong>*Tip*</strong> If you allow Open Registration (i.e. Free Subscribers), you might want to place a link on your Membership Options Page, which points directly to your free Registration Form, instead of routing a Customer through your Payment Gateway first. It\'s a matter of preference though. For further details, please check the section above: <code>s2Member -› General Options -› Open Registration</code>.</em></p>'."\n";
1325
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> Even with BuddyPress, s2Member still needs a Membership Options Page. This is where your Payment Button(s) will go, giving people the ability to pay you. And again, this is also the Page that anyone could be redirected to <em>(by s2Member)</em>, should they attempt to access an area of your site, which may require access to something they are currenty NOT allowed to view.'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' For more on this advanced topic, please check your Dashboard here: <code>s2Member -› API Scripting -› Membership Options Page / Variables</code>.' : '').'</em></p>'."\n" : '';
1326
- echo '<p><em><strong>*Tip*</strong> s2Member will NEVER allow this Page to be protected from public access.</em></p>'."\n";
1327
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_membership_options_page", get_defined_vars());
1328
-
1329
- echo '<table class="form-table">'."\n";
1330
- echo '<tbody>'."\n";
1331
- echo '<tr>'."\n";
1332
-
1333
- echo '<th>'."\n";
1334
- echo '<label for="ws-plugin--s2member-membership-options-page">'."\n";
1335
- echo 'Membership Options Page:'."\n";
1336
- echo '</label>'."\n";
1337
- echo '</th>'."\n";
1338
-
1339
- echo '</tr>'."\n";
1340
- echo '<tr>'."\n";
1341
-
1342
- echo '<td>'."\n";
1343
- echo '<select name="ws_plugin__s2member_membership_options_page" id="ws-plugin--s2member-membership-options-page">'."\n";
1344
- echo '<option value="">&mdash; Select &mdash;</option>'."\n";
1345
- foreach(($ws_plugin__s2member_temp_a = array_merge((array)get_pages())) as $ws_plugin__s2member_temp_o)
1346
- echo '<option value="'.esc_attr($ws_plugin__s2member_temp_o->ID).'"'.(($ws_plugin__s2member_temp_o->ID == $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"]) ? ' selected="selected"' : '').'>'.esc_html($ws_plugin__s2member_temp_o->post_title).'</option>'."\n";
1347
- echo '</select><br />'."\n";
1348
- echo 'Please choose a Page that provides Users a way to signup for Membership. This Page should also contain your Payment Button(s). We recommend the following title: <code>Membership Signup</code>'."\n";
1349
- echo '</td>'."\n";
1350
-
1351
- echo '</tr>'."\n";
1352
- echo '</tbody>'."\n";
1353
- echo '</table>'."\n";
1354
-
1355
- echo '<div class="ws-menu-page-hr"></div>'."\n";
1356
-
1357
- echo '<table class="form-table">'."\n";
1358
- echo '<tbody>'."\n";
1359
- echo '<tr>'."\n";
1360
-
1361
- echo '<th>'."\n";
1362
- echo '<label for="ws-plugin--s2member-membership-options-page-vars-enable">'."\n";
1363
- echo 'Enable MOP Vars (i.e. Membership Options Page Variables)?'."\n";
1364
- echo '</label>'."\n";
1365
- echo '</th>'."\n";
1366
-
1367
- echo '</tr>'."\n";
1368
- echo '<tr>'."\n";
1369
-
1370
- echo '<td>'."\n";
1371
- echo '<select name="ws_plugin__s2member_membership_options_page_vars_enable" id="ws-plugin--s2member-membership-options-page-vars-enable">'."\n";
1372
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page_vars_enable"]) ? ' selected="selected"' : '').'>Yes (enable MOP Vars in all redirections; recommended behavior)</option>'."\n";
1373
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page_vars_enable"]) ? ' selected="selected"' : '').'>No (don\'t include the additional details provided by MOP Vars)</option>'."\n";
1374
- echo '</select><br />'."\n";
1375
- echo 'See: <code>Dashboard ⥱ s2Member ⥱ API / Scripting ⥱ Membership Options Page / Variables</code><br />'."\n";
1376
- echo 'Recommended setting: (<code>Yes, enable MOP Vars</code>)'."\n";
1377
- echo '</td>'."\n";
1378
-
1379
- echo '</tr>'."\n";
1380
- echo '</tbody>'."\n";
1381
- echo '</table>'."\n";
1382
- echo '</div>'."\n";
1383
-
1384
- echo '</div>'."\n";
1385
-
1386
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_membership_options_page", get_defined_vars());
1387
- }
1388
-
1389
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_profile_modifications", TRUE, get_defined_vars()))
1390
- {
1391
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_profile_modifications", get_defined_vars());
1392
-
1393
- echo '<div class="ws-menu-page-group" title="Member Profile Modifications">'."\n";
1394
-
1395
- echo '<div class="ws-menu-page-section ws-plugin--s2member-profile-modifications-section">'."\n";
1396
- echo '<h3>Giving Members The Ability To Modify Their Profile</h3>'."\n";
1397
- echo '<p>s2Member can be configured to redirect Members away from the <a href="'.esc_attr(admin_url("/profile.php")).'" target="_blank" rel="external">default Profile Editing Panel</a> that is built into WordPress. When/if a Member attempts to access the default Profile Editing Panel, they\'ll instead, be redirected to the Login Welcome Page that you\'ve configured through s2Member. <strong>Why would I redirect?</strong> Unless you\'ve made some drastic modifications to your WordPress installation, the default Profile Editing Panel that ships with WordPress, is NOT really suited for public access, even by a Member.</p>'."\n";
1398
- echo '<p>So instead of using this default Profile Editing Panel; s2Member creates an added layer of functionality, on top of WordPress. It does this by providing you <em>(as the site owner)</em>, with a special Shortcode: <code>[s2Member-Profile /]</code> that you can place into your Login Welcome Page, or any Post/Page for that matter <em>(even into a Text Widget)</em>. This Shortcode produces an Inline Profile Editing Form that supports all aspects of s2Member, including Password changes; and any Custom Registration/Profile Fields that you\'ve configured with s2Member.</p>'."\n";
1399
- echo '<p>Alternatively, s2Member also gives you the ability to send your Members to a <a href="'.esc_attr(home_url("/?s2member_profile=1")).'" target="_blank" rel="external">special Stand-Alone version</a>. This Stand-Alone version has been designed <em>(with a bare-bones format)</em>, intentionally. This makes it possible for you to <a href="#" onclick="if(!window.open(\''.home_url("/?s2member_profile=1").'\', \'_popup\', \'width=600,height=400,left=100,screenX=100,top=100,screenY=100,location=0,menubar=0,toolbar=0,status=0,scrollbars=1,resizable=1\')) alert(\'Please disable popup blockers and try again!\'); return false;" rel="external">open it up in a popup window</a>, or embed it into your Login Welcome Page using an IFRAME. Code samples are provided below.</p>'."\n";
1400
- echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress already provides Users/Members with a Profile Editing Panel, powered by your theme. If you\'ve configured Custom Registration/Profile Fields with s2Member, you can also enable s2Member\'s Profile Field integration with BuddyPress (recommended). For further details, see: <code>s2Member -› General Options -› Registration/Profile Fields</code>.</em></p>'."\n" : '';
1401
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_profile_modifications", get_defined_vars());
1402
-
1403
- echo '<table class="form-table">'."\n";
1404
- echo '<tbody>'."\n";
1405
- echo '<tr>'."\n";
1406
-
1407
- echo '<th>'."\n";
1408
- echo '<label for="ws-plugin--s2member-force-admin-lockouts">'."\n";
1409
- echo 'Redirect Members away from the Default Profile Panel?'."\n";
1410
- echo '</label>'."\n";
1411
- echo '</th>'."\n";
1412
-
1413
- echo '</tr>'."\n";
1414
- echo '<tr>'."\n";
1415
-
1416
- echo '<td>'."\n";
1417
- echo '<select name="ws_plugin__s2member_force_admin_lockouts" id="ws-plugin--s2member-force-admin-lockouts">'."\n";
1418
- echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["force_admin_lockouts"]) ? ' selected="selected"' : '').'>No (I want to use the WordPress default methodologies)</option>'."\n";
1419
- echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["force_admin_lockouts"]) ? ' selected="selected"' : '').'>Yes (redirect to Login Welcome Page; locking all /wp-admin/ areas)</option>'."\n";
1420
- echo '</select><br />'."\n";
1421
- echo 'Recommended setting (<code>Yes</code>). <em><strong>*Note*</strong> When this is set to (<code>Yes</code>), s2Member will take an initiative to further safeguard ALL <code>/wp-admin/</code> areas of your installation; not just the Default Profile Panel.</em>'."\n";
1422
- echo '</td>'."\n";
1423
-
1424
- echo '</tr>'."\n";
1425
- echo '</tbody>'."\n";
1426
- echo '</table>'."\n";
1427
 
1428
- echo '<div class="ws-menu-page-hr"></div>'."\n";
 
 
1429
 
1430
- echo '<p><strong>Shortcode (copy/paste)</strong>, for an Inline Profile Modification Form:<br />'."\n";
1431
- echo '<p><input type="text" autocomplete="off" value="'.format_to_edit('[s2Member-Profile /]').'" onclick="this.select ();" /></p>'."\n";
1432
 
1433
- echo '<p style="margin-top:20px;"><strong>Stand-Alone (copy/paste)</strong>, for popup window:</p>'."\n";
1434
- echo '<p><input type="text" autocomplete="off" value="'.format_to_edit(preg_replace("/\<\?php echo S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL; \?\>/", c_ws_plugin__s2member_utils_strings::esc_refs(home_url("/?s2member_profile=1")), file_get_contents(dirname(__FILE__)."/code-samples/current-user-profile-modification-page-url-2-ops.x-php"))).'" onclick="this.select ();" /></p>'."\n";
1435
- echo '</div>'."\n";
1436
 
1437
- echo '</div>'."\n";
1438
 
1439
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_profile_modifications", get_defined_vars());
1440
- }
1441
 
1442
- if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_url_shortening", TRUE, get_defined_vars()))
1443
- {
1444
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_url_shortening", get_defined_vars());
 
 
1445
 
1446
- echo '<div class="ws-menu-page-group" title="URL Shortening Service Preference">'."\n";
1447
 
1448
- echo '<div class="ws-menu-page-section ws-plugin--s2member-url-shortening-section">'."\n";
1449
- echo '<h3>URL Shortening Service API (Preference)</h3>'."\n";
1450
- echo '<p>In a few special cases, long URLs generated by s2Member, containing encrypted authentication details, will be shortened; using one of the URL Shortening APIs <em>(listed below)</em>. A shortened URL prevents issues with VERY long links becoming corrupted by a Customer\'s email application. For instance, the Signup Confirmation Email that s2Member sends out to a new paying Customer, may contain a link which is shortened to prevent corruption by email applications. By default, s2Member uses the tinyURL API, which has proven itself to be the most reliable. However, in cases where an API service call fails, s2Member will automatically use one or more of its other APIs as a backup. The option below, allows you to configure which URL Shortening API s2Member should try first <em>(i.e. the one you prefer)</em>.</p>'."\n";
1451
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_url_shortening", get_defined_vars());
 
 
1452
 
1453
- echo '<table class="form-table">'."\n";
1454
- echo '<tbody>'."\n";
1455
- echo '<tr>'."\n";
1456
 
1457
- echo '<th>'."\n";
1458
- echo '<label for="ws-plugin--s2member-default-url-shortener">'."\n";
1459
- echo 'URL Shortening Service API (Preference):'."\n";
1460
- echo '</label>'."\n";
1461
- echo '</th>'."\n";
 
 
1462
 
1463
- echo '</tr>'."\n";
1464
- echo '<tr>'."\n";
 
1465
 
1466
- echo '<td>'."\n";
1467
- echo '<select name="ws_plugin__s2member_default_url_shortener" id="ws-plugin--s2member-default-url-shortener">'."\n";
1468
- echo '<option value="tiny_url"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["default_url_shortener"] === "tiny_url") ? ' selected="selected"' : '').'>tinyurl.com (free tinyURL API service)</option>'."\n";
1469
- echo '<option value="goo_gl"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["default_url_shortener"] === "goo_gl") ? ' selected="selected"' : '').'>goo.gl (free Google URL Shortening API service)</option>'."\n";
1470
- echo '</select>'."\n";
1471
- echo '</td>'."\n";
1472
 
1473
- echo '</tr>'."\n";
1474
- echo '<tr>'."\n";
1475
 
1476
- echo '<th>'."\n";
1477
- echo '<label for="ws-plugin--s2member-default-custom-str-url-shortener">'."\n";
1478
- echo 'Custom URL Shortening Service API (Optional/Advanced):'."\n";
1479
- echo '</label>'."\n";
1480
- echo '</th>'."\n";
1481
 
1482
- echo '</tr>'."\n";
1483
- echo '<tr>'."\n";
 
 
 
 
 
1484
 
1485
- echo '<td>'."\n";
1486
- echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_default_custom_str_url_shortener" id="ws-plugin--s2member-default-custom-str-url-shortener" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["default_custom_str_url_shortener"]).'" /><br />'."\n";
1487
- echo 'Your own custom URL <code>(i.e. GET request)</code>, with <code>%%s2_long_url%%</code> Replacement Code. [ <a href="#" onclick="alert(\'s2Member makes it possible for advanced site owners to use a custom URL shortening service they prefer, over the ones currently pre-integrated with s2Member. In order for this to work, your URL shortening service MUST support basic GET requests through its API (sometimes referred to as a REST or NVP API). In addition, your URL shortening service MUST be capable of returning a simple URL in the response that s2Member receives, as a result of s2Member processing the GET request you formulate. See example below.\\n\\nBitly example GET request with format=txt:\nhttp://api.bitly.com/v3/shorten?login=demo&apiKey=2d71bf07&format=txt&longUrl=%%s2_long_url%%\\n(s2Member expects a shortened URL in the response from Bitly)\\n\\n* If you configure s2Member to use your own custom URL shortening service, s2Member will try your configuration first, and if anything fails, it will fall back on its own pre-integrated backups. When configuring your URL for the GET request, s2Member makes two Replacement Codes available:\\n\\n%%s2_long_url%% = The full URL that needs to be shortened (raw URL-encoded).\\n%%s2_long_url_md5%% = An MD5 hash of the full URL (might be useful in some APIs).\\n\\n* If you have any trouble getting your URL shortening service integrated with s2Member in this way, you might take a look at this WordPress Filter ( `ws_plugin__s2member_url_shorten` ), which s2Member makes available for advanced circumstances. Search s2Member\\\'s source code for `ws_plugin__s2member_url_shorten`.\'); return false;" tabindex="-1">click for details</a> ]<br />'."\n";
1488
- echo '</td>'."\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1489
 
1490
- echo '</tr>'."\n";
1491
- echo '</tbody>'."\n";
1492
- echo '</table>'."\n";
1493
- echo '</div>'."\n";
1494
 
1495
- echo '</div>'."\n";
 
 
 
1496
 
1497
- do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_url_shortening", get_defined_vars());
1498
- }
 
 
1499
 
1500
- do_action("ws_plugin__s2member_during_gen_ops_page_after_left_sections", get_defined_vars());
1501
 
1502
- echo '<div class="ws-menu-page-hr"></div>'."\n";
 
 
1503
 
1504
- echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
1505
 
1506
- echo '</form>'."\n";
1507
 
1508
- echo '</td>'."\n";
1509
 
1510
- echo '<td class="ws-menu-page-table-r">'."\n";
1511
- c_ws_plugin__s2member_menu_pages_rs::display();
1512
- echo '</td>'."\n";
1513
 
1514
- echo '</tr>'."\n";
1515
- echo '</tbody>'."\n";
1516
- echo '</table>'."\n";
1517
 
1518
- echo '</div>'."\n";
1519
- }
1520
  }
1521
  }
 
1522
 
1523
- new c_ws_plugin__s2member_menu_page_gen_ops ();
1524
- ?>
18
  exit("Do not access this file directly.");
19
 
20
  if(!class_exists("c_ws_plugin__s2member_menu_page_gen_ops"))
21
+ {
22
+ /**
23
+ * Menu page for the s2Member plugin (General Options page).
24
+ *
25
+ * @package s2Member\Menu_Pages
26
+ * @since 110531
27
+ */
28
+ class c_ws_plugin__s2member_menu_page_gen_ops
29
  {
30
+ public function __construct()
 
 
 
 
 
 
31
  {
32
+ echo '<div class="wrap ws-menu-page">'."\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ echo '<div class="ws-menu-page-toolbox">'."\n";
35
+ c_ws_plugin__s2member_menu_pages_tb::display();
36
+ echo '</div>'."\n";
37
 
38
+ echo '<h2>General Options</h2>'."\n";
 
 
 
 
 
 
39
 
40
+ echo '<table class="ws-menu-page-table">'."\n";
41
+ echo '<tbody class="ws-menu-page-table-tbody">'."\n";
42
+ echo '<tr class="ws-menu-page-table-tr">'."\n";
43
+ echo '<td class="ws-menu-page-table-l">'."\n";
44
 
45
+ echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" autocomplete="off">'."\n";
46
+ echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
47
+ echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
48
 
49
+ do_action("ws_plugin__s2member_during_gen_ops_page_before_left_sections", get_defined_vars());
 
50
 
51
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_uninstall", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars()))
52
+ {
53
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_uninstall", get_defined_vars());
54
 
55
+ echo '<div class="ws-menu-page-group" title="Plugin Deletion Safeguards"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["run_uninstall_routines"]) ? ' default-state="open"' : '').'>'."\n";
56
 
57
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-uninstall-section">'."\n";
58
+ echo '<h3>Plugin Deletion Safeguards (highly recommended)</h3>'."\n";
59
+ echo '<p>By default, s2Member will retain all of it\'s Roles, Capabilities, and your Configuration Options when/if you delete s2Member from the Plugins Menu in WordPress. However, if you would like for s2Member to erase itself completely, please choose: <code>No (upon deletion, erase all data/options)</code>.</p>';
60
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_uninstall", get_defined_vars());
 
 
61
 
62
+ echo '<table class="form-table">'."\n";
63
+ echo '<tbody>'."\n";
64
+ echo '<tr>'."\n";
65
 
66
+ echo '<th>'."\n";
67
+ echo '<label for="ws-plugin--s2member-run-uninstall-routines">'."\n";
68
+ echo 'Safeguard s2Member Data/Options?'."\n";
69
+ echo '</label>'."\n";
70
+ echo '</th>'."\n";
71
+
72
+ echo '</tr>'."\n";
73
+ echo '<tr>'."\n";
74
 
75
+ echo '<td>'."\n";
76
+ echo '<select name="ws_plugin__s2member_run_uninstall_routines" id="ws-plugin--s2member-run-uninstall-routines">'."\n";
77
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["run_uninstall_routines"]) ? ' selected="selected"' : '').'>Yes (safeguard all data/options)</option>'."\n";
78
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["run_uninstall_routines"]) ? ' selected="selected"' : '').'>No (upon deletion, erase all data/options)</option>'."\n";
79
+ echo '</select><br />'."\n";
80
+ echo 'Recommended setting: (<code>Yes, safeguard all data/options</code>)'."\n";
81
+ echo '</td>'."\n";
82
 
83
+ echo '</tr>'."\n";
84
+ echo '</tbody>'."\n";
85
+ echo '</table>'."\n";
86
+ echo '</div>'."\n";
87
+
88
+ echo '</div>'."\n";
89
+
90
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_uninstall", get_defined_vars());
91
+ }
92
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_security", TRUE, get_defined_vars()))
93
+ {
94
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_security", get_defined_vars());
95
+
96
+ echo '<div class="ws-menu-page-group" title="Security Encryption Key">'."\n";
97
+
98
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-security-section">'."\n";
99
+ echo '<img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/large-icon.png" title="s2Member (a Membership management system for WordPress)" alt="" style="float:right; margin:0 0 0 25px; border:0;" />'."\n";
100
+ echo '<h3>Security Encryption Key (optional, for tighter security)</h3>'."\n";
101
+ echo '<p>Just like WordPress, s2Member is open-source software. Which is wonderful. However, this also makes it possible for anyone to grab a copy of the software, and try to learn their way around its security measures. In order to keep your installation of s2Member unique/secure, you should configure a Security Encryption Key. s2Member will use your Security Encryption Key to protect itself against hackers. It does this by encrypting all sensitive information with your Key. A Security Encryption Key is unique to your installation.</p>'."\n";
102
+ echo '<p>Once you configure this, you <em>do not</em> want to change it—ever! In fact, it is a <em>very</em> good idea to keep this backed up in a safe place, just in case you need to move your site, or re-install s2Member in the future. Some of the sensitive data that s2Member stores will be encrypted with this Key. If you change it, that data can no longer be read, even by s2Member itself. In other words, don\'t use s2Member for six months, then decide to change your Key. That would break your installation.</p>'."\n";
103
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_security", get_defined_vars());
104
+
105
+ echo '<table class="form-table">'."\n";
106
+ echo '<tbody>'."\n";
107
+ echo '<tr>'."\n";
108
+
109
+ echo '<th>'."\n";
110
+ echo '<label for="ws-plugin--s2member-sec-encryption-key">'."\n";
111
+ echo 'Security Encryption Key (at least 60 chars)'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]) ? ' <a href="#" onclick="ws_plugin__s2member_enableSecurityKey(); return false;" title="(not recommended)">edit key</a>' : ' <a href="#" onclick="ws_plugin__s2member_generateSecurityKey(); return false;" title="Insert an auto-generated Key. (recommended)">auto-generate</a>')."\n";
112
+ echo '</label>'."\n";
113
+ echo '</th>'."\n";
114
+
115
+ echo '</tr>'."\n";
116
+ echo '<tr>'."\n";
117
+
118
+ echo '<td>'."\n";
119
+ echo '<input type="text" maxlength="256" autocomplete="off" name="ws_plugin__s2member_sec_encryption_key" id="ws-plugin--s2member-sec-encryption-key" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]).'"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]) ? ' disabled="disabled"' : '').' />'."\n";
120
+ echo (!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]) ? '<br />This may contain letters, numbers, spaces; even punctuation. Up to 256 characters.<br /><em>Ex: <code>'.esc_html(strtoupper(c_ws_plugin__s2member_utils_strings::random_str_gen(64))).'</code></em>'."\n" : '';
121
+ echo (count($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key_history"]) > 1) ? '<br /><a href="#" onclick="ws_plugin__s2member_securityKeyHistory(); return false;">Click here</a> for a history of your last 10 Encryption Keys.<div id="ws-plugin--s2member-sec-encryption-key-history" style="display:none;"><code>'.implode('</code><br /><code>', $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key_history"]).'</code></div>'."\n" : '';
122
+ echo '</td>'."\n";
123
+
124
+ echo '</tr>'."\n";
125
+ echo '</tbody>'."\n";
126
+ echo '</table>'."\n";
127
+
128
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
129
+
130
+ echo '<h3>Additional Details Regarding this Key:</h3>'."\n";
131
+ echo '<p>Your Security Encryption Key is used throughout s2Member\'s source code for many different things. However, most (not all, but most) uses of this Key are related to transactional processing within a particular session; so changing the Key won\'t really impact these scenarios in any significant way. Your Security Encryption Key is simply there to enhance security of data that is being transmitted in these cases.</p>'."\n";
132
+ echo '<p>That said, there are a few scenarios where use of your Security Encryption Key is more long-term. These include: Specific Post/Page Access Links, Registration Access Links, and it can also have a long-term impact on IPN communication because some data analyzed by s2Member includes a checksum that depends on your Key. If the Key changes, it could cause future IPN data (i.e., data from your payment gateway) to fail validation.</p>'."\n";
133
+
134
+ echo '</div>'."\n";
135
+
136
+ echo '</div>'."\n";
137
+
138
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_security", get_defined_vars());
139
+ }
140
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_localhost_info", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars()))
141
+ {
142
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_localhost_info", get_defined_vars());
143
+
144
+ echo '<div class="ws-menu-page-group" title="Localhost WAMP/MAMP Developers">'."\n";
145
+
146
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-localhost-info-section">'."\n";
147
+ echo '<h3>Localhost WAMP/MAMP Installs (are you a developer?)</h3>'."\n";
148
+ echo '<p>If you\'re developing your site in a <code>localhost</code> environment, running something like WAMP/MAMP, or <a href="http://www.easyphp.org/" target="_blank" rel="external">EasyPHP</a>, please add this line to your <code>/wp-config.php</code> file: <code><span style="color:#0000BB;">define</span><span style="color:#007700;">(</span><span style="color:#DD0000;">"LOCALHOST"</span>, <span style="color:#0000BB;">true</span><span style="color:#007700;">);</span></code>.</p>'."\n";
149
+ echo '<p>This lets s2Member know definitively that your site is in a <code>localhost</code> environment. s2Member will adjust itself accordingly, maximizing functionality during your developement. s2Member can usually auto-detect this, but in cases where your <code>localhost</code> installation runs on something other than <code>127.0.0.1/localhost</code>, you need to tell s2Member definitively, by adding that line to your <code>/wp-config.php</code> file. For instance, s2Member needs to know when your server IP is the same as all User IPs.</p>'."\n";
150
+ echo '<p><em>Once your site goes live, please remove the line. If you\'re already on a live server connected to the web, please ignore this section.</em></p>'."\n";
151
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_localhost_info", get_defined_vars());
152
+ echo '</div>'."\n";
153
+
154
+ echo '</div>'."\n";
155
+
156
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_localhost_info", get_defined_vars());
157
+ }
158
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_lazy_load", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars()))
159
+ {
160
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_lazy_load", get_defined_vars());
161
+
162
+ echo '<div class="ws-menu-page-group" title="CSS/JS Lazy Loading">'."\n";
163
+
164
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-lazy-load-section">'."\n";
165
+ echo '<h3>CSS/JS Lazy Loading (Client-Side Libraries)</h3>'."\n";
166
+ echo '<p>By default, s2Member will load it\'s CSS/JS libraries on every page of your site. However, you may wish to enable lazy-loading here (i.e., only load when absolutely necessary).</p>'."\n";
167
+ echo '<p><em><strong>Tip:</strong> Do you need s2Member\'s CSS/JS on every page? If not, you can turn lazy-loading on. If you need s2Member\'s CSS/JS on a given Post/Page, you can insert an HTML comment into the Post/Page content like this: <code>&lt;!--s2member--&gt;</code>. If a Post/Page contains the word <code>s2member</code> or an <code>[s2*</code> Shortcode, this will automatically trigger s2Member\'s lazy-load routine (no matter what you configure here). Thus, it\'s an easy way to force s2Member to load it\'s CSS/JS on specific Posts/Pages where you deem this necessary. There is also a WordPress filter available: <code>add_filter("ws_plugin__s2member_lazy_load_css_js", "__return_true");</code> for developers. This could be incorporated into more dynamic scenarios.</em></p>'."\n";
168
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_lazy_load", get_defined_vars());
169
+
170
+ echo '<table class="form-table">'."\n";
171
+ echo '<tbody>'."\n";
172
+ echo '<tr>'."\n";
173
+
174
+ echo '<th>'."\n";
175
+ echo '<label for="ws-plugin--s2member-lazy-load-css-js">'."\n";
176
+ echo 'Lazy-Load s2Member\'s CSS/JS Libraries?'."\n";
177
+ echo '</label>'."\n";
178
+ echo '</th>'."\n";
179
+
180
+ echo '</tr>'."\n";
181
+ echo '<tr>'."\n";
182
+
183
+ echo '<td>'."\n";
184
+ echo '<select name="ws_plugin__s2member_lazy_load_css_js" id="ws-plugin--s2member-lazy-load-css-js">'."\n";
185
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["lazy_load_css_js"]) ? ' selected="selected"' : '').'>No (always load the CSS/JS libraries; i.e., on every page of the site)</option>'."\n";
186
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["lazy_load_css_js"]) ? ' selected="selected"' : '').'>Yes (lazy-load CSS/JS libraries; i.e., load only when absolutely necessary)</option>'."\n";
187
+ echo '</select>'."\n";
188
+ echo '</td>'."\n";
189
+
190
+ echo '</tr>'."\n";
191
+ echo '</tbody>'."\n";
192
+ echo '</table>'."\n";
193
+ echo '</div>'."\n";
194
+
195
+ echo '</div>'."\n";
196
+
197
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_lazy_load", get_defined_vars());
198
+ }
199
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_s_badge_wp_footer_code", TRUE, get_defined_vars()))
200
+ {
201
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_s_badge_wp_footer_code", get_defined_vars());
202
+
203
+ echo '<div class="ws-menu-page-group" title="s2Member Security Badge">'."\n";
204
+
205
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-s-badge-wp-footer-code-section">'."\n";
206
+ echo '<h3>Security Badge &amp; Footer Configuration (optional)</h3>'."\n";
207
+ echo '<div class="ws-menu-page-right">'.c_ws_plugin__s2member_utilities::s_badge_gen("1", TRUE, TRUE).'</div>'."\n";
208
+ echo '<p>An s2Member Security Badge (optional), can be used to express your site\'s concern for security; demonstrating to all Users/Members that your site (and the s2Member software) takes security seriously. However, in order to qualify your site, you <em>must</em> generate a Security Encryption Key (previous section), and then click "Save All Changes". Only then, will s2Member officially verify your installation</em>.</p>'."\n";
209
+ echo '<p>Once you\'ve <a href="http://www.s2member.com/kb/security-badges/" target="_blank" rel="external">properly configured all security aspects of s2Member</a>, your s2Member Security Badge will be verified. To see the "verified" version of your Security Badge, you might need to refresh your browser after saving all changes (i.e., after you create a Security Encryption Key). Also, s2Member will <em>not</em> "verify" your site if you turn off Unique IP Restrictions, Brute Force Login Protection, or if your <code>/wp-config.php</code> file lacks <a href="http://codex.wordpress.org/Editing_wp-config.php#Security_Keys" target="_blank" rel="external">Security Keys</a> <em>(each at least 60 chars in length)</em>. In addition, it\'s not possible for s2Member to verify your Security Badge if your site is in a <code>localhost</code> environment; i.e., not connected to the web.</p>'."\n";
210
+ echo '<p><strong>How does s2Member know when my site is secure?</strong><br />If enabled below, an API call for "Security Badge Status" will allow web service connections to determine your status. Clicking <a href="'.esc_attr(home_url("/?s2member_s_badge_status=1")).'" target="_blank" rel="external">this link</a> will report <code>1</code> <em>(secure)</em>, <code>0</code> <em>(at risk)</em>, or <code>-</code> <em>(API disabled)</em>. Once all security considerations are satisfied, s2Member will report <code>1</code> <em>(secure)</em> for your installation. <strong>Note:</strong> this simple API will not, and should not, report any other information. It will only report the current status of your Security Badge, as determined by your installation of s2Member. When/if you install the s2Member Security Badge, s2Member will make a connection to your site <strong>once per day</strong>, to test your status.</p>'."\n";
211
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_s_badge_wp_footer_code", get_defined_vars());
212
+
213
+ echo '<table class="form-table">'."\n";
214
+ echo '<tbody>'."\n";
215
+ echo '<tr>'."\n";
216
+
217
+ echo '<th>'."\n";
218
+ echo '<label for="ws-plugin--s2member-s-badge-status-enabled">'."\n";
219
+ echo 'Enable Security Badge Status API?'."\n";
220
+ echo '</label>'."\n";
221
+ echo '</th>'."\n";
222
+
223
+ echo '</tr>'."\n";
224
+ echo '<tr>'."\n";
225
+
226
+ echo '<td>'."\n";
227
+ echo '<select name="ws_plugin__s2member_s_badge_status_enabled" id="ws-plugin--s2member-s-badge-status-enabled">'."\n";
228
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["s_badge_status_enabled"]) ? ' selected="selected"' : '').'>No (default, Badge Status API is disabled)</option>'."\n";
229
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["s_badge_status_enabled"]) ? ' selected="selected"' : '').'>Yes (enable Badge Status API for verification)</option>'."\n";
230
+ echo '</select><br />'."\n";
231
+ echo 'This must be enabled if you want s2Member to verify your Security Badge.'."\n";
232
+ echo '</td>'."\n";
233
+
234
+ echo '</tr>'."\n";
235
+ echo '<tr>'."\n";
236
+
237
+ echo '<th>'."\n";
238
+ echo '<label for="ws-plugin--s2member-wp-footer-code">'."\n";
239
+ echo 'Customize WordPress Footer:<br />'."\n";
240
+ echo '<small>[ <a href="#" onclick="this.$code = jQuery(\'textarea#ws-plugin--s2member-wp-footer-code\'); this.$code.val(jQuery.trim(unescape(\''.rawurlencode('[s2Member-Security-Badge v="1" /]').'\')+\'\n\'+this.$code.val())); return false;">Click HERE to insert your Security Badge</a> ],<br />or use Shortcode <code>[s2Member-Security-Badge v="1" /]</code> in a Post/Page/Widget.<br />The <code>v="1"</code> attribute is a Security Badge style/variation. Try variations <code>1|2|3</code>.</small>'."\n";
241
+ echo '</label>'."\n";
242
+ echo '</th>'."\n";
243
+
244
+ echo '</tr>'."\n";
245
+ echo '<tr>'."\n";
246
+
247
+ echo '<td>'."\n";
248
+ echo '<textarea name="ws_plugin__s2member_wp_footer_code" id="ws-plugin--s2member-wp-footer-code" rows="8" wrap="off" spellcheck="false">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["wp_footer_code"]).'</textarea><br />'."\n";
249
+ echo 'Any valid XHTML / JavaScript'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) ? '' : ' (or even PHP)').' code will work just fine here.'."\n";
250
+ echo '</td>'."\n";
251
+
252
+ echo '</tr>'."\n";
253
+ echo '</tbody>'."\n";
254
+ echo '</table>'."\n";
255
+ echo '</div>'."\n";
256
+
257
+ echo '</div>'."\n";
258
+
259
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_s_badge_wp_footer_code", get_defined_vars());
260
+ }
261
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_email_config", TRUE, get_defined_vars()))
262
+ {
263
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_email_config", get_defined_vars());
264
+
265
+ echo '<div class="ws-menu-page-group" title="Email Configuration">'."\n";
266
+
267
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-email-section">'."\n";
268
+ echo '<h3 style="margin:0;">Email From: '.esc_html('"Name" <address>').'</h3>'."\n";
269
+ echo '<p style="margin:0;">This is the name/address that will appear in outgoing email notifications sent by the s2Member plugin.</p>'."\n";
270
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_email_from_name_config", get_defined_vars());
271
+
272
+ echo '<table class="form-table">'."\n";
273
+ echo '<tbody>'."\n";
274
+ echo '<tr>'."\n";
275
+
276
+ echo '<th>'."\n";
277
+ echo '<label for="ws-plugin--s2member-reg-email-from-name">'."\n";
278
+ echo 'Email From Name:'."\n";
279
+ echo '</label>'."\n";
280
+ echo '</th>'."\n";
281
+
282
+ echo '</tr>'."\n";
283
+ echo '<tr>'."\n";
284
+
285
+ echo '<td>'."\n";
286
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_reg_email_from_name" id="ws-plugin--s2member-reg-email-from-name" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_from_name"]).'" /><br />'."\n";
287
+ echo 'We recommend that you use the name of your site here.'."\n";
288
+ echo '</td>'."\n";
289
+
290
+ echo '</tr>'."\n";
291
+ echo '<tr>'."\n";
292
+
293
+ echo '<th>'."\n";
294
+ echo '<label for="ws-plugin--s2member-reg-email-from-email">'."\n";
295
+ echo 'Email From Address:'."\n";
296
+ echo '</label>'."\n";
297
+ echo '</th>'."\n";
298
+
299
+ echo '</tr>'."\n";
300
+ echo '<tr>'."\n";
301
+
302
+ echo '<td>'."\n";
303
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_reg_email_from_email" id="ws-plugin--s2member-reg-email-from-email" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_from_email"]).'" /><br />'."\n";
304
+ echo 'Example: support@your-domain.com. <em>Please read <a href="#" onclick="alert(\'Running WordPress with an SMTP mail plugin?\\n\\nPlease be advised. If you run an SMTP mail plugin with WordPress, be sure to configure s2Member with a valid `From:` address (i.e., one matching your SMTP configuration perhaps). Most free SMTP servers, such as Gmail and Yahoo, require that your `From:` header match the email address associated with your account. Please check with your SMTP service provider before attempting to configure plugins like s2Member to use a different `From:` address when sending email messages.\'); return false;">this important note</a></em>.'."\n";
305
+ echo '</td>'."\n";
306
+
307
+ echo '</tr>'."\n";
308
+ echo '<tr>'."\n";
309
+
310
+ echo '<th>'."\n";
311
+ echo '<label for="ws-plugin--s2member-reg-email-support-link">'."\n";
312
+ echo 'Email Support/Contact Link:'."\n";
313
+ echo '</label>'."\n";
314
+ echo '</th>'."\n";
315
+
316
+ echo '</tr>'."\n";
317
+ echo '<tr>'."\n";
318
+
319
+ echo '<td>'."\n";
320
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_reg_email_support_link" id="ws-plugin--s2member-reg-email-support-link" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["reg_email_support_link"]).'" /><br />'."\n";
321
+ echo 'Ex: <code>mailto:support@your-domain.com</code> (<em>mailto link</em>)<br />'."\n";
322
+ echo 'Or: <code>'.esc_html(home_url("/contact-us/")).'</code>'."\n";
323
+ echo '</td>'."\n";
324
+
325
+ echo '</tr>'."\n";
326
+ echo '</tbody>'."\n";
327
+ echo '</table>'."\n";
328
+
329
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
330
+
331
+ echo '<h3 style="margin:0;">New User Email Configuration</h3>'."\n";
332
+ echo '<input type="hidden" id="ws-plugin--s2member-pluggables-wp-new-user-notification" value="'.esc_attr((empty($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["pluggables"]["wp_new_user_notification"])) ? '0' : '1').'" />'."\n";
333
+ echo (empty($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["pluggables"]["wp_new_user_notification"])) ? '<p class="ws-menu-page-error" style="margin:0;"><em><strong>Conflict warning:</strong> You have another theme or plugin installed that is preventing s2Member from controlling this aspect of your installation. When the pluggable function <code><a href="http://codex.wordpress.org/Function_Reference/wp_new_user_notification" target="_blank" rel="external">wp_new_user_notification()</a></code> is handled by another plugin, it\'s not possible for s2Member to allow customization of New User Emails. This is NOT a major issue. In fact, in some cases, it might be desirable. That being said, if you DO want to use s2Member\'s customization of New User Emails, you will need to deactivate one plugin at a time until this conflict warning goes away.</em></p>'."\n" : '';
334
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_emails", get_defined_vars());
335
+
336
+ echo '<table class="form-table">'."\n";
337
+ echo '<tbody>'."\n";
338
+ echo '<tr>'."\n";
339
+
340
+ echo '<td>'."\n";
341
+ echo '<select name="ws_plugin__s2member_new_user_emails_enabled" id="ws-plugin--s2member-new-user-emails-enabled">'."\n";
342
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_emails_enabled"]) ? ' selected="selected"' : '').'>No (default, use WordPress defaults)</option>'."\n";
343
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_emails_enabled"]) ? ' selected="selected"' : '').'>Yes (customize New User Emails with s2Member)</option>'."\n";
344
+ echo '</select>'."\n";
345
+ echo '</td>'."\n";
346
+
347
+ echo '</tr>'."\n";
348
+ echo '</tbody>'."\n";
349
+ echo '</table>'."\n";
350
+
351
+ echo '<div id="ws-plugin--s2member-new-user-emails">'."\n";
352
+
353
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
354
+
355
+ echo '<h3 style="margin:0;">New User Email Message (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-new-user-email-details\').toggle(); return false;" class="ws-dotted-link">click to customize</a>)</h3>'."\n";
356
+ echo '<p style="margin:0;">This email is sent to all new Users/Members. It should always contain their Username/Password. In addition to this email, s2Member will also send new paying Customers a Signup Confirmation Email, which you can customize from your Dashboard, under: <strong>s2Member ⥱ PayPal Options</strong>. You may wish to customize these emails further, by providing details that are specifically geared to your site.</p>'."\n";
357
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_email", get_defined_vars());
358
+
359
+ echo '<div id="ws-plugin--s2member-new-user-email-details" style="display:none;">'."\n";
360
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> please note that BuddyPress does NOT send this email to Users that register through the BuddyPress registration system. This is because BuddyPress sends each User an activation link; eliminating the need for this email all together. However, you CAN still customize s2Member\'s separate email to paying Members. See: <strong>s2Member ⥱ PayPal Options ⥱ Signup Confirmation Email</strong>.</em></p>'."\n" : '';
361
+ echo '<table class="form-table">'."\n";
362
+ echo '<tbody>'."\n";
363
+ echo '<tr>'."\n";
364
+
365
+ echo '<th>'."\n";
366
+ echo '<label for="ws-plugin--s2member-new-user-email-subject">'."\n";
367
+ echo 'New User Email Subject:'."\n";
368
+ echo '</label>'."\n";
369
+ echo '</th>'."\n";
370
+
371
+ echo '</tr>'."\n";
372
+ echo '<tr>'."\n";
373
+
374
+ echo '<td>'."\n";
375
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_new_user_email_subject" id="ws-plugin--s2member-new-user-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_email_subject"]).'" /><br />'."\n";
376
+ echo 'Subject Line used in the email sent to new Users/Members.'."\n";
377
+ echo '</td>'."\n";
378
+
379
+ echo '</tr>'."\n";
380
+ echo '<tr>'."\n";
381
+
382
+ echo '<th>'."\n";
383
+ echo '<label for="ws-plugin--s2member-new-user-email-message">'."\n";
384
+ echo 'New User Email Message:'."\n";
385
+ echo '</label>'."\n";
386
+ echo '</th>'."\n";
387
+
388
+ echo '</tr>'."\n";
389
+ echo '<tr>'."\n";
390
+
391
+ echo '<td>'."\n";
392
+ echo '<textarea name="ws_plugin__s2member_new_user_email_message" id="ws-plugin--s2member-new-user-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_email_message"]).'</textarea><br />'."\n";
393
+ echo 'Message Body used in the email sent to new Users/Members.<br /><br />'."\n";
394
+ echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
395
+ echo '<ul>'."\n";
396
+ echo '<li><code>%%role%%</code> = The Role ID <code>(subscriber, s2member_level[0-9]+, administrator, editor, author, contributor)</code>.</li>'."\n";
397
+ echo '<li><code>%%label%%</code> = The Role ID Label <code>(Subscriber, s2Member Level 1, s2Member Level 2; or your own custom Labels—if configured)</code>.</li>'."\n";
398
+ echo '<li><code>%%level%%</code> = The Level number <code>(0, 1, 2, 3, 4)</code>. (<em>deprecated, no longer recommended; use <code>%%role%%</code></em>)</li>'."\n";
399
+ echo '<li><code>%%ccaps%%</code> = Custom Capabilities. Ex: <code>music,videos,free_gift</code> (<em>in comma-delimited format</em>).</li>'."\n";
400
+ echo '<li><code>%%user_first_name%%</code> = The First Name of the Member who registered their Username.</li>'."\n";
401
+ echo '<li><code>%%user_last_name%%</code> = The Last Name of the Member who registered their Username.</li>'."\n";
402
+ echo '<li><code>%%user_full_name%%</code> = The Full Name (First &amp; Last) of the Member who registered their Username.</li>'."\n";
403
+ echo '<li><code>%%user_email%%</code> = The Email Address of the Member who registered their Username.</li>'."\n";
404
+ echo '<li><code>%%user_login%%</code> = The Username the Member selected during registration.</li>'."\n";
405
+ echo '<li><code>%%user_pass%%</code> = The Password selected or generated during registration.</li>'."\n";
406
+ echo '<li><code>%%user_ip%%</code> = The User\'s IP Address, detected via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
407
+ echo '<li><code>%%user_id%%</code> = A unique WordPress User ID generated during registration.</li>'."\n";
408
+ echo '<li><code>%%wp_login_url%%</code> = The full URL where Users can get logged into your site.</li>'."\n";
409
+ echo '</ul>'."\n";
410
+
411
+ echo '<strong>Custom Registration/Profile Fields are also supported in this email:</strong>'."\n";
412
+ echo '<ul>'."\n";
413
+ echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
414
+ echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
415
+ echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
416
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member ⥱ General Options ⥱ Registration/Profile Fields</em>.</li>'."\n";
417
+ echo '</ul>'."\n";
418
+
419
+ echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
420
+ echo '<ul>'."\n";
421
+ echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
422
+ echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
423
+ echo '</ul>'."\n";
424
+ echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
425
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
426
+ echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
427
+
428
+ echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
429
+ '<div class="ws-menu-page-hr"></div>'."\n".
430
+ '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags—optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$user</code> variable is the most important one. It\'s an instance of the <a href="http://codex.wordpress.org/Class_Reference/WP_User" target="_blank" rel="external"><code>WP_User</code></a> class (e.g., <code>$user->ID</code>, <code>$user->has_cap()</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
431
+ : '';
432
+ echo '</td>'."\n";
433
+
434
+ echo '</tr>'."\n";
435
+ echo '</tbody>'."\n";
436
+ echo '</table>'."\n";
437
+ echo '</div>'."\n";
438
+
439
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
440
+
441
+ echo '<h3 style="margin:0;">Administrative: New User Notification (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-new-user-admin-email-details\').toggle(); return false;" class="ws-dotted-link">click to customize</a>)</h3>'."\n";
442
+ echo '<p style="margin:0;">This email notification is sent to you, each time a new User/Member registers.</p>'."\n";
443
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_admin_email", get_defined_vars());
444
+
445
+ echo '<div id="ws-plugin--s2member-new-user-admin-email-details" style="display:none;">'."\n";
446
+ echo '<table class="form-table">'."\n";
447
+ echo '<tbody>'."\n";
448
+ echo '<tr>'."\n";
449
+
450
+ echo '<th>'."\n";
451
+ echo '<label for="ws-plugin--s2member-new-user-admin-email-recipients">'."\n";
452
+ echo 'New User Notification Recipients:'."\n";
453
+ echo '</label>'."\n";
454
+ echo '</th>'."\n";
455
+
456
+ echo '</tr>'."\n";
457
+ echo '<tr>'."\n";
458
+
459
+ echo '<td>'."\n";
460
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_new_user_admin_email_recipients" id="ws-plugin--s2member-new-user-admin-email-recipients" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_admin_email_recipients"]).'" /><br />'."\n";
461
+ echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:<br />'."\n";
462
+ echo '<code>"Name" &lt;user@example.com&gt;; admin@example.com; "Webmaster" &lt;webmaster@example.com&gt;</code>'."\n";
463
+ echo '</td>'."\n";
464
+
465
+ echo '</tr>'."\n";
466
+ echo '<tr>'."\n";
467
+
468
+ echo '<th>'."\n";
469
+ echo '<label for="ws-plugin--s2member-new-user-admin-email-subject">'."\n";
470
+ echo 'New User Notification Subject:'."\n";
471
+ echo '</label>'."\n";
472
+ echo '</th>'."\n";
473
+
474
+ echo '</tr>'."\n";
475
+ echo '<tr>'."\n";
476
+
477
+ echo '<td>'."\n";
478
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_new_user_admin_email_subject" id="ws-plugin--s2member-new-user-admin-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_admin_email_subject"]).'" /><br />'."\n";
479
+ echo 'Subject Line used in the email notification sent to Administrator.'."\n";
480
+ echo '</td>'."\n";
481
+
482
+ echo '</tr>'."\n";
483
+ echo '<tr>'."\n";
484
+
485
+ echo '<th>'."\n";
486
+ echo '<label for="ws-plugin--s2member-new-user-admin-email-message">'."\n";
487
+ echo 'New User Notification Message:'."\n";
488
+ echo '</label>'."\n";
489
+ echo '</th>'."\n";
490
+
491
+ echo '</tr>'."\n";
492
+ echo '<tr>'."\n";
493
+
494
+ echo '<td>'."\n";
495
+ echo '<textarea name="ws_plugin__s2member_new_user_admin_email_message" id="ws-plugin--s2member-new-user-admin-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["new_user_admin_email_message"]).'</textarea><br />'."\n";
496
+ echo 'Message Body used in the email notification sent to Administrator.<br /><br />'."\n";
497
+ echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
498
+ echo '<ul>'."\n";
499
+ echo '<li><code>%%role%%</code> = The Role ID <code>(subscriber, s2member_level[0-9]+, administrator, editor, author, contributor)</code>.</li>'."\n";
500
+ echo '<li><code>%%label%%</code> = The Role ID Label <code>(Subscriber, s2Member Level 1, s2Member Level 2; or your own custom Labels—if configured)</code>.</li>'."\n";
501
+ echo '<li><code>%%level%%</code> = The Level number <code>(0, 1, 2, 3, 4)</code>. (<em>deprecated, no longer recommended; use <code>%%role%%</code></em>)</li>'."\n";
502
+ echo '<li><code>%%ccaps%%</code> = Custom Capabilities. Ex: <code>music,videos,free_gift</code> (<em>in comma-delimited format</em>).</li>'."\n";
503
+ echo '<li><code>%%user_first_name%%</code> = The First Name of the Member who registered their Username.</li>'."\n";
504
+ echo '<li><code>%%user_last_name%%</code> = The Last Name of the Member who registered their Username.</li>'."\n";
505
+ echo '<li><code>%%user_full_name%%</code> = The Full Name (First &amp; Last) of the Member who registered their Username.</li>'."\n";
506
+ echo '<li><code>%%user_email%%</code> = The Email Address of the Member who registered their Username.</li>'."\n";
507
+ echo '<li><code>%%user_login%%</code> = The Username the Member selected during registration.</li>'."\n";
508
+ echo '<li><code>%%user_pass%%</code> = The Password selected or generated during registration.</li>'."\n";
509
+ echo '<li><code>%%user_ip%%</code> = The User\'s IP Address, detected via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
510
+ echo '<li><code>%%user_id%%</code> = A unique WordPress User ID generated during registration.</li>'."\n";
511
+ echo '<li><code>%%wp_login_url%%</code> = The full URL where Users can get logged into your site.</li>'."\n";
512
+ echo '</ul>'."\n";
513
+
514
+ echo '<strong>Custom Registration/Profile Fields are also supported in this email:</strong>'."\n";
515
+ echo '<ul>'."\n";
516
+ echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
517
+ echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
518
+ echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
519
+ echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member ⥱ General Options ⥱ Registration/Profile Fields</em>.</li>'."\n";
520
+ echo '</ul>'."\n";
521
+
522
+ echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
523
+ echo '<ul>'."\n";
524
+ echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
525
+ echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
526
+ echo '</ul>'."\n";
527
+ echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
528
+ echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
529
+ echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
530
+
531
+ echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
532
+ '<div class="ws-menu-page-hr"></div>'."\n".
533
+ '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags—optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$user</code> variable is the most important one. It\'s an instance of the <a href="http://codex.wordpress.org/Class_Reference/WP_User" target="_blank" rel="external"><code>WP_User</code></a> class (e.g., <code>$user->ID</code>, <code>$user->has_cap()</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
534
+ : '';
535
+ echo '</td>'."\n";
536
+
537
+ echo '</tr>'."\n";
538
+ echo '</tbody>'."\n";
539
+ echo '</table>'."\n";
540
+ echo '</div>'."\n";
541
+ echo '</div>'."\n";
542
+
543
+ echo '</div>'."\n";
544
+
545
+ echo '</div>'."\n";
546
+
547
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_email_config", get_defined_vars());
548
+ }
549
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_open_registration", TRUE, get_defined_vars()))
550
+ {
551
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_open_registration", get_defined_vars());
552
+
553
+ if(is_multisite() && is_main_site()) // A Multisite Network, and we're on the Main Site?
554
+ {
555
+ echo '<div class="ws-menu-page-group" title="Open Registration">'."\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
 
557
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-open-registration-section">'."\n";
558
+ echo '<h3>Open Registration / Free Subscribers (optional)</h3>'."\n";
559
+ echo '<p>On the Main Site of a Multisite Network, the settings for Open Registration are consolidated into the <strong>s2Member Multisite (Config)</strong> panel.</p>'."\n";
560
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_open_registration", get_defined_vars());
561
+ echo '</div>'."\n";
 
 
 
562
 
563
+ echo '</div>'."\n";
564
+ }
565
+ else // Else we display this section normally. No special considerations are required in this case.
566
+ {
567
+ echo '<div class="ws-menu-page-group" title="Open Registration">'."\n";
568
 
569
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-open-registration-section">'."\n";
570
+ echo '<h3>Open Registration / Free Subscribers (optional)</h3>'."\n";
571
+ echo '<p>s2Member supports Free Subscribers (at Level #0), along with four Primary Levels [1-4] of paid Membership. If you want your visitors to be capable of registering absolutely free, you will want to "allow" Open Registration. Whenever a visitor registers without paying, they\'ll automatically become a Free Subscriber at Level #0.</p>'."\n";
572
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_open_registration", get_defined_vars());
573
 
574
+ echo '<table class="form-table">'."\n";
575
+ echo '<tbody>'."\n";
576
+ echo '<tr>'."\n";
577
 
578
+ echo '<th>'."\n";
579
+ echo '<label for="ws-plugin--s2member-allow-subscribers-in">'."\n";
580
+ echo 'Allow Open Registration? (Free Subscribers)'."\n";
581
+ echo '</label>'."\n";
582
+ echo '</th>'."\n";
583
 
584
+ echo '</tr>'."\n";
585
+ echo '<tr>'."\n";
586
+
587
+ echo '<td>'."\n";
588
+ echo '<select name="ws_plugin__s2member_allow_subscribers_in" id="ws-plugin--s2member-allow-subscribers-in">'."\n";
589
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["allow_subscribers_in"]) ? ' selected="selected"' : '').'>No (do NOT allow Open Registration)</option>'."\n";
590
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["allow_subscribers_in"]) ? ' selected="selected"' : '').'>Yes (allow Open Registration; Free Subscribers at Level #0)</option>'."\n";
591
+ echo '</select><br />'."\n";
592
+ echo 'If you set this to <code>Yes</code>, you\'re unlocking <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re already logged-in. Please log out before testing BuddyPress registration.' : '').'\');">/wp-login.php?action=register</a>. When a visitor registers without paying, they\'ll automatically become a Free Subscriber at Level #0. The s2Member software reserves Level #0; to be used only for Free Subscribers. All other Membership Levels [1-4] require payment.'."\n";
593
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><br /><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re already logged-in. Please log out before testing BuddyPress registration.\');">here</a>.<br />s2Member integrates with BuddyPress, and the above setting will control Open Regisration for BuddyPress too.</em>'."\n" : '';
594
+ echo '</td>'."\n";
595
 
596
+ echo '</tr>'."\n";
597
+ echo '</tbody>'."\n";
598
+ echo '</table>'."\n";
599
+ echo '</div>'."\n";
600
 
601
+ echo '</div>'."\n";
602
+ }
603
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_open_registration", get_defined_vars());
604
+ }
605
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_membership_levels", TRUE, get_defined_vars()))
606
+ {
607
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_membership_levels", get_defined_vars());
608
 
609
+ echo '<div class="ws-menu-page-group" title="Membership Levels/Labels">'."\n";
 
 
 
 
 
610
 
611
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-membership-levels-section">'."\n";
612
+ echo '<h3>Membership Levels (required, please customize these)</h3>'."\n";
613
+ echo '<p>The default Membership Levels are labeled generically; feel free to modify them as needed. s2Member supports Free Subscribers <em>(at Level #0)</em>, along with several Primary Roles for paid Membership <em>(i.e., Levels 1-4)</em>, created by the s2Member plugin.'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' s2Member also supports unlimited Custom Capability Packages <em>(see <strong>s2Member ⥱ API Scripting ⥱ Custom Capabilities</strong>)</em>' : '').'. That being said, you don\'t have to use all of the Membership Levels if you don\'t want to. To use only 1 or 2 of these Levels, just design your Membership Options Page, so it only includes Payment Buttons for the Levels being used.</p>'."\n";
614
+ echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '<p><em><strong>TIP:</strong> <strong>Unlimited Membership Levels</strong> are only possible with <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Add-on / Prices")).'" target="_blank" rel="external">s2Member Pro</a>. However, Custom Capabilities are possible in all versions of s2Member, including the free version. Custom Capabilities are a great way to extend s2Member in creative ways. If you\'re an advanced site owner, a theme designer, or a web developer integrating s2Member for a client, please check your Dashboard, under: <strong>s2Member ⥱ API Scripting ⥱ Custom Capabilities</strong>. We also recommend <a href="http://www.s2member.com/videos/A2C07377CF60025E/" target="_blank" rel="external">this video tutorial</a>.</em></p>'."\n" : '';
615
+ echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '<p><strong>See also:</strong> These KB articles: <a href="http://www.s2member.com/kb/roles-caps/" target="_blank" rel="external">s2Member Roles/Capabilities</a> and <a href="http://www.s2member.com/kb/simple-shortcode-conditionals/" target="_blank" rel="external">Simple Shortcode Conditionals</a>.</p>'."\n" : '';
616
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_membership_levels", get_defined_vars());
617
 
618
+ echo '<table class="form-table">'."\n";
619
+ echo '<tbody>'."\n";
 
 
 
 
 
620
 
621
+ for($n = 0; $n <= $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; $n++)
622
+ {
623
+ echo '<tr>'."\n";
624
 
625
+ echo '<th>'."\n";
626
+ echo '<label for="ws-plugin--s2member-level'.$n.'-label">'."\n";
627
+ echo ($n === 0) ? 'Level #'.$n.' <em>(Free Subscribers)</em>:'."\n" : 'Level #'.$n.' Members:'."\n";
628
+ echo '</label>'."\n";
629
+ echo '</th>'."\n";
630
 
631
+ echo '</tr>'."\n";
632
+ echo '<tr>'."\n";
633
 
634
+ echo '<td>'."\n";
635
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_level'.$n.'_label" id="ws-plugin--s2member-level'.$n.'-label" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level".$n."_label"]).'" /><br />'."\n";
636
+ echo 'This is the Label for Level #'.$n.(($n === 0) ? ' (Free Subscribers)' : ' Members').'.<br />'."\n";
637
+ echo '</td>'."\n";
638
 
639
+ echo '</tr>'."\n";
640
+ }
641
+ echo '</tbody>'."\n";
642
+ echo '</table>'."\n";
643
 
644
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
 
645
 
646
+ echo '<table class="form-table" style="margin-top:0;">'."\n";
647
+ echo '<tbody>'."\n";
648
+ echo '<tr>'."\n";
 
 
649
 
650
+ echo '<th style="padding-top:0;">'."\n";
651
+ echo '<label for="ws-plugin--s2member-apply-label-translations">'."\n";
652
+ echo 'Force WordPress to use your Labels?'."\n";
653
+ echo '</label>'."\n";
654
+ echo '</th>'."\n";
655
 
656
+ echo '</tr>'."\n";
657
+ echo '<tr>'."\n";
 
 
658
 
659
+ echo '<td>'."\n";
660
+ echo '<input type="radio" name="ws_plugin__s2member_apply_label_translations" id="ws-plugin--s2member-apply-label-translations-0" value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["apply_label_translations"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-apply-label-translations-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_apply_label_translations" id="ws-plugin--s2member-apply-label-translations-1" value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["apply_label_translations"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-apply-label-translations-1">Yes, force WordPress to use my Labels.</label><br />'."\n";
661
+ echo 'This affects your administrative Dashboard only <em>(i.e., your list of Users)</em>.<br />s2Member can force WordPress to use your Labels instead of referencing Roles by `s2Member Level #`. If this is your first installation of s2Member, we suggest leaving this set to <code>no</code> until you\'ve had a chance to get acclimated with s2Member\'s functionality. In fact, many site owners choose to leave this off, because they find it less confusing when Roles are referred to by their s2Member Level #.'."\n";
662
+ echo '</td>'."\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
 
664
+ echo '</tr>'."\n";
665
+ echo '</tbody>'."\n";
666
+ echo '</table>'."\n";
667
 
668
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
 
669
 
670
+ echo '<input type="button" value="Reset Roles/Capabilities" class="ws-menu-page-right ws-plugin--s2member-reset-roles-button" style="min-width:175px;" />'."\n";
671
+ echo '<p>The button to the right, is a nifty tool, which allows you to reset s2Member\'s internal Roles and Capabilities that integrate with WordPress. If you, or a developer working with you, has made attempts to alter the default <em>internal</em> Role/Capability sets that come with s2Member, and you need to reset them back to the way s2Member expects them to be, please use this tool. <em>Attn Developers: it is also possible lock-in your modified Roles/Capabilities with an s2Member Filter. Please see <a href="http://www.s2member.com/kb/roles-caps/#modifying-roles-caps" target="_blank" rel="external">this KB article for details</a>.</em></p>'."\n";
 
672
 
673
+ echo '</div>'."\n";
674
 
675
+ echo '</div>'."\n";
 
676
 
677
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_membership_levels", get_defined_vars());
678
+ }
679
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_login_registration", TRUE, get_defined_vars()))
680
+ {
681
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_login_registration", get_defined_vars());
682
 
683
+ echo '<div class="ws-menu-page-group" title="Login/Registration Design">'."\n";
684
 
685
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-login-registration-section">'."\n";
686
+ echo '<h3>Login/Registration Page Customization (optional)</h3>'."\n";
687
+ echo '<p>These settings customize your Standard Login/Registration Pages:<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_register_url()).'</a>)</p>'."\n";
688
+ echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '<p><em>The Main Site of a Multisite Blog Farm uses this Form instead, powered by your theme.<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Multisite Registration Form.\\n* s2Member makes this form available to logged-in Super Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'</a>)</em></p>'."\n" : '';
689
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form, powered by your theme.<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.\');">'.esc_html(c_ws_plugin__s2member_utils_urls::bp_register_url()).'</a>)</em></p>'."\n" : '';
690
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_login_registration", get_defined_vars());
691
 
692
+ echo '<table class="form-table">'."\n";
693
+ echo '<tbody>'."\n";
694
+ echo '<tr>'."\n";
695
 
696
+ echo '<td>'."\n";
697
+ echo '<h3 style="margin:0;">Enable This Functionality?</h3>'."\n";
698
+ echo '<select name="ws_plugin__s2member_login_reg_design_enabled" id="ws-plugin--s2member-login-reg-design-enabled">'."\n";
699
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_design_enabled"]) ? ' selected="selected"' : '').'>No (default, use WordPress defaults)</option>'."\n";
700
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_design_enabled"]) ? ' selected="selected"' : '').'>Yes (customize Login/Registration with s2Member)</option>'."\n";
701
+ echo '</select>'."\n";
702
+ echo '</td>'."\n";
703
 
704
+ echo '</tr>'."\n";
705
+ echo '</tbody>'."\n";
706
+ echo '</table>'."\n";
707
 
708
+ echo '<div id="ws-plugin--s2member-login-reg-design"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_design_enabled"]) ? ' style="display:none;"' : '').'>'."\n";
 
 
 
 
 
709
 
710
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
 
711
 
712
+ echo '<table class="form-table">'."\n";
713
+ echo '<tbody>'."\n";
714
+ echo '<tr>'."\n";
 
 
715
 
716
+ echo '<td>'."\n";
717
+ echo '<h3 style="margin:0;">Overall Font/Size Configuration</h3>'."\n";
718
+ echo '<p style="margin:0;">These settings are all focused on your Login/Registration Fonts.</p>'."\n";
719
+ echo '</td>'."\n";
720
+
721
+ echo '</tr>'."\n";
722
+ echo '<tr>'."\n";
723
 
724
+ echo '<th>'."\n";
725
+ echo '<label for="ws-plugin--s2member-login-reg-font-size">'."\n";
726
+ echo 'Overall Font Size:'."\n";
727
+ echo '</label>'."\n";
728
+ echo '</th>'."\n";
729
+
730
+ echo '</tr>'."\n";
731
+ echo '<tr>'."\n";
732
+
733
+ echo '<td>'."\n";
734
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_font_size" id="ws-plugin--s2member-login-reg-font-size" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_font_size"]).'" /><br />'."\n";
735
+ echo 'Set this to a numeric value, calculated in pixels.'."\n";
736
+ echo '</td>'."\n";
737
+
738
+ echo '</tr>'."\n";
739
+ echo '<tr>'."\n";
740
+
741
+ echo '<th>'."\n";
742
+ echo '<label for="ws-plugin--s2member-login-reg-font-family">'."\n";
743
+ echo 'Overall Font Family:'."\n";
744
+ echo '</label>'."\n";
745
+ echo '</th>'."\n";
746
+
747
+ echo '</tr>'."\n";
748
+ echo '<tr>'."\n";
749
+
750
+ echo '<td>'."\n";
751
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_font_family" id="ws-plugin--s2member-login-reg-font-family" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_font_family"]).'" /><br />'."\n";
752
+ echo 'Set this to a web-safe font family.'."\n";
753
+ echo '</td>'."\n";
754
+
755
+ echo '</tr>'."\n";
756
+ echo '<tr>'."\n";
757
+
758
+ echo '<th>'."\n";
759
+ echo '<label for="ws-plugin--s2member-login-reg-font-field-size">'."\n";
760
+ echo 'Form Field Font Size:'."\n";
761
+ echo '</label>'."\n";
762
+ echo '</th>'."\n";
763
+
764
+ echo '</tr>'."\n";
765
+ echo '<tr>'."\n";
766
+
767
+ echo '<td>'."\n";
768
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_font_field_size" id="ws-plugin--s2member-login-reg-font-field-size" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_font_field_size"]).'" /><br />'."\n";
769
+ echo 'Set this to a numeric value, calculated in pixels.'."\n";
770
+ echo '</td>'."\n";
771
+
772
+ echo '</tr>'."\n";
773
+ echo '</tbody>'."\n";
774
+ echo '</table>'."\n";
775
+
776
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
777
+
778
+ echo '<table class="form-table" style="margin-top:0;">'."\n";
779
+ echo '<tbody>'."\n";
780
+ echo '<tr>'."\n";
781
+
782
+ echo '<td>'."\n";
783
+ echo '<h3 style="margin:0;">Background Configuration</h3>'."\n";
784
+ echo '<p style="margin:0;">These settings are all focused on your Login/Registration Background.</p>'."\n";
785
+ echo '</td>'."\n";
786
+
787
+ echo '</tr>'."\n";
788
+ echo '<tr>'."\n";
789
+
790
+ echo '<th>'."\n";
791
+ echo '<label for="ws-plugin--s2member-login-reg-background-color">'."\n";
792
+ echo 'Background Color:'."\n";
793
+ echo '</label>'."\n";
794
+ echo '</th>'."\n";
795
+
796
+ echo '</tr>'."\n";
797
+ echo '<tr>'."\n";
798
+
799
+ echo '<td>'."\n";
800
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_color" id="ws-plugin--s2member-login-reg-background-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_color"]).'" /><br />'."\n";
801
+ echo 'Set this to a 6-digit hex color code.'."\n";
802
+ echo '</td>'."\n";
803
+
804
+ echo '</tr>'."\n";
805
+ echo '<tr>'."\n";
806
+
807
+ echo '<th>'."\n";
808
+ echo '<label for="ws-plugin--s2member-login-reg-background-image">'."\n";
809
+ echo 'Background Image:'."\n";
810
+ echo '</label>'."\n";
811
+ echo '</th>'."\n";
812
+
813
+ echo '</tr>'."\n";
814
+ echo '<tr>'."\n";
815
+
816
+ echo '<td>'."\n";
817
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_image" id="ws-plugin--s2member-login-reg-background-image" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image"]).'" /><br />'."\n";
818
+ echo '<input type="button" id="ws-plugin--s2member-login-reg-background-image-media-btn" value="Open Media Library" class="ws-menu-page-media-btn" rel="ws-plugin--s2member-login-reg-background-image" />'."\n";
819
+ echo 'Set this to the URL of your Background Image. (this is optional)<br />';
820
+ echo 'If supplied, your Background Image will be tiled.'."\n";
821
+ echo '</td>'."\n";
822
+
823
+ echo '</tr>'."\n";
824
+ echo '<tr>'."\n";
825
+
826
+ echo '<th>'."\n";
827
+ echo '<label for="ws-plugin--s2member-login-reg-background-image-repeat">'."\n";
828
+ echo 'Background Image Tile:'."\n";
829
+ echo '</label>'."\n";
830
+ echo '</th>'."\n";
831
+
832
+ echo '</tr>'."\n";
833
+ echo '<tr>'."\n";
834
+
835
+ echo '<td>'."\n";
836
+ echo '<select name="ws_plugin__s2member_login_reg_background_image_repeat" id="ws-plugin--s2member-login-reg-background-image-repeat">'."\n";
837
+ echo '<option value="repeat"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "repeat") ? ' selected="selected"' : '').'>Seamless Tile ( background-repeat: repeat; )</option>'."\n";
838
+ echo '<option value="repeat-x"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "repeat-x") ? ' selected="selected"' : '').'>Tile Horizontally ( background-repeat: repeat-x; )</option>'."\n";
839
+ echo '<option value="repeat-y"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "repeat-y") ? ' selected="selected"' : '').'>Tile Vertically ( background-repeat: repeat-y; )</option>'."\n";
840
+ echo '<option value="no-repeat"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_image_repeat"] === "no-repeat") ? ' selected="selected"' : '').'>No Tiles ( background-repeat: no-repeat; )</option>'."\n";
841
+ echo '</select><br />'."\n";
842
+ echo 'This controls the way your Background Image is styled with CSS. [ <a href="http://www.w3schools.com/css/pr_background-repeat.asp" target="_blank" rel="external">learn more</a> ]'."\n";
843
+ echo '</td>'."\n";
844
+
845
+ echo '</tr>'."\n";
846
+ echo '<tr>'."\n";
847
+
848
+ echo '<th>'."\n";
849
+ echo '<label for="ws-plugin--s2member-login-reg-background-text-color">'."\n";
850
+ echo 'Color of Text on top of your Background:'."\n";
851
+ echo '</label>'."\n";
852
+ echo '</th>'."\n";
853
+
854
+ echo '</tr>'."\n";
855
+ echo '<tr>'."\n";
856
+
857
+ echo '<td>'."\n";
858
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_text_color" id="ws-plugin--s2member-login-reg-background-text-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_text_color"]).'" /><br />'."\n";
859
+ echo 'Set this to a 6-digit hex color code.'."\n";
860
+ echo '</td>'."\n";
861
+
862
+ echo '</tr>'."\n";
863
+ echo '<tr>'."\n";
864
+
865
+ echo '<th>'."\n";
866
+ echo '<label for="ws-plugin--s2member-login-reg-background-text-shadow-color">'."\n";
867
+ echo 'Shadow Color for Text on top of your Background:'."\n";
868
+ echo '</label>'."\n";
869
+ echo '</th>'."\n";
870
+
871
+ echo '</tr>'."\n";
872
+ echo '<tr>'."\n";
873
+
874
+ echo '<td>'."\n";
875
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_text_shadow_color" id="ws-plugin--s2member-login-reg-background-text-shadow-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_text_shadow_color"]).'" /><br />'."\n";
876
+ echo 'Set this to a 6-digit hex color code.'."\n";
877
+ echo '</td>'."\n";
878
+
879
+ echo '</tr>'."\n";
880
+ echo '<tr>'."\n";
881
+
882
+ echo '<th>'."\n";
883
+ echo '<label for="ws-plugin--s2member-login-reg-background-box-shadow-color">'."\n";
884
+ echo 'Shadow Color for Boxes on top of your Background:'."\n";
885
+ echo '</label>'."\n";
886
+ echo '</th>'."\n";
887
+
888
+ echo '</tr>'."\n";
889
+ echo '<tr>'."\n";
890
+
891
+ echo '<td>'."\n";
892
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_background_box_shadow_color" id="ws-plugin--s2member-login-reg-background-box-shadow-color" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_background_box_shadow_color"]).'" /><br />'."\n";
893
+ echo 'Set this to a 6-digit hex color code.'."\n";
894
+ echo '</td>'."\n";
895
+
896
+ echo '</tr>'."\n";
897
+ echo '</tbody>'."\n";
898
+ echo '</table>'."\n";
899
+
900
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
901
+
902
+ echo '<table class="form-table" style="margin-top:0;">'."\n";
903
+ echo '<tbody>'."\n";
904
+ echo '<tr>'."\n";
905
+
906
+ echo '<td>'."\n";
907
+ echo '<h3 style="margin:0;">Logo Image Configuration</h3>'."\n";
908
+ echo '<p style="margin:0;">These settings are all focused on your Login/Registration Logo.</p>'."\n";
909
+ echo '</td>'."\n";
910
+
911
+ echo '</tr>'."\n";
912
+ echo '<tr>'."\n";
913
+
914
+ echo '<th>'."\n";
915
+ echo '<label for="ws-plugin--s2member-login-reg-logo-src">'."\n";
916
+ echo 'Logo Image Location:'."\n";
917
+ echo '</label>'."\n";
918
+ echo '</th>'."\n";
919
+
920
+ echo '</tr>'."\n";
921
+ echo '<tr>'."\n";
922
+
923
+ echo '<td>'."\n";
924
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_src" id="ws-plugin--s2member-login-reg-logo-src" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_src"]).'" /><br />'."\n";
925
+ echo '<input type="button" id="ws-plugin--s2member-login-reg-logo-src-media-btn" value="Open Media Library" class="ws-menu-page-media-btn" rel="ws-plugin--s2member-login-reg-logo-src" />'."\n";
926
+ echo 'Set this to the URL of your Logo Image.<br />'."\n";
927
+ echo 'Suggested size is around 500 x 100.'."\n";
928
+ echo '</td>'."\n";
929
+
930
+ echo '</tr>'."\n";
931
+ echo '<tr>'."\n";
932
+
933
+ echo '<th>'."\n";
934
+ echo '<label for="ws-plugin--s2member-login-reg-logo-src-width">'."\n";
935
+ echo 'Logo Image Width:'."\n";
936
+ echo '</label>'."\n";
937
+ echo '</th>'."\n";
938
+
939
+ echo '</tr>'."\n";
940
+ echo '<tr>'."\n";
941
+
942
+ echo '<td>'."\n";
943
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_src_width" id="ws-plugin--s2member-login-reg-logo-src-width" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_src_width"]).'" /><br />'."\n";
944
+ echo 'The pixel Width of your Logo Image. <em>* This ALSO affects the overall width of your Login/Registration forms. If you want wider form fields, use a wider Logo.</em>'."\n";
945
+ echo '</td>'."\n";
946
+
947
+ echo '</tr>'."\n";
948
+ echo '<tr>'."\n";
949
+
950
+ echo '<th>'."\n";
951
+ echo '<label for="ws-plugin--s2member-login-reg-logo-src-height">'."\n";
952
+ echo 'Logo Image Height:'."\n";
953
+ echo '</label>'."\n";
954
+ echo '</th>'."\n";
955
+
956
+ echo '</tr>'."\n";
957
+ echo '<tr>'."\n";
958
+
959
+ echo '<td>'."\n";
960
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_src_height" id="ws-plugin--s2member-login-reg-logo-src-height" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_src_height"]).'" /><br />'."\n";
961
+ echo 'The pixel Height of your Logo Image.'."\n";
962
+ echo '</td>'."\n";
963
+
964
+ echo '</tr>'."\n";
965
+ echo '<tr>'."\n";
966
+
967
+ echo '<th>'."\n";
968
+ echo '<label for="ws-plugin--s2member-login-reg-logo-url">'."\n";
969
+ echo 'Logo Image Click URL:'."\n";
970
+ echo '</label>'."\n";
971
+ echo '</th>'."\n";
972
+
973
+ echo '</tr>'."\n";
974
+ echo '<tr>'."\n";
975
+
976
+ echo '<td>'."\n";
977
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_url" id="ws-plugin--s2member-login-reg-logo-url" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_url"]).'" /><br />'."\n";
978
+ echo 'Set this to the Click URL for your Logo Image.'."\n";
979
+ echo '</td>'."\n";
980
+
981
+ echo '</tr>'."\n";
982
+ echo '<tr>'."\n";
983
+
984
+ echo '<th>'."\n";
985
+ echo '<label for="ws-plugin--s2member-login-reg-logo-title">'."\n";
986
+ echo 'Logo Image Title Attribute:'."\n";
987
+ echo '</label>'."\n";
988
+ echo '</th>'."\n";
989
+
990
+ echo '</tr>'."\n";
991
+ echo '<tr>'."\n";
992
+
993
+ echo '<td>'."\n";
994
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_reg_logo_title" id="ws-plugin--s2member-login-reg-logo-title" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_logo_title"]).'" /><br />'."\n";
995
+ echo 'Used as the <code>title=""</code> attribute for your Logo Image.'."\n";
996
+ echo '</td>'."\n";
997
+
998
+ echo '</tr>'."\n";
999
+ echo '</tbody>'."\n";
1000
+ echo '</table>'."\n";
1001
+
1002
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
1003
+
1004
+ echo '<table class="form-table" style="margin-top:0;">'."\n";
1005
+ echo '<tbody>'."\n";
1006
+ echo '<tr>'."\n";
1007
+
1008
+ echo '<th>'."\n";
1009
+ echo '<label for="ws-plugin--s2member-login-reg-footer-backtoblog">'."\n";
1010
+ echo 'Display [Back to Home Page] Link At Bottom?'."\n";
1011
+ echo '</label>'."\n";
1012
+ echo '</th>'."\n";
1013
+
1014
+ echo '</tr>'."\n";
1015
+ echo '<tr>'."\n";
1016
+
1017
+ echo '<td>'."\n";
1018
+ echo '<select name="ws_plugin__s2member_login_reg_footer_backtoblog" id="ws-plugin--s2member-login-reg-footer-backtoblog">'."\n";
1019
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_footer_backtoblog"]) ? ' selected="selected"' : '').'>Yes, display link at bottom pointing visitors back to the home page</option>'."\n";
1020
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_footer_backtoblog"]) ? ' selected="selected"' : '').'>No, hide this link (I\'ll create my own custom footer w/ the details I prefer)</option>'."\n";
1021
+ echo '</select>'."\n";
1022
+ echo '</td>'."\n";
1023
+
1024
+ echo '</tr>'."\n";
1025
+ echo '<tr>'."\n";
1026
+
1027
+ echo '<td>'."\n";
1028
+ echo '<h3 style="margin:0;">Footer Design (i.e., Bottom)</h3>'."\n";
1029
+ echo '<p style="margin:0;">This field accepts raw HTML'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' (and/or PHP)' : '').' code.</p>'."\n";
1030
+ echo '</td>'."\n";
1031
+
1032
+ echo '</tr>'."\n";
1033
+ echo '<tr>'."\n";
1034
+
1035
+ echo '<th>'."\n";
1036
+ echo '<label for="ws-plugin--s2member-login-reg-footer-design">'."\n";
1037
+ echo 'Login/Registration Footer Design (optional):'."\n";
1038
+ echo '</label>'."\n";
1039
+ echo '</th>'."\n";
1040
+
1041
+ echo '</tr>'."\n";
1042
+ echo '<tr>'."\n";
1043
+
1044
+ echo '<td>'."\n";
1045
+ echo '<textarea name="ws_plugin__s2member_login_reg_footer_design" id="ws-plugin--s2member-login-reg-footer-design" rows="3" wrap="off" spellcheck="false">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_reg_footer_design"]).'</textarea><br />'."\n";
1046
+ echo 'This optional HTML'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' (and/or PHP)' : '').' code will appear at the very bottom of your Login/Registration Forms.'."\n";
1047
+ echo '</td>'."\n";
1048
+
1049
+ echo '</tr>'."\n";
1050
+ echo '</tbody>'."\n";
1051
+ echo '</table>'."\n";
1052
+ echo '</div>'."\n";
1053
+
1054
+ echo '</div>'."\n";
1055
+
1056
+ echo '</div>'."\n";
1057
+
1058
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_login_registration", get_defined_vars());
1059
+ }
1060
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_custom_reg_fields", TRUE, get_defined_vars()))
1061
+ {
1062
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_custom_reg_fields", get_defined_vars());
1063
+
1064
+ echo '<div class="ws-menu-page-group" title="Registration/Profile Fields &amp; Options">'."\n";
1065
+
1066
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-custom-reg-fields-section">'."\n";
1067
+ echo '<h3>Custom Registration/Profile Fields (optional, for further customization)</h3>'."\n";
1068
+ echo '<p>Some fields are already built-in by default. The defaults are: <code>*Username*, *Email*, *First Name*, *Last Name*</code>.</p>'."\n";
1069
+
1070
+ echo '<p>Custom Fields will appear in your Standard Registration Form, and in User/Member Profiles:<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_register_url()).'</a>)</p>'."\n";
1071
+ echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '<p><em>The Main Site of a Multisite Blog Farm uses this Form. s2Member supports Custom Fields here too.<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Multisite Registration Form.\\n* s2Member makes this form available to logged-in Super Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">'.esc_html(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'</a>)</em></p>'."\n" : '';
1072
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.\');">here</a>.<br />s2Member can integrate your Custom Fields with BuddyPress too, please see options below.</em></p>'."\n" : '';
1073
+ echo '<p><strong>Regarding Registration:</strong> Custom Fields do not appear during repeat registration and/or checkout attempts (i.e., they do not appear for any user that is currently logged into the site). Please make sure that you test registration and/or checkout forms while not logged in (i.e., please test as a first-time customer). Existing users, members, customers may update Custom Fields by editing their Profile.</p>'."\n";
1074
+
1075
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_custom_reg_fields", get_defined_vars());
1076
+
1077
+ echo '<table class="form-table">'."\n";
1078
+ echo '<tbody>'."\n";
1079
+ echo '<tr>'."\n";
1080
+
1081
+ echo '<th>'."\n";
1082
+ echo '<label>'."\n";
1083
+ echo 'Custom Registration/Profile Fields:'."\n";
1084
+ echo '</label>'."\n";
1085
+ echo '</th>'."\n";
1086
+
1087
+ echo '</tr>'."\n";
1088
+ echo '<tr>'."\n";
1089
+
1090
+ echo '<td>'."\n";
1091
+ echo '<input type="hidden" name="ws_plugin__s2member_custom_reg_fields" id="ws-plugin--s2member-custom-reg-fields" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_fields"]).'" />'."\n";
1092
+ echo '<div id="ws-plugin--s2member-custom-reg-field-configuration"></div>'."\n"; // This is filled by JavaScript routines.
1093
+ echo '</td>'."\n";
1094
+
1095
+ echo '</tr>'."\n";
1096
+ echo '<tr>'."\n";
1097
+
1098
+ echo '<th>'."\n";
1099
+ echo '<label for="ws-plugin--s2member-custom-reg-names">'."\n";
1100
+ echo 'Collect First/Last Names during Registration?'."\n";
1101
+ echo '</label>'."\n";
1102
+ echo '</th>'."\n";
1103
+
1104
+ echo '</tr>'."\n";
1105
+ echo '<tr>'."\n";
1106
+
1107
+ echo '<td>'."\n";
1108
+ echo '<select name="ws_plugin__s2member_custom_reg_names" id="ws-plugin--s2member-custom-reg-names">'."\n";
1109
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_password"]) ? ' selected="selected"' : '').'>Yes (always collect First/Last Names during registration)</option>'."\n";
1110
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_names"]) ? ' selected="selected"' : '').'>No (do NOT collect First/Last Names during registration)</option>'."\n";
1111
+ echo '</select><br />'."\n";
1112
+ echo 'Recommended setting (<code>Yes</code>). It\'s usually a good idea to leave this on.'."\n";
1113
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Has no affect on BuddyPress registration form (BuddyPress always collects a full <code>Name</code> field).</em>'."\n" : '';
1114
+ echo (c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? '<br /><em>* s2Member Pro (Checkout) Forms always require a First/Last Name for billing.</em>'."\n" : '';
1115
+ echo '</td>'."\n";
1116
+
1117
+ echo '</tr>'."\n";
1118
+ echo '<tr>'."\n";
1119
+
1120
+ echo '<th>'."\n";
1121
+ echo '<label for="ws-plugin--s2member-custom-reg-display-name">'."\n";
1122
+ echo 'Set "Display Name" during Registration?'."\n";
1123
+ echo '</label>'."\n";
1124
+ echo '</th>'."\n";
1125
+
1126
+ echo '</tr>'."\n";
1127
+ echo '<tr>'."\n";
1128
+
1129
+ echo '<td>'."\n";
1130
+ echo '<select name="ws_plugin__s2member_custom_reg_display_name" id="ws-plugin--s2member-custom-reg-display-name">'."\n";
1131
+ echo '<option value="full"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "full") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s Full Name)</option>'."\n";
1132
+ echo '<option value="first"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "first") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s First Name)</option>'."\n";
1133
+ echo '<option value="last"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "last") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s Last Name)</option>'."\n";
1134
+ echo '<option value="login"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"] === "login") ? ' selected="selected"' : '').'>Yes (set Display Name to User\'s Username)</option>'."\n";
1135
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_display_name"]) ? ' selected="selected"' : '').'>No (leave Display Name at default WordPress value)</option>'."\n";
1136
+ echo '</select>'."\n";
1137
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Has no affect on BuddyPress registration form (BuddyPress always uses its full <code>Name</code> field).</em>'."\n" : '';
1138
+ echo '</td>'."\n";
1139
+
1140
+ echo '</tr>'."\n";
1141
+ echo '<tr>'."\n";
1142
+
1143
+ echo '<th>'."\n";
1144
+ echo '<label for="ws-plugin--s2member-custom-reg-password">'."\n";
1145
+ echo 'Allow Custom Passwords during Registration?'."\n";
1146
+ echo '</label>'."\n";
1147
+ echo '</th>'."\n";
1148
+
1149
+ echo '</tr>'."\n";
1150
+ echo '<tr>'."\n";
1151
+
1152
+ echo '<td>'."\n";
1153
+ echo '<select name="ws_plugin__s2member_custom_reg_password" id="ws-plugin--s2member-custom-reg-password"'.((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site() && !c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? ' disabled="disabled"' : '').'>'."\n";
1154
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_password"]) ? ' selected="selected"' : '').'>No (send auto-generated passwords via email; after registration)</option>'."\n";
1155
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_password"]) ? ' selected="selected"' : '').'>Yes (allow members to create their own password during registration)</option>'."\n";
1156
+ echo '</select><br />'."\n";
1157
+ echo 'Auto-generated Passwords are recommended for best security; i.e., this also serves as a form of email confirmation.'."\n";
1158
+ echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '<br /><em>* For security purposes, Custom Passwords are not possible on the Main Site of a Blog Farm. <a href="#" onclick="alert(\'For security purposes, Custom Passwords are not possible on the Main Site of a Blog Farm. A User must wait for the activation/confirmation email; where a randomly generated Password will be assigned. Please note, this limitation only affects your Main Site, via `/wp-signup.php`. In other words, your Customers (i.e., other Blog Owners) will still have the ability to allow Custom Passwords with s2Member. YOU are affected by this limitation, NOT them.\\n\\n* NOTE: s2Member (Pro) removes this limitation.\\nIf you install the s2Member Pro Add-on, you WILL be able to allow Custom Passwords through s2Member Pro-Forms; even on a Multisite Blog Farm.\'); return false;" tabindex="-1">[?]</a></em>'."\n" : '';
1159
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Does not affect BuddyPress registration form (always <code>yes</code> with BuddyPress registration).</em>'."\n" : '';
1160
+ echo '</td>'."\n";
1161
+
1162
+ echo '</tr>'."\n";
1163
+ echo '<tr>'."\n";
1164
+
1165
+ echo '<th>'."\n";
1166
+ echo '<label for="ws-plugin--s2member-custom-reg-force-personal-emails">'."\n";
1167
+ echo 'Force Personal Emails during Registration?'."\n";
1168
+ echo '</label>'."\n";
1169
+ echo '</th>'."\n";
1170
+
1171
+ echo '</tr>'."\n";
1172
+ echo '<tr>'."\n";
1173
+
1174
+ echo '<td>'."\n";
1175
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_custom_reg_force_personal_emails" id="ws-plugin--s2member-custom-reg-force-personal-emails" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_force_personal_emails"]).'" /><br />'."\n";
1176
+ echo 'To force personal email addresses, provide a comma-delimited list of email users to reject. <a href="#" onclick="alert(\'s2Member will reject [user]@ (based on your configuration here). A JavaScript alert message will be issued, asking the User to, `please use a personal email address`.\'); return false;" tabindex="-1">[?]</a><br />'."\n";
1177
+ echo 'Ex: <code>info,help,admin,webmaster,hostmaster,sales,support,spam</code><br />'."\n";
1178
+ echo 'See: <a href="http://kb.mailchimp.com/article/what-role-addresses-does-mailchimp-specifically-block-from-bulk-importing/" target="_blank" rel="external">this article</a> for a more complete list.'."\n";
1179
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<br /><em>* Affects BuddyPress registration form too.</em>'."\n" : '';
1180
+ echo '</td>'."\n";
1181
+
1182
+ echo '</tr>'."\n";
1183
+ echo '<tr>'."\n";
1184
+
1185
+ echo '<th>'."\n";
1186
+ echo '<label for="ws-plugin--s2member-custom-reg-fields-4bp">'."\n";
1187
+ echo 'Integrate Custom Registration/Profile Fields with BuddyPress?'."\n";
1188
+ echo '</label>'."\n";
1189
+ echo '</th>'."\n";
1190
+
1191
+ echo '</tr>'."\n";
1192
+ echo '<tr>'."\n";
1193
+
1194
+ echo '<td>'."\n";
1195
+ echo '<div class="ws-menu-page-scrollbox" style="height:65px;">'."\n";
1196
+ echo '<input type="hidden" name="ws_plugin__s2member_custom_reg_fields_4bp[]" value="update-signal"'.((!c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? ' disabled="disabled"' : '').' />'."\n";
1197
+ foreach(array("profile-view" => "Yes, integrate with BuddyPress Public Profiles.", "registration" => "Yes, integrate with BuddyPress Registration Form.", "profile" => "Yes, integrate with BuddyPress Profile Editing Panel.") as $ws_plugin__s2member_temp_s_value => $ws_plugin__s2member_temp_s_label)
1198
+ echo '<input type="checkbox" name="ws_plugin__s2member_custom_reg_fields_4bp[]" id="ws-plugin--s2member-custom-reg-fields-4bp-'.esc_attr(preg_replace("/[^a-z0-9_\-]/", "-", $ws_plugin__s2member_temp_s_value)).'" value="'.esc_attr($ws_plugin__s2member_temp_s_value).'"'.((in_array($ws_plugin__s2member_temp_s_value, $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_fields_4bp"])) ? ' checked="checked"' : '').((!c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? ' disabled="disabled"' : '').' /> <label for="ws-plugin--s2member-custom-reg-fields-4bp-'.esc_attr(preg_replace("/[^a-z0-9_\-]/", "-", $ws_plugin__s2member_temp_s_value)).'">'.$ws_plugin__s2member_temp_s_label.'</label><br />'."\n";
1199
+ echo '</div>'."\n";
1200
+ echo (!c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<em>* BuddyPress is not installed; which is perfectly OK. BuddyPress is not a requirement.</em>'."\n" : '<em>* The options above, make it possible to integrate Custom Registration/Profile Fields (i.e., those configured with s2Member) into BuddyPress as well. However, if you configure Profile Fields with BuddyPress, those will NOT be integrated with s2Member. Therefore, if you need Custom Registration/Profile Fields to work with both s2Member and with BuddyPress, please configure them with s2Member.</em>';
1201
+ echo '</td>'."\n";
1202
+
1203
+ echo '</tr>'."\n";
1204
+ echo '</tbody>'."\n";
1205
+ echo '</table>'."\n";
1206
+ echo '</div>'."\n";
1207
+
1208
+ echo '</div>'."\n";
1209
+
1210
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_custom_reg_fields", get_defined_vars());
1211
+ }
1212
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_login_welcome_page", TRUE, get_defined_vars()))
1213
+ {
1214
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_login_welcome_page", get_defined_vars());
1215
+
1216
+ echo '<div class="ws-menu-page-group" title="Login Welcome Page">'."\n";
1217
+
1218
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-login-welcome-page-section">'."\n";
1219
+ echo '<h3>Login Welcome Page (required, please customize this)</h3>'."\n";
1220
+ echo '<p>Please create and/or choose an existing Page to use as the first page Members will see after logging in.</p>'."\n";
1221
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> s2Member integrates with BuddyPress. Your Login Welcome Page affects BuddyPress too.</em></p>'."\n" : '';
1222
+ echo '<p><em><strong>Always Private:</strong> This Page will always require a logged-in User/Member. In fact, this Page will be protected from public access by s2Member automatically. <strong>Note:</strong> for technical reasons, your Login Welcome Page <strong>cannot</strong> be set to your Front Page (i.e., your Home Page); or your Posts Page (i.e., your main Blog page). Please create a separate (dedicated) Page in WordPress, and then designate it as your Login Welcome Page below.</em></p>'."\n";
1223
+ echo '<p><strong>See also:</strong> This KB article: <a href="http://www.s2member.com/kb/customizing-your-lwp/" target="_blank" rel="external">Customizing Your Login Welcome Page</a>.</p>'."\n";
1224
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_login_welcome_page", get_defined_vars());
1225
+
1226
+ echo '<table class="form-table">'."\n";
1227
+ echo '<tbody>'."\n";
1228
+ echo '<tr>'."\n";
1229
+
1230
+ echo '<th>'."\n";
1231
+ echo '<label for="ws-plugin--s2member-login-welcome-page">'."\n";
1232
+ echo 'Login Welcome Page:'."\n";
1233
+ echo '</label>'."\n";
1234
+ echo '</th>'."\n";
1235
+
1236
+ echo '</tr>'."\n";
1237
+ echo '<tr>'."\n";
1238
+
1239
+ echo '<td>'."\n";
1240
+ echo '<select name="ws_plugin__s2member_login_welcome_page" id="ws-plugin--s2member-login-welcome-page">'."\n";
1241
+ echo '<option value="">&mdash; Select &mdash;</option>'."\n";
1242
+ foreach(($ws_plugin__s2member_temp_a = array_merge((array)get_pages())) as $ws_plugin__s2member_temp_o)
1243
+ echo '<option value="'.esc_attr($ws_plugin__s2member_temp_o->ID).'"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_override"] && $ws_plugin__s2member_temp_o->ID == $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_welcome_page"]) ? ' selected="selected"' : '').'>'.esc_html($ws_plugin__s2member_temp_o->post_title).'</option>'."\n";
1244
+ echo '</select><br />'."\n";
1245
+ echo 'Please choose a Page to be used as the first page Members will see after logging in. This Page can contain anything you like. We recommend the following title: <code>Welcome To Our Members Area</code>.'."\n";
1246
+ echo '</td>'."\n";
1247
+
1248
+ echo '</tr>'."\n";
1249
+ echo '<tr>'."\n";
1250
+
1251
+ echo '<th>'."\n";
1252
+ echo '<label for="ws-plugin--s2member-login-redirection-override">'."\n";
1253
+ echo 'Or, a Special Redirection URL?'."\n";
1254
+ echo '</label>'."\n";
1255
+ echo '</th>'."\n";
1256
+
1257
+ echo '</tr>'."\n";
1258
+ echo '<tr>'."\n";
1259
+
1260
+ echo '<td>'."\n";
1261
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_login_redirection_override" id="ws-plugin--s2member-login-redirection-override" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_override"]).'" /><br />'."\n";
1262
+ echo 'Or, you may configure a Special Redirection URL, if you prefer. You\'ll need to type in the full URL, starting with: <code>http://</code>. <em>A few <a href="#" onclick="alert(\'Replacement Codes:\\n\\n%%current_user_login%% = The current User\\\'s Username, lowercase (deprecated, please use %%current_user_nicename%%).\\n\\n%%current_user_nicename%% = The current User\\\'s Nicename in lowercase format (i.e., a cleaner version of the username for URLs; recommended for best compatibility).\\n\\n%%current_user_id%% = The current User\\\'s ID.\\n\\n%%current_user_level%% = The current User\\\'s s2Member Level.\\n\\n%%current_user_role%% = The current User\\\'s WordPress Role.'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '\\n\\n%%current_user_ccaps%% = The current User\\\'s Custom Capabilities.' : '').'\\n\\n%%current_user_logins%% = Number of times the current User has logged in.\\n\\nFor example, if you\\\'re using BuddyPress, and you want to redirect Members to their BuddyPress Profile page after logging in, you would setup a Special Redirection URL, like this: '.home_url("/members/%%current_user_nicename%%/profile/").'\\n\\nOr ... using %%current_user_level%%, you could have a separate Login Welcome Page for each Membership Level that you plan to offer. BuddyPress not required.\'); return false;">Replacement Codes</a> are also supported here.</em>'."\n";
1263
+ echo '</td>'."\n";
1264
+
1265
+ echo '</tr>'."\n";
1266
+ echo '</tbody>'."\n";
1267
+ echo '</table>'."\n";
1268
+
1269
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
1270
+
1271
+ echo '<table class="form-table" style="margin-top:0;">'."\n";
1272
+ echo '<tbody>'."\n";
1273
+ echo '<tr>'."\n";
1274
+
1275
+ echo '<th>'."\n";
1276
+ echo '<label for="ws-plugin--s2member-login-redirection-always-http">'."\n";
1277
+ echo 'Always Redirect non-Administrative Users (after login) using HTTP?'."\n";
1278
+ echo '</label>'."\n";
1279
+ echo '</th>'."\n";
1280
+
1281
+ echo '</tr>'."\n";
1282
+ echo '<tr>'."\n";
1283
+
1284
+ echo '<td>'."\n";
1285
+ echo '<select name="ws_plugin__s2member_login_redirection_always_http" id="ws-plugin--s2member-login-redirection-always-http">'."\n";
1286
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_always_http"]) ? ' selected="selected"' : '').'>No, do NOT modify (use WordPress default behavior; i.e., detect URL scheme automatically)</option>'."\n";
1287
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["login_redirection_always_http"]) ? ' selected="selected"' : '').'>Yes, always redirect non-administrative users to non-SSL version (i.e., always use http://)</option>'."\n";
1288
+ echo '</select><br />'."\n";
1289
+ echo 'Recommended setting: <code>Yes</code>. This is compatible w/ <a href="http://codex.wordpress.org/Administration_Over_SSL" target="_blank" rel="external"><code>FORCE_SSL_LOGIN</code></a> and/or <a href="http://codex.wordpress.org/Administration_Over_SSL" target="_blank" rel="external"><code>FORCE_SSL_ADMIN</code></a>.'."\n";
1290
+ echo '</td>'."\n";
1291
+
1292
+ echo '</tr>'."\n";
1293
+ echo '</tbody>'."\n";
1294
+ echo '</table>'."\n";
1295
+ echo '</div>'."\n";
1296
+
1297
+ echo '</div>'."\n";
1298
+
1299
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_login_welcome_page", get_defined_vars());
1300
+ }
1301
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_membership_options_page", TRUE, get_defined_vars()))
1302
+ {
1303
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_membership_options_page", get_defined_vars());
1304
+
1305
+ echo '<div class="ws-menu-page-group" title="Membership Options Page">'."\n";
1306
+
1307
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-membership-options-page-section">'."\n";
1308
+ echo '<h3>Membership Options Page (required, please customize this)</h3>'."\n";
1309
+ echo '<p>Please create and/or choose an existing Page that showcases your Membership Options. This special Page is where you\'ll insert the Payment Button(s) generated for you by s2Member (or Pro-Forms; if you\'re running s2Member Pro). This Page serves as your lead-in signup page <em>(i.e., you\'ll give visitors one or more registration options here, and they\'ll pay for the option they choose)</em>.</p>'."\n";
1310
+ echo '<p>Your Membership Options Page should detail all of the features that come with Membership to your site, and provide a Payment Button (or Pro-Form; if you\'re running s2Member Pro) for each Level of access you plan to offer. This is also the Page that anyone could be redirected to <em>(by s2Member)</em>, should they attempt to access an area of your site that requires access to something they\'re currenty not allowed to view.</p>'."\n";
1311
+ echo '<p><em><strong>Tip:</strong> If you allow Open Registration (i.e., Free Subscribers), you might want to place a link on your Membership Options Page, which points directly to your free Registration Form, instead of routing a Customer through your Payment Gateway first. It\'s a matter of preference though. For further details, please check the section above: <strong>s2Member ⥱ General Options ⥱ Open Registration</strong>.</em></p>'."\n";
1312
+ echo c_ws_plugin__s2member_utils_conds::bp_is_installed() ? '<p><em><strong>BuddyPress:</strong> Even with BuddyPress, s2Member still needs a Membership Options Page. This is where your Payment Buttons (or Pro-Forms; if you\'re running s2Member Pro) will go, giving people the ability to pay you. And again, this is also the Page that anyone could be redirected to <em>(by s2Member)</em>, should they attempt to access an area of your site that requires access to something they\'re currenty not allowed to view.</em></p>'."\n" : '';
1313
+ echo '<p><em><strong>Always Public:</strong> This Page must be public at all times. In fact, s2Member will not allow this Page to be restricted in any way. <strong>Note:</strong> for technical reasons, your Membership Options Page <strong>cannot</strong> be set to your Front Page (i.e., your Home Page); or your Posts Page (i.e., your main Blog page). Please create a separate (dedicated) Page in WordPress, and then designate it as your Membership Options Page below.</em></p>'."\n";
1314
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_membership_options_page", get_defined_vars());
1315
+
1316
+ echo '<table class="form-table">'."\n";
1317
+ echo '<tbody>'."\n";
1318
+ echo '<tr>'."\n";
1319
+
1320
+ echo '<th>'."\n";
1321
+ echo '<label for="ws-plugin--s2member-membership-options-page">'."\n";
1322
+ echo 'Membership Options Page:'."\n";
1323
+ echo '</label>'."\n";
1324
+ echo '</th>'."\n";
1325
+
1326
+ echo '</tr>'."\n";
1327
+ echo '<tr>'."\n";
1328
+
1329
+ echo '<td>'."\n";
1330
+ echo '<select name="ws_plugin__s2member_membership_options_page" id="ws-plugin--s2member-membership-options-page">'."\n";
1331
+ echo '<option value="">&mdash; Select &mdash;</option>'."\n";
1332
+ foreach(($ws_plugin__s2member_temp_a = array_merge((array)get_pages())) as $ws_plugin__s2member_temp_o)
1333
+ echo '<option value="'.esc_attr($ws_plugin__s2member_temp_o->ID).'"'.(($ws_plugin__s2member_temp_o->ID == $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"]) ? ' selected="selected"' : '').'>'.esc_html($ws_plugin__s2member_temp_o->post_title).'</option>'."\n";
1334
+ echo '</select><br />'."\n";
1335
+ echo 'Please choose a Page that provides Users with a way to sign up. We recommend the following title: <code>Membership Signup</code>'."\n";
1336
+ echo '</td>'."\n";
1337
+
1338
+ echo '</tr>'."\n";
1339
+ echo '</tbody>'."\n";
1340
+ echo '</table>'."\n";
1341
+
1342
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
1343
+
1344
+ echo '<table class="form-table">'."\n";
1345
+ echo '<tbody>'."\n";
1346
+ echo '<tr>'."\n";
1347
+
1348
+ echo '<th>'."\n";
1349
+ echo '<label for="ws-plugin--s2member-membership-options-page-vars-enable">'."\n";
1350
+ echo 'Enable MOP Vars (i.e., Membership Options Page Variables)?'."\n";
1351
+ echo '</label>'."\n";
1352
+ echo '</th>'."\n";
1353
+
1354
+ echo '</tr>'."\n";
1355
+ echo '<tr>'."\n";
1356
+
1357
+ echo '<td>'."\n";
1358
+ echo '<select name="ws_plugin__s2member_membership_options_page_vars_enable" id="ws-plugin--s2member-membership-options-page-vars-enable">'."\n";
1359
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page_vars_enable"]) ? ' selected="selected"' : '').'>Yes (enable MOP Vars in all redirections; recommended behavior)</option>'."\n";
1360
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page_vars_enable"]) ? ' selected="selected"' : '').'>No (don\'t include the additional details provided by MOP Vars)</option>'."\n";
1361
+ echo '</select><br />'."\n";
1362
+ echo 'See also: <strong>Dashboard ⥱ s2Member ⥱ API / Scripting ⥱ Membership Options Page / Variables</strong><br />'."\n";
1363
+ echo 'Recommended setting: (<code>Yes, enable MOP Vars</code>)'."\n";
1364
+ echo '</td>'."\n";
1365
+
1366
+ echo '</tr>'."\n";
1367
+ echo '</tbody>'."\n";
1368
+ echo '</table>'."\n";
1369
+ echo '</div>'."\n";
1370
+
1371
+ echo '</div>'."\n";
1372
+
1373
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_membership_options_page", get_defined_vars());
1374
+ }
1375
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_profile_modifications", TRUE, get_defined_vars()))
1376
+ {
1377
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_profile_modifications", get_defined_vars());
1378
+
1379
+ echo '<div class="ws-menu-page-group" title="Member Profile Modifications">'."\n";
1380
+
1381
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-profile-modifications-section">'."\n";
1382
+ echo '<h3>Giving Members The Ability To Modify Their Profile</h3>'."\n";
1383
+ echo '<p>s2Member can be configured to redirect Members away from the <a href="'.esc_attr(admin_url("/profile.php")).'" target="_blank" rel="external">default Profile Editing Panel</a> that is built into WordPress. When/if a Member attempts to access the default Profile Editing Panel, they\'ll instead be redirected to the Login Welcome Page that you\'ve configured with s2Member. <strong>Why would I redirect away from the default Profile Editing Panel?</strong> Unless you\'ve made some drastic modifications to your WordPress installation, the default Profile Editing Panel that comes with WordPress is <em>not</em> suited for any sort of public access.</p>'."\n";
1384
+ echo '<p>So instead of using this default Profile Editing Panel, s2Member provides you <em>(the site owner)</em> with a special Shortcode: <code>[s2Member-Profile /]</code>. You can insert this into your Login Welcome Page, or any Post/Page for that matter <em>(even into a Text Widget)</em>. This Shortcode produces an Inline Profile Editing Form that supports all aspects of s2Member, including Password changes; and any Custom Registration/Profile Fields that you\'ve configured with s2Member. Alternatively, you can send your Members to a <a href="'.esc_attr(home_url("/?s2member_profile=1")).'" target="_blank" rel="external">special Stand-Alone version</a>. The stand-alone version makes it possible for you to <a href="#" onclick="if(!window.open(\''.home_url("/?s2member_profile=1").'\', \'_popup\', \'width=600,height=400,left=100,screenX=100,top=100,screenY=100,location=0,menubar=0,toolbar=0,status=0,scrollbars=1,resizable=1\')) alert(\'Please disable popup blockers and try again!\'); return false;" rel="external">open it up in a popup window</a>, or embed it into your Login Welcome Page using an IFRAME. Code samples below.</p>'."\n";
1385
+ echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress already provides Users/Members with a Profile Editing Panel, powered by your theme. If you\'ve configured Custom Registration/Profile Fields with s2Member, you can also enable s2Member\'s Profile Field integration with BuddyPress (recommended). For further details, see: <strong>s2Member ⥱ General Options ⥱ Registration/Profile Fields</strong>.</em></p>'."\n" : '';
1386
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_profile_modifications", get_defined_vars());
1387
+
1388
+ echo '<table class="form-table">'."\n";
1389
+ echo '<tbody>'."\n";
1390
+ echo '<tr>'."\n";
1391
+
1392
+ echo '<th>'."\n";
1393
+ echo '<label for="ws-plugin--s2member-force-admin-lockouts">'."\n";
1394
+ echo 'Redirect Members away from the Default Profile Panel?'."\n";
1395
+ echo '</label>'."\n";
1396
+ echo '</th>'."\n";
1397
+
1398
+ echo '</tr>'."\n";
1399
+ echo '<tr>'."\n";
1400
+
1401
+ echo '<td>'."\n";
1402
+ echo '<select name="ws_plugin__s2member_force_admin_lockouts" id="ws-plugin--s2member-force-admin-lockouts">'."\n";
1403
+ echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["force_admin_lockouts"]) ? ' selected="selected"' : '').'>No (I want to use the WordPress default methodologies)</option>'."\n";
1404
+ echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["force_admin_lockouts"]) ? ' selected="selected"' : '').'>Yes (redirect to Login Welcome Page; locking all /wp-admin/ areas)</option>'."\n";
1405
+ echo '</select><br />'."\n";
1406
+ echo 'Recommended setting (<code>Yes</code>). <em><strong>Note:</strong> When this is set to (<code>Yes</code>), s2Member will take an initiative to further safeguard ALL <code>/wp-admin/</code> areas of your installation; not just the Default Profile Panel.</em>'."\n";
1407
+ echo '</td>'."\n";
1408
+
1409
+ echo '</tr>'."\n";
1410
+ echo '</tbody>'."\n";
1411
+ echo '</table>'."\n";
1412
+
1413
+ echo '<div class="ws-menu-page-hr"></div>'."\n";
1414
+
1415
+ echo '<p style="margin-bottom:0;"><strong>Shortcode (copy/paste)</strong>, for an Inline Profile Modification Form:<br />'."\n";
1416
+ echo '<p style="margin-top:0;"><input type="text" autocomplete="off" value="'.format_to_edit('[s2Member-Profile /]').'" onclick="this.select ();" /></p>'."\n";
1417
+
1418
+ echo '<p style="margin-top:25px; margin-bottom:0;"><strong>Stand-Alone (copy/paste)</strong>, for popup window:</p>'."\n";
1419
+ echo '<p style="margin-top:0;"><input type="text" autocomplete="off" value="'.format_to_edit(preg_replace("/\<\?php echo S2MEMBER_CURRENT_USER_PROFILE_MODIFICATION_PAGE_URL; \?\>/", c_ws_plugin__s2member_utils_strings::esc_refs(home_url("/?s2member_profile=1")), file_get_contents(dirname(__FILE__)."/code-samples/current-user-profile-modification-page-url-2-ops.x-php"))).'" onclick="this.select ();" /></p>'."\n";
1420
+ echo '</div>'."\n";
1421
+
1422
+ echo '</div>'."\n";
1423
+
1424
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_profile_modifications", get_defined_vars());
1425
+ }
1426
+ if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_url_shortening", TRUE, get_defined_vars()))
1427
+ {
1428
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_url_shortening", get_defined_vars());
1429
+
1430
+ echo '<div class="ws-menu-page-group" title="URL Shortening Service Preference">'."\n";
1431
+
1432
+ echo '<div class="ws-menu-page-section ws-plugin--s2member-url-shortening-section">'."\n";
1433
+ echo '<h3>URL Shortening Service API (Preference)</h3>'."\n";
1434
+ echo '<p>In a few special cases, long URLs generated by s2Member (i.e., those containing encrypted authentication details) will be shortened. URLs are shortened automatically, using one of the URL Shortening APIs <em>(listed below)</em>. A shortened URL prevents issues with very long links becoming corrupted by a Customer\'s email application. For instance, the Signup Confirmation Email that s2Member sends out to a new paying Customer may contain a link which is shortened to prevent corruption by email applications. By default, s2Member uses the tinyURL API. tinyURL has proven itself to be the most reliable. However, in cases where an API service call fails, s2Member will automatically use one or more of its other APIs as a backup. The option below allows you to configure which URL Shortening API s2Member should try first <em>(i.e., the one you prefer)</em>.</p>'."\n";
1435
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_url_shortening", get_defined_vars());
1436
+
1437
+ echo '<table class="form-table">'."\n";
1438
+ echo '<tbody>'."\n";
1439
+ echo '<tr>'."\n";
1440
+
1441
+ echo '<th>'."\n";
1442
+ echo '<label for="ws-plugin--s2member-default-url-shortener">'."\n";
1443
+ echo 'URL Shortening Service API (Preference):'."\n";
1444
+ echo '</label>'."\n";
1445
+ echo '</th>'."\n";
1446
+
1447
+ echo '</tr>'."\n";
1448
+ echo '<tr>'."\n";
1449
+
1450
+ echo '<td>'."\n";
1451
+ echo '<select name="ws_plugin__s2member_default_url_shortener" id="ws-plugin--s2member-default-url-shortener">'."\n";
1452
+ echo '<option value="tiny_url"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["default_url_shortener"] === "tiny_url") ? ' selected="selected"' : '').'>tinyurl.com (free tinyURL API service)</option>'."\n";
1453
+ echo '<option value="goo_gl"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["default_url_shortener"] === "goo_gl") ? ' selected="selected"' : '').'>goo.gl (free Google URL Shortening API service)</option>'."\n";
1454
+ echo '</select>'."\n";
1455
+ echo '</td>'."\n";
1456
+
1457
+ echo '</tr>'."\n";
1458
+ echo '<tr>'."\n";
1459
+
1460
+ echo '<th>'."\n";
1461
+ echo '<label for="ws-plugin--s2member-default-custom-str-url-shortener">'."\n";
1462
+ echo 'Custom URL Shortening Service API (Optional/Advanced):'."\n";
1463
+ echo '</label>'."\n";
1464
+ echo '</th>'."\n";
1465
 
1466
+ echo '</tr>'."\n";
1467
+ echo '<tr>'."\n";
 
 
1468
 
1469
+ echo '<td>'."\n";
1470
+ echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_default_custom_str_url_shortener" id="ws-plugin--s2member-default-custom-str-url-shortener" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["default_custom_str_url_shortener"]).'" /><br />'."\n";
1471
+ echo 'Your own custom URL <code>(i.e., GET request)</code>, with <code>%%s2_long_url%%</code> Replacement Code. [<a href="#" onclick="alert(\'Advanced site owners can use a custom URL shortening service they prefer.\\n\\nIn order for this to work, your URL shortening service must support simple GET requests through its API (sometimes referred to as a REST or NVP API).\\n\\nIn addition, your URL shortening service must return a plain-text URL in the response. See example below.\\n\\nBitly example GET request with format=txt:\\n\\nhttp://api.bitly.com/v3/shorten?login=demo&apiKey=2d71bf07&format=txt&longUrl=%%s2_long_url%%\\n\\ns2Member expects a shortened URL in the response from Bitly.\\n\\nIf you configure s2Member to use your own custom URL shortening service, s2Member will try your configuration first, and if anything fails, it will fall back on its own pre-integrated backups.\\n\\nWhen configuring your URL for the GET request, s2Member makes two Replacement Codes available:\\n\\n%%s2_long_url%% = The full URL that needs to be shortened (raw URL-encoded).\\n\\n%%s2_long_url_md5%% = An MD5 hash of the full URL (might be useful in some APIs).\'); return false;" tabindex="-1">details</a>]<br />'."\n";
1472
+ echo '</td>'."\n";
1473
 
1474
+ echo '</tr>'."\n";
1475
+ echo '</tbody>'."\n";
1476
+ echo '</table>'."\n";
1477
+ echo '</div>'."\n";
1478
 
1479
+ echo '</div>'."\n";
1480
 
1481
+ do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_url_shortening", get_defined_vars());
1482
+ }
1483
+ do_action("ws_plugin__s2member_during_gen_ops_page_after_left_sections", get_defined_vars());
1484
 
1485
+ echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
1486
 
1487
+ echo '</form>'."\n";
1488
 
1489
+ echo '</td>'."\n";
1490
 
1491
+ echo '<td class="ws-menu-page-table-r">'."\n";
1492
+ c_ws_plugin__s2member_menu_pages_rs::display();
1493
+ echo '</td>'."\n";
1494
 
1495
+ echo '</tr>'."\n";
1496
+ echo '</tbody>'."\n";
1497
+ echo '</table>'."\n";
1498
 
1499
+ echo '</div>'."\n";
 
1500
  }
1501
  }
1502
+ }
1503
 
1504
+ new c_ws_plugin__s2member_menu_page_gen_ops ();
 
includes/menu-pages/info.inc.php DELETED
@@ -1,81 +0,0 @@
1
- <?php
2
- /**
3
- * Menu page for the s2Member plugin (s2Member Info page).
4
- *
5
- * Copyright: © 2009-2011
6
- * {@link http://www.websharks-inc.com/ WebSharks, Inc.}
7
- * (coded in the USA)
8
- *
9
- * Released under the terms of the GNU General Public License.
10
- * You should have received a copy of the GNU General Public License,
11
- * along with this software. In the main directory, see: /licensing/
12
- * If not, see: {@link http://www.gnu.org/licenses/}.
13
- *
14
- * @package s2Member\Menu_Pages
15
- * @since 3.0
16
- */
17
- if(!defined('WPINC')) // MUST have WordPress.
18
- exit ("Do not access this file directly.");
19
-
20
- if (!class_exists ("c_ws_plugin__s2member_menu_page_info"))
21
- {
22
- /**
23
- * Menu page for the s2Member plugin (s2Member Info page).
24
- *
25
- * @package s2Member\Menu_Pages
26
- * @since 110531
27
- */
28
- class c_ws_plugin__s2member_menu_page_info
29
- {
30
- public function __construct ()
31
- {
32
- echo '<div class="wrap ws-menu-page">' . "\n";
33
-
34
- echo '<div class="ws-menu-page-toolbox">'."\n";
35
- c_ws_plugin__s2member_menu_pages_tb::display ();
36
- echo '</div>'."\n";
37
-
38
- echo '<h2>Specs / Info</h2>' . "\n";
39
-
40
- echo '<table class="ws-menu-page-table">' . "\n";
41
- echo '<tbody class="ws-menu-page-table-tbody">' . "\n";
42
- echo '<tr class="ws-menu-page-table-tr">' . "\n";
43
- echo '<td class="ws-menu-page-table-l">' . "\n";
44
-
45
- echo '<img src="' . esc_attr ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]) . '/images/brand-icon.png" class="ws-menu-page-brand-icon" alt="." />' . "\n";
46
-
47
- echo '<a href="' . esc_attr (add_query_arg ("c_check_ver", urlencode (c_ws_plugin__s2member_readmes::parse_readme_value ("Version")), c_ws_plugin__s2member_readmes::parse_readme_value ("Plugin URI"))) . '" target="_blank"><img src="' . esc_attr ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]) . '/images/brand-updates.png" class="ws-menu-page-brand-updates" alt="." /></a>' . "\n";
48
-
49
- do_action("ws_plugin__s2member_during_info_page_before_left_sections", get_defined_vars ());
50
-
51
- if (apply_filters("ws_plugin__s2member_during_info_page_during_left_sections_display_readme", true, get_defined_vars ()))
52
- {
53
- do_action("ws_plugin__s2member_during_info_page_during_left_sections_before_readme", get_defined_vars ());
54
-
55
- echo '<div class="ws-menu-page-readme">' . "\n";
56
- do_action("ws_plugin__s2member_during_info_page_during_left_sections_during_readme", get_defined_vars ());
57
- echo c_ws_plugin__s2member_readmes::parse_readme () . "\n";
58
- echo '</div>' . "\n";
59
-
60
- do_action("ws_plugin__s2member_during_info_page_during_left_sections_after_readme", get_defined_vars ());
61
- }
62
-
63
- do_action("ws_plugin__s2member_during_info_page_after_left_sections", get_defined_vars ());
64
-
65
- echo '</td>' . "\n";
66
-
67
- echo '<td class="ws-menu-page-table-r">' . "\n";
68
- c_ws_plugin__s2member_menu_pages_rs::display ();
69
- echo '</td>' . "\n";
70
-
71
- echo '</tr>' . "\n";
72
- echo '</tbody>' . "\n";
73
- echo '</table>' . "\n";
74
-
75
- echo '</div>' . "\n";
76
- }
77
- }
78
- }
79
-
80
- new c_ws_plugin__s2member_menu_page_info ();
81
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/menu-pages/logs.inc.php CHANGED
@@ -52,23 +52,23 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_logs"))
52
 
53
  echo '<div class="ws-menu-page-section ws-plugin--s2member-help">'."\n";
54
  echo '<h3>Getting Help w/ s2Member (Troubleshooting)</h3>'."\n";
55
- echo '<p>s2Member is pretty easy to setup and install initially. Most of the official documentation is right here in your Dashboard (i.e. there is a lot of inline documentation built into the software). That being said, it CAN take some time to master everything there is to know about s2Member\'s advanced features. If you need assistance with s2Member, please search the <a href="http://www.s2member.com/kb/" target="_blank" rel="external">s2Member Knowledge Base</a>, <a href="http://www.s2member.com/videos/" target="_blank" rel="external">Video Tutorials</a>, <a href="http://www.s2member.com/forums/" target="_blank" rel="external">Forums</a> and <a href="http://www.s2member.com/codex/" target="_blank" rel="external">Codex</a>. If you are planning to do something creative with s2Member, you might want to <a href="http://jobs.wordpress.net" target="_blank" rel="external">hire a freelance developer</a> to assist you.</p>'."\n";
56
  echo '<p><strong>See also:</strong> <a href="http://www.s2member.com/kb/common-troubleshooting-tips/" target="_blank" rel="external">s2Member Troubleshooting Guide</a> (please read this first if you\'re having trouble).</p>'."\n";
57
 
58
  echo '<div class="ws-menu-page-hr"></div>'."\n";
59
 
60
- echo '<h3>Testing Server Compatibility</h3>'."\n";
61
  echo '<p>Please download the <a href="http://www.s2member.com/r/server-check-tool/">s2Member Server Scanner</a>. Unzip, upload via FTP; then open in a browser for a full report.</p>'."\n";
62
 
63
  echo '<div class="ws-menu-page-hr"></div>'."\n";
64
 
65
- echo '<h3>Troubleshooting Payment Gateway Integrations</h3>'."\n";
66
  echo '<p>Please use the s2Member Log Viewer (below). Log files can be very helpful.</p>'."\n";
67
 
68
  echo '<div class="ws-menu-page-hr"></div>'."\n";
69
 
70
- echo '<h3>Search s2Member KB Articles, Forums, Codex and more<em>!</em></h3>'."\n";
71
- echo '<form method="get" action="http://www.s2member.com/quick-s.php" target="_blank" onsubmit="if(this.q.value === \'enter search terms...\') this.q.value = \'\';">'."\n";
72
  echo '<p><input type="text" name="q" value="enter search terms..." style="width:60%;" onfocus="if(this.value === \'enter search terms...\') this.value = \'\';" onblur="if(this.value === \'\') this.value = \'enter search terms...\';" /> <input type="submit" value="Search" style="font-size:120%; font-weight:normal;" /></p>'."\n";
73
  echo '</form>'."\n";
74
 
@@ -88,19 +88,19 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_logs"))
88
  echo '<h3>Logging Configuration</h3>'."\n";
89
 
90
  echo '<div class="info">'."\n";
91
- echo '<p style="font-size:110%; margin-top:0;"><span>We HIGHLY recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems that occur during processing. Enable logging here, and then view your log files below, in the s2Member Log Viewer.</span></p>'."\n";
92
- echo '<p style="font-size:110%; margin-bottom:0;"><span class="ws-menu-page-error">However, it is VERY IMPORTANT to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We STRONGLY suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
93
  echo '</div>'."\n";
94
 
95
- echo '<div class="notice" style="margin-bottom:0;">'."\n";
96
- echo '<p style="font-size:110%; margin-bottom:0;"><span>Regarding s2Member Security Badges. If debug logging is enabled, your site will NOT qualify for an s2Member Security Badge until you disable logging (and you must ALSO download, and then delete any existing log files). For further details, please see KB Article: <a href="http://www.s2member.com/kb/security-badges/" target="_blank" rel="external">s2Member Security Badges</a>.</span></p>'."\n";
97
  echo '</div>'."\n";
98
 
99
  echo '<div class="ws-menu-page-hr"></div>'."\n";
100
 
101
  do_action("ws_plugin__s2member_during_logs_page_during_left_sections_during_log_settings", get_defined_vars());
102
 
103
- echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form">'."\n";
104
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
105
 
106
  echo '<table class="form-table">'."\n";
@@ -143,7 +143,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_logs"))
143
  echo '</tbody>'."\n";
144
  echo '</table>'."\n";
145
 
146
- echo '<p class="submit" style="margin-top:20px;">'."\n";
147
  echo '<input type="submit" value="Update Logging Configuration" />'."\n";
148
  echo '</p>'."\n";
149
 
@@ -167,30 +167,30 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_logs"))
167
 
168
  echo '<div class="ws-menu-page-hr"></div>'."\n";
169
 
170
- echo '<form method="post" onsubmit="if(!confirm(\'Archive all existing log files?\n\nAll of your current log files will be archived (i.e. they will simply be renamed with an ARCHIVED tag &amp; date in their file name); and new log files will be created automatically the next time s2Member logs something on your installation.\n\nPlease click OK to confirm this action.\')) return false;">'."\n";
171
  echo '<input type="hidden" name="ws_plugin__s2member_logs_archive_start_fresh" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-logs-archive-start-fresh")).'" />'."\n";
172
- echo '<input type="submit" value="Archive All Current Log Files" class="ws-menu-page-right ws-plugin--s2member-archive-logs-start-fresh-button" style="font-size:110%; font-weight:normal; clear:right; min-width:200px;" />'."\n";
173
  echo '</form>'."\n";
174
 
175
- echo '<form method="post" onsubmit="if(!confirm(\'Delete all existing log files?\n\nThis will permanently delete ALL of your existing log files (including any archived log files).\n\nPlease click OK to confirm this action.\')) return false;">'."\n";
176
  echo '<input type="hidden" name="ws_plugin__s2member_logs_delete_start_fresh" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-logs-delete-start-fresh")).'" />'."\n";
177
- echo '<input type="submit" value="Permanently Delete All Log Files" class="ws-menu-page-right ws-plugin--s2member-delete-logs-start-fresh-button" style="font-size:110%; font-weight:normal; clear:right; min-width:200px;" />'."\n";
178
  echo '</form>'."\n";
179
 
180
- echo '<form method="post">'."\n";
181
  echo '<input type="hidden" name="ws_plugin__s2member_logs_download_zip" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-logs-download-zip")).'" />'."\n";
182
- echo '<input type="submit" value="Download All Log Files (Zip File)" class="ws-menu-page-right ws-plugin--s2member-logs-download-zip-button" style="font-size:110%; font-weight:normal; clear:right; min-width:200px;" />'."\n";
183
  echo '</form>'."\n";
184
 
185
- echo '<p><strong>Debugging Tips:</strong> &nbsp;&nbsp; It is normal to see a few errors in your log files. This is because s2Member logs ALL of its communication with Payment Gateways. Everything not just successes. With that in mind, there will be some failures that s2Member expects (to a certain extent); and s2Member deals with these gracefully. What you\'re looking for here, are things that jump right out at you as being a major issue (e.g. when s2Member makes a point of providing details to you in a log entry about problems that should be corrected on your installation). Please read carefully.</p>'."\n";
186
- echo '<p><strong>Test Transaction Tips:</strong> &nbsp;&nbsp; Generally speaking, it is best to run test transactions for yourself. Be sure to run your final test transactions against a live Payment Gateway that is NOT in Sandbox/Test Mode (<a href="#" onclick="alert(\'While some Payment Gateways make it possible for you to run test transactions in Sandbox/Test Mode, these are NOT a reliable way to test s2Member.\n\nOften times (particularly with PayPal) Sandbox/Test mode behaves somewhat differently often with buggy behavior. This can really create frustration for site owners. Therefore, it is always a good idea to run low-dollar test transactions against a live Payment Gateway.\n\nAlso, please be sure that you are NOT logged in as an Administrator when running test transactions. For most test transactions, you will want to be completely logged out of your site before completing checkout (just like a new Customer would be). If you are testing an upgrade or downgrade (where you DO need to be logged-in), please do NOT attempt this under an Administrative account. s2Member will NOT upgrade/downgrade Administrative accounts for security purposes.\'); return false;">click here for details</a>). After running test transactions, please review the log file entries pertaining to your transaction. Does s2Member report any major issues? If so, please read through any details that s2Member provides in the log file. If you need assistance, please <a href="http://www.s2member.com/quick-s.php" target="_blank" rel="external">search s2Member.com</a> for answers to common questions.</p>'."\n";
187
- echo '<p><strong>s2 Core Processors:</strong> &nbsp;&nbsp; It is normal to have a <code>paypay-ipn.log</code> and/or a <code>paypay-rtn.log</code> file at all times. Ultimately, all Payment Gateway integrations supported by s2Member pass through it\'s core PayPal processors; even if you\'ve integrated with another Payment Gateway. If you are having trouble, and you don\'t find any errors in your Payment Gateway log files, please check the <code>paypay-ipn.log</code> and <code>paypay-rtn.log</code> files too. Regarding s2Member Pro Forms... If you\'ve integrated s2Member Pro Forms, you will NOT have a <code>paypay-rtn.log</code> file, because that particular processor is not used with Pro Form integrations. However, you will have a <code>paypay-ipn.log</code> file, and you will need to make a point of inspecting this file to ensure there were no post-processing issues.</p>'."\n";
188
- echo '<p><strong>s2 HTTP API Logs:</strong> &nbsp;&nbsp; If s2Member is not behaving as expected, and you cannot find errors anywhere in your Payment Gateway log files (or with any core PayPal processors), please review your <code>s2-http-api-debug.log</code> file too. Look for any HTTP connections where s2Member is getting <code>403</code>, <code>404</code>, <code>503</code> errors from your server. This can sometimes happen due to <a href="http://www.s2member.com/kb/mod-security-random-503-403-errors/" target="_blank" rel="external">paranoid Mod Security configurations</a>, and it may require you to contact your hosting company for assistance.</p>'."\n";
189
- echo '<p style="font-style:italic;"><strong>Archived Log Files:</strong> &nbsp;&nbsp; All s2Member log files are stored here: <code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["logs_dir"])).'</code>. Any log files that contain the word <code>ARCHIVED</code> in their name, are files that reached a size of more than 2MB, so s2Member archived them automatically to prevent any single log file from becoming too large. Archived log file names will also contain the date/time they were archived by s2Member. These archived log files typically contain much older (and possibly outdated) log entries.</p>'."\n";
190
 
191
  echo '<div class="ws-menu-page-hr"></div>'."\n";
192
 
193
- echo '<h3>s2Member Log File Descriptions (for ALL possible log file names)</h3>'."\n";
194
 
195
  echo '<div class="ws-menu-page-hr"></div>'."\n";
196
 
@@ -229,7 +229,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_logs"))
229
  unset($_k, $_v); // Housekeeping.
230
 
231
  if(preg_match("/\.log$/", $_log_file) && stripos($_log_file, "-ARCHIVED-") === FALSE)
232
- $log_file_options .= '<option data-type="current" title="'.esc_attr($_log_file_description["long"]).'" value="'.esc_attr($_log_file).'"'.(($view_log_file === $_log_file) ? ' style="font-weight:bold;" selected="selected"' : '').'>'.esc_html($_log_file).' '.esc_html($_log_file_description["short"]).'</option>';
233
  }
234
  unset($_log_file_description, $_log_file); // Housekeeping.
235
  $log_file_options .= '</optgroup>';
@@ -254,7 +254,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_logs"))
254
  '<option value="" disabled="disabled"></option>'.
255
  $log_file_options;
256
 
257
- echo '<form method="post" name="ws_plugin__s2member_log_viewer" id="ws-plugin--s2member-log-viewer">'."\n";
258
 
259
  echo '<table class="form-table">'."\n";
260
  echo '<tbody>'."\n";
52
 
53
  echo '<div class="ws-menu-page-section ws-plugin--s2member-help">'."\n";
54
  echo '<h3>Getting Help w/ s2Member (Troubleshooting)</h3>'."\n";
55
+ echo '<p>s2Member is pretty easy to setup and install initially. Most of the official documentation is right here in your Dashboard (i.e., there is a lot of inline documentation built into the software). That being said, it can take some time to master everything there is to know about s2Member\'s advanced features. If you need assistance with s2Member, please search the <a href="http://www.s2member.com/kb/" target="_blank" rel="external">s2Member Knowledge Base</a>, <a href="http://www.s2member.com/videos/" target="_blank" rel="external">Video Tutorials</a>, <a href="http://www.s2member.com/forums/" target="_blank" rel="external">Forums</a> and <a href="http://www.s2member.com/codex/" target="_blank" rel="external">Codex</a>. If you are planning to do something creative with s2Member, you might want to <a href="http://jobs.wordpress.net" target="_blank" rel="external">hire a freelance developer</a> to assist you.</p>'."\n";
56
  echo '<p><strong>See also:</strong> <a href="http://www.s2member.com/kb/common-troubleshooting-tips/" target="_blank" rel="external">s2Member Troubleshooting Guide</a> (please read this first if you\'re having trouble).</p>'."\n";
57
 
58
  echo '<div class="ws-menu-page-hr"></div>'."\n";
59
 
60
+ echo '<h3 style="margin-bottom:0;">Testing Server Compatibility</h3>'."\n";
61
  echo '<p>Please download the <a href="http://www.s2member.com/r/server-check-tool/">s2Member Server Scanner</a>. Unzip, upload via FTP; then open in a browser for a full report.</p>'."\n";
62
 
63
  echo '<div class="ws-menu-page-hr"></div>'."\n";
64
 
65
+ echo '<h3 style="margin-bottom:0;">Troubleshooting Payment Gateway Integrations</h3>'."\n";
66
  echo '<p>Please use the s2Member Log Viewer (below). Log files can be very helpful.</p>'."\n";
67
 
68
  echo '<div class="ws-menu-page-hr"></div>'."\n";
69
 
70
+ echo '<h3 style="margin-bottom:0;">Search s2Member KB Articles, Forums, Codex and more<em>!</em></h3>'."\n";
71
+ echo '<form method="get" action="http://www.s2member.com/quick-s.php" target="_blank" onsubmit="if(this.q.value === \'enter search terms...\') this.q.value = \'\';" autocomplete="off">'."\n";
72
  echo '<p><input type="text" name="q" value="enter search terms..." style="width:60%;" onfocus="if(this.value === \'enter search terms...\') this.value = \'\';" onblur="if(this.value === \'\') this.value = \'enter search terms...\';" /> <input type="submit" value="Search" style="font-size:120%; font-weight:normal;" /></p>'."\n";
73
  echo '</form>'."\n";
74
 
88
  echo '<h3>Logging Configuration</h3>'."\n";
89
 
90
  echo '<div class="info">'."\n";
91
+ echo '<p style="margin-top:0;"><span>We highly recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems that occur during processing. Enable logging here, and then view your log files below, in the s2Member Log Viewer.</span></p>'."\n";
92
+ echo '<p style="margin-bottom:0;"><span class="ws-menu-page-error">However, it is very important to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We strongly suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
93
  echo '</div>'."\n";
94
 
95
+ echo '<div class="warning" style="margin-bottom:0;">'."\n";
96
+ echo '<p style="margin:0;"><span>Regarding s2Member Security Badges. If debug logging is enabled, your site will not qualify for an s2Member Security Badge until you disable logging (and you must also download, and then delete any existing log files). For further details, please see KB Article: <a href="http://www.s2member.com/kb/security-badges/" target="_blank" rel="external">s2Member Security Badges</a>.</span></p>'."\n";
97
  echo '</div>'."\n";
98
 
99
  echo '<div class="ws-menu-page-hr"></div>'."\n";
100
 
101
  do_action("ws_plugin__s2member_during_logs_page_during_left_sections_during_log_settings", get_defined_vars());
102
 
103
+ echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" autocomplete="off">'."\n";
104
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
105
 
106
  echo '<table class="form-table">'."\n";
143
  echo '</tbody>'."\n";
144
  echo '</table>'."\n";
145
 
146
+ echo '<p class="submit">'."\n";
147
  echo '<input type="submit" value="Update Logging Configuration" />'."\n";
148
  echo '</p>'."\n";
149
 
167
 
168
  echo '<div class="ws-menu-page-hr"></div>'."\n";
169
 
170
+ echo '<form method="post" onsubmit="if(!confirm(\'Archive all existing log files?\n\nAll of your current log files will be archived (i.e., they will simply be renamed with an ARCHIVED tag &amp; date in their file name); and new log files will be created automatically the next time s2Member logs something on your installation.\n\nPlease click OK to confirm this action.\')) return false;" autocomplete="off">'."\n";
171
  echo '<input type="hidden" name="ws_plugin__s2member_logs_archive_start_fresh" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-logs-archive-start-fresh")).'" />'."\n";
172
+ echo '<input type="submit" value="Archive All Current Log Files" class="ws-menu-page-right ws-plugin--s2member-archive-logs-start-fresh-button" style="font-size:110%; font-weight:normal; clear:right; min-width:300px;" />'."\n";
173
  echo '</form>'."\n";
174
 
175
+ echo '<form method="post" onsubmit="if(!confirm(\'Delete all existing log files?\n\nThis will permanently delete ALL of your existing log files (including any archived log files).\n\nPlease click OK to confirm this action.\')) return false;" autocomplete="off">'."\n";
176
  echo '<input type="hidden" name="ws_plugin__s2member_logs_delete_start_fresh" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-logs-delete-start-fresh")).'" />'."\n";
177
+ echo '<input type="submit" value="Permanently Delete All Log Files" class="ws-menu-page-right ws-plugin--s2member-delete-logs-start-fresh-button" style="font-size:110%; font-weight:normal; clear:right; min-width:300px;" />'."\n";
178
  echo '</form>'."\n";
179
 
180
+ echo '<form method="post" autocomplete="off">'."\n";
181
  echo '<input type="hidden" name="ws_plugin__s2member_logs_download_zip" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-logs-download-zip")).'" />'."\n";
182
+ echo '<input type="submit" value="Download All Log Files (Zip File)" class="ws-menu-page-right ws-plugin--s2member-logs-download-zip-button" style="font-size:110%; font-weight:normal; clear:right; min-width:300px;" />'."\n";
183
  echo '</form>'."\n";
184
 
185
+ echo '<p><strong>Debugging Tips:</strong> &nbsp;&nbsp; It is normal to see a few errors in your log files. This is because s2Member logs <em>all</em> of its communication with Payment Gateways. Everything—not just successes. With that in mind, there will be some failures that s2Member expects (to a certain extent); and s2Member deals with these gracefully. What you\'re looking for here, are things that jump right out at you as being a major issue (e.g., when s2Member makes a point of providing details to you in a log entry about problems that should be corrected on your installation). Please read carefully.</p>'."\n";
186
+ echo '<p><strong>Test Transaction Tips:</strong> &nbsp;&nbsp; Generally speaking, it is best to run test transactions for yourself. Be sure to run your final test transactions against a live Payment Gateway that is <em>not</em> in Sandbox/Test Mode (<a href="#" onclick="alert(\'While some Payment Gateways make it possible for you to run test transactions in Sandbox/Test Mode, these are not a reliable way to test s2Member.\n\nOften times (particularly with PayPal) Sandbox/Test mode behaves somewhat differently—often with buggy behavior. This can really create frustration for site owners. Therefore, it is always a good idea to run low-dollar test transactions against a live Payment Gateway.\n\nAlso, please be sure that you are not logged in as an Administrator when running test transactions. For most test transactions, you will want to be completely logged-out of your site before completing checkout (just like a new Customer would be). If you are testing an upgrade or downgrade (where you do need to be logged-in), please do not attempt this under an Administrative account. s2Member will not upgrade/downgrade Administrative accounts—for security purposes.\'); return false;">click here for details</a>). After running test transactions, please review the log file entries pertaining to your transaction. Does s2Member report any major issues? If so, please read through any details that s2Member provides in the log file. If you need assistance, please <a href="http://www.s2member.com/quick-s.php" target="_blank" rel="external">search s2Member.com</a> for answers to common questions.</p>'."\n";
187
+ echo '<p><strong>s2 Core Processors:</strong> &nbsp;&nbsp; It is normal to have a <code>gateway-core-ipn.log</code> and/or a <code>gateway-core-rtn.log</code> file at all times. Ultimately, all Payment Gateway integrations supported by s2Member pass through it\'s core post-processing handlers. If you\'re having trouble, and you don\'t find any errors in your Payment Gateway log files, please check the <code>gateway-core-ipn.log</code> and <code>gateway-core-rtn.log</code> files too. Regarding s2Member Pro-Forms... If you\'ve integrated s2Member Pro-Forms, you will not have a <code>gateway-core-rtn.log</code> file, because that particular processor is not used with Pro-Form integrations. However, you will have a <code>gateway-core-ipn.log</code> file, and you will need to make a point of inspecting this file to ensure there were no post-processing issues.</p>'."\n";
188
+ echo '<p><strong>s2 HTTP API Logs:</strong> &nbsp;&nbsp; If s2Member is not behaving as expected, and you cannot find errors anywhere in your Payment Gateway log files (or with any core processors), please review your <code>s2-http-api-debug.log</code> file too. Look for any HTTP connections where s2Member is getting <code>403</code>, <code>404</code>, <code>503</code> errors from your server. This can sometimes happen due to <a href="http://www.s2member.com/kb/mod-security-random-503-403-errors/" target="_blank" rel="external">paranoid Mod Security configurations</a>, and it may require you to contact your hosting company for assistance.</p>'."\n";
189
+ echo '<p style="font-style:italic;"><strong>Archived Log Files:</strong> &nbsp;&nbsp; All s2Member log files are stored here: <code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["logs_dir"])).'</code>. Any log files that contain the word <code>ARCHIVED</code> in their name, are files that reached a size of more than 2MB; so s2Member archived them automatically to prevent any single log file from becoming too large. Archived log file names will also contain the date/time they were archived by s2Member. These archived log files typically contain much older (and possibly outdated) log entries.</p>'."\n";
190
 
191
  echo '<div class="ws-menu-page-hr"></div>'."\n";
192
 
193
+ echo '<h3>s2Member Log File Descriptions (for <em>all</em> possible log file names)</h3>'."\n";
194
 
195
  echo '<div class="ws-menu-page-hr"></div>'."\n";
196
 
229
  unset($_k, $_v); // Housekeeping.
230
 
231
  if(preg_match("/\.log$/", $_log_file) && stripos($_log_file, "-ARCHIVED-") === FALSE)
232
+ $log_file_options .= '<option data-type="current" title="'.esc_attr($_log_file_description["long"]).'" value="'.esc_attr($_log_file).'"'.(($view_log_file === $_log_file) ? ' style="font-weight:bold;" selected="selected"' : '').'>'.esc_html($_log_file).'—'.esc_html($_log_file_description["short"]).'</option>';
233
  }
234
  unset($_log_file_description, $_log_file); // Housekeeping.
235
  $log_file_options .= '</optgroup>';
254
  '<option value="" disabled="disabled"></option>'.
255
  $log_file_options;
256
 
257
+ echo '<form method="post" name="ws_plugin__s2member_log_viewer" id="ws-plugin--s2member-log-viewer" autocomplete="off">'."\n";
258
 
259
  echo '<table class="form-table">'."\n";
260
  echo '<tbody>'."\n";
includes/menu-pages/menu-pages-s-min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function($){var esc_attr=esc_html=function(string){if(/[&\<\>"']/.test(string=String(string))){string=string.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),string=string.replace(/"/g,"&quot;").replace(/'/g,"&#039;")}return string};ws_plugin__s2member_generateSecurityKey=function(obj){if(!obj){obj="#ws-plugin--s2member-sec-encryption-key"}var mt_rand=function(min,max){min=(arguments.length<1)?0:min;max=(arguments.length<2)?2147483647:max;return Math.floor(Math.random()*(max-min+1))+min};var chars="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#%^&*()";for(var i=0,key="";i<64;i++){key+=chars.substr(mt_rand(0,chars.length-1),1)}$(obj).val(key);return false};if(location.href.match(/page\=ws-plugin--s2member/)){$("input.ws-plugin--s2member-update-roles-button, input.ws-plugin--s2member-reset-roles-button").click(function(){var $this=$(this);$this.val("one moment please ...");var levels='<?php echo (int)$GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; ?>';var resetUpdate=($this.hasClass("ws-plugin--s2member-reset-roles-button"))?"Reset":"Update";$.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(response){if(response==="1"){alert("s2Member's Roles/Capabilities "+((resetUpdate.toLowerCase()==="reset")?"have been successfully reset":"updated successfully")+".\nYour installation of s2Member has Membership Levels 0-"+levels+"."),$this.val(resetUpdate+" Roles/Capabilities")}else{if(response==="l"){alert("Sorry, your request failed.\ns2Member's Roles/Capabilities are locked by Filter:\nws_plugin__s2member_lock_roles_caps"),$this.val(resetUpdate+" Roles/Capabilities")}else{alert("Sorry, your request failed.\nAccess denied. Do you have the ability to `create_users`?"),$this.val(resetUpdate+" Roles/Capabilities")}}});return false})}if(location.href.match(/page\=ws-plugin--s2member-logs/)){$("input#ws-plugin--s2member-gateway-debug-logs-extensive-1").click(function(){var $this=$(this),thisChecked=(this.checked)?true:false;if(thisChecked){$("input#ws-plugin--s2member-gateway-debug-logs-1").attr("checked","checked")}});var $toggles=$("a.ws-plugin--s2member-log-file-viewport-toggle");$toggles.click(function(){$("textarea#ws-plugin--s2member-log-file-viewer").each(function(){var $viewer=$(this);if($viewer.attr("data-state")!=="expanded"){$viewer.css({height:($viewer.prop("scrollHeight")+50)+"px","overflow-y":"auto"});$toggles.html("&#8657; normalize viewport &#10073;");$viewer.attr("data-state","expanded")}else{$viewer.css({height:"auto","overflow-y":"scroll"});$toggles.html("&#8659; expand viewport &#8659;");$viewer.attr("data-state","scrolling")}});return false})}if(location.href.match(/page\=ws-plugin--s2member-mms-ops/)){$("select#ws-plugin--s2member-mms-registration-file").change(function(){if($(this).val()==="wp-signup"){var gv=$("select#ws-plugin--s2member-mms-registration-grants").val(),l0v=$("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")[((gv==="all")?"show":"hide")]();$("input#ws-plugin--s2member-mms-registration-blogs-level0").val(((gv==="all")?((l0v>0)?l0v:"1"):"0"))}else{if($(this).val()==="wp-login"){var gv=$("select#ws-plugin--s2member-mms-registration-grants").val(),l0v=$("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")})}if(location.href.match(/page\=ws-plugin--s2member-gen-ops/)){ws_plugin__s2member_enableSecurityKey=function(){if(confirm("Edit Key? Are you sure?\nThis could break your installation!\n\n*Note* If you've been testing s2Member, feel free to change this Key before you go live. Just don't go live, and then change it. You'll have unhappy Customers. Data corruption WILL occur! For your safety, s2Member keeps a history of the last 10 Keys that you've used. If you get yourself into a real situation, s2Member will let you revert back to a previous Key.")){$("input#ws-plugin--s2member-sec-encryption-key").removeAttr("disabled")}return false};ws_plugin__s2member_securityKeyHistory=function(){$("div#ws-plugin--s2member-sec-encryption-key-history").toggle();return false};$("select#ws-plugin--s2member-new-user-emails-enabled").change(function(){var $pluggable=$("input#ws-plugin--s2member-pluggables-wp-new-user-notification"),$this=$(this),$newUserEmails=$("div#ws-plugin--s2member-new-user-emails");if($pluggable.val()==="0"||$this.val()==="0"){($pluggable.val()==="0")?$this.attr("disabled","disabled"):$this.removeAttr("disabled");$(":input",$newUserEmails).attr("disabled","disabled"),$newUserEmails.css("opacity","0.5")}else{$this.removeAttr("disabled"),$(":input",$newUserEmails).removeAttr("disabled"),$newUserEmails.css("opacity","")}}).trigger("change");$("select#ws-plugin--s2member-login-reg-design-enabled").change(function(){var $this=$(this),$loginRegDesign=$("div#ws-plugin--s2member-login-reg-design");if($this.val()==="0"){$(":input",$loginRegDesign).attr("disabled","disabled"),$loginRegDesign.css("opacity","0.5"),$loginRegDesign.hide()}else{$(":input",$loginRegDesign).removeAttr("disabled"),$loginRegDesign.css("opacity",""),$loginRegDesign.show()}}).trigger("change");if($("input#ws-plugin--s2member-custom-reg-fields").length&&$("div#ws-plugin--s2member-custom-reg-field-configuration").length){(function(){var i,fieldDefaults,tools,table,$tools,$table;var $fields=$("input#ws-plugin--s2member-custom-reg-fields");var $configuration=$("div#ws-plugin--s2member-custom-reg-field-configuration");var fields=($fields.val())?$.JSON.parse($fields.val()):[];fields=(fields instanceof Array)?fields:[];fieldDefaults={section:"no",sectitle:"",id:"",label:"",type:"text",deflt:"",options:"",expected:"",required:"yes",levels:"all",editable:"yes",classes:"",styles:"",attrs:""};for(i=0;i<fields.length;i++){fields[i]=$.extend(true,{},fieldDefaults,fields[i])}tools='<div id="ws-plugin--s2member-custom-reg-field-configuration-tools"></div>',table='<table id="ws-plugin--s2member-custom-reg-field-configuration-table"></table>';$configuration.html(tools+table);$tools=$("div#ws-plugin--s2member-custom-reg-field-configuration-tools"),$table=$("table#ws-plugin--s2member-custom-reg-field-configuration-table");ws_plugin__s2member_customRegFieldSectionChange=function(select){var section=$(select).val();var sectitle_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle";(section==="yes")?$(sectitle_trs).css("display",""):$(sectitle_trs).css("display","none")};ws_plugin__s2member_customRegFieldTypeChange=function(select){var type=$(select).val();var deflt_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt",options_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-options",expected_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected";(type.match(/^(text|textarea)$/))?$(deflt_trs).css("display",""):$(deflt_trs).css("display","none");(type.match(/^(select|selects|checkboxes|radios)$/))?$(options_trs).css("display",""):$(options_trs).css("display","none");(type.match(/^(text|textarea)$/))?$(expected_trs).css("display",""):$(expected_trs).css("display","none")};ws_plugin__s2member_customRegFieldDelete=function(index){var newFields=new Array();for(var i=0;i<fields.length;i++){if(i!==index){newFields.push(fields[i])}}fields=newFields,updateFields(),buildTable()};ws_plugin__s2member_customRegFieldMoveUp=function(index){if(typeof fields[index]==="object"&&typeof fields[index-1]==="object"){var prevFieldObj=fields[index-1],thisFieldObj=fields[index];fields[index-1]=thisFieldObj,fields[index]=prevFieldObj;updateFields(),buildTable()}};ws_plugin__s2member_customRegFieldMoveDown=function(index){if(typeof fields[index]==="object"&&typeof fields[index+1]==="object"){var nextFieldObj=fields[index+1],thisFieldObj=fields[index];fields[index+1]=thisFieldObj,fields[index]=nextFieldObj;updateFields(),buildTable()}};ws_plugin__s2member_customRegFieldCreate=function(){var $table=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),field={};$(":input[property]",$table).each(function(){var $this=$(this),property=$this.attr("property"),val=$.trim($this.val());field[property]=val});if((field=validateField(field))){fields.push(field),updateFields(),buildTools(),buildTable(),scrollReset();setTimeout(function(){var row="tr.ws-plugin--s2member-custom-reg-field-configuration-table-row-"+(fields.length-1);alert('Field created successfully.\n* Remember to "Save All Changes".')},500)}};ws_plugin__s2member_customRegFieldUpdate=function(index){var $table=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),field={};$(":input[property]",$table).each(function(){var $this=$(this),property=$this.attr("property"),val=$.trim($this.val());field[property]=val});if((field=validateField(field,index))){fields[index]=field,updateFields(),buildTools(),buildTable(),scrollReset();setTimeout(function(){var row="tr.ws-plugin--s2member-custom-reg-field-configuration-table-row-"+index;alert('Field updated successfully.\n* Remember to "Save All Changes".')},500)}};ws_plugin__s2member_customRegFieldAdd=function(){buildTools(true)};ws_plugin__s2member_customRegFieldEdit=function(index){buildTools(false,index),scrollReset()};ws_plugin__s2member_customRegFieldCancel=function(){buildTools(),scrollReset()};var validateField=function(field,index){var editing=(typeof index==="number"&&typeof fields[index]==="object")?true:false,errors=[],options,i;if(typeof field!=="object"||typeof(field=$.extend(true,{},fieldDefaults,field))!=="object"){alert("Invalid field object. Please try again.");return false}field.sectitle=(field.section==="yes")?field.sectitle:"";field.deflt=(field.type.match(/^(text|textarea)$/))?field.deflt:"";field.deflt=(field.type.match(/^(text)$/))?field.deflt.replace(/[\r\n\t ]+/g," "):field.deflt;field.options=(field.type.match(/^(select|selects|checkboxes|radios)$/))?field.options:"";field.expected=(field.type.match(/^(text|textarea)$/))?field.expected:"";if(!field.id){errors.push("Unique Field ID:\nThis is required. Please try again.")}else{if(fieldIdExists(field.id)&&(!editing||field.id!==fields[index].id)){errors.push("Unique Field ID:\nThat Field ID already exists. Please try again.")}}if(!field.label){errors.push("Field Label/Description:\nThis is required. Please try again.")}if(field.type.match(/^(select|selects|checkboxes|radios)$/)&&!field.options){errors.push("Option Configuration File:\nThis is required. Please try again.")}else{if(field.type.match(/^(select|selects|checkboxes|radios)$/)){for(i=0;i<(options=field.options.split(/[\r\n]+/)).length;i++){if(!(options[i]=$.trim(options[i])).match(/^([^\|]*)(\|)([^\|]*)(\|default)?$/)){errors.push("Option Configuration File:\nInvalid configuration at line #"+(i+1)+".");break}}field.options=$.trim(options.join("\n"))}}if(!(field.levels=field.levels.replace(/ /g,""))){errors.push("Applicable Levels:\nThis is required. Please try again.")}else{if(!field.levels.match(/^(all|[0-9,]+)$/)){errors.push("Applicable Levels:\nShould be comma-delimited Levels, or just type: all.\n(examples: 0,1,2,3,4 or type the word: all)")}}if(field.classes&&field.classes.match(/[^a-z 0-9 _ \-]/i)){errors.push("CSS Classes:\nContains invalid characters. Please try again.\n(only: alphanumerics, underscores, hyphens, spaces)")}if(field.styles&&field.styles.match(/["\=\>\<]/)){errors.push('CSS Styles:\nContains invalid characters. Please try again.\n(do NOT use these characters: = " < >)')}if(field.attrs&&field.attrs.match(/[\>\<]/)){errors.push("Other Attributes:\nContains invalid characters. Please try again.\n(do NOT use these characters: < >)")}if(errors.length>0){alert(errors.join("\n\n"));return false}else{return field}};var updateFields=function(){$fields.val(((fields.length>0)?$.JSON.stringify(fields):""))};var fieldId2Var=function(fieldId){return(typeof fieldId==="string")?$.trim(fieldId).toLowerCase().replace(/[^a-z0-9]/g,"_"):""};var fieldTypeDesc=function(type){var types={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)"};if(typeof types[type]==="string"){return types[type]}return""};var fieldIdExists=function(fieldId){for(var i=0;i<fields.length;i++){if(fields[i].id===fieldId){return true}}};var scrollReset=function(){scrollTo(0,$("div.ws-plugin--s2member-custom-reg-fields-section").offset()["top"]-100)};var buildTools=function(adding,index){var i=0,html="",form="",w=0,h=0,editing=(typeof index==="number"&&typeof fields[index]==="object")?true:false,displayForm=(adding||editing)?true:false,field=(editing)?$.extend(true,{},fieldDefaults,fields[index]):fieldDefaults;html+='<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();if(displayForm){form+='<div id="ws-plugin--s2member-custom-reg-field-configuration-thickbox-tools-form">';form+='<table id="ws-plugin--s2member-custom-reg-field-configuration-tools-form">';form+="<tbody>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">Starts A New Section?</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">';form+='<td colspan="2">';form+='<select property="section" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section" onchange="ws_plugin__s2member_customRegFieldSectionChange(this);">';form+='<option value="no"'+((field.section==="no")?' selected="selected"':"")+'">No (this Field flows normally)</option>';form+='<option value="yes"'+((field.section==="yes")?' selected="selected"':"")+'">Yes (this Field begins a new section)</option>';form+="</select><br />";form+="<small>Optional. Allows Fields to be grouped into sections.</small>";form+="</td>";form+="</tr>";form+='<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"'+((field.section==="yes")?"":' style="display:none;"')+'><td colspan="2"><hr /></td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle ws-plugin--s2member-custom-reg-field-configuration-tools-form-section"'+((field.section==="yes")?"":' style="display:none;"')+">";form+='<td colspan="2">';form+="Title for this new section? (optional)<br />";form+='<input type="text" property="sectitle" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle" value="'+esc_attr(field.sectitle)+'" /><br />';form+="<small>If empty, a simple divider will be used by default.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-type"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">Form Field Type: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">';form+='<td colspan="2">';form+='<select property="type" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type" onchange="ws_plugin__s2member_customRegFieldTypeChange(this);">';form+='<option value="text"'+((field.type==="text")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("text"))+"</option>";form+='<option value="textarea"'+((field.type==="textarea")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("textarea"))+"</option>";form+='<option value="select"'+((field.type==="select")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("select"))+"</option>";form+='<option value="selects"'+((field.type==="selects")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("selects"))+"</option>";form+='<option value="checkbox"'+((field.type==="checkbox")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("checkbox"))+"</option>";form+='<option value="pre_checkbox"'+((field.type==="pre_checkbox")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("pre_checkbox"))+"</option>";form+='<option value="checkboxes"'+((field.type==="checkboxes")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("checkboxes"))+"</option>";form+='<option value="radios"'+((field.type==="radios")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("radios"))+"</option>";form+="</select><br />";form+="<small>The options below may change, based on the Field Type you choose here.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-label"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">Field Label/Desc: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">';form+='<td colspan="2">';form+='<input type="text" property="label" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label" value="'+esc_attr(field.label)+'" /><br />';form+="<small>Examples: <code>Choose Country</code>, <code>Street Address</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-id"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">Unique Field ID: *</label></label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">';form+='<td colspan="2">';form+='<input type="text" property="id" maxlength="25" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id" value="'+esc_attr(field.id)+'" /><br />';form+="<small>Examples: <code>country_code</code>, <code>street_address</code></small><br />";form+='<small>e.g. <code>[s2Get user_field="country_code" /]</code></small>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-required"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">Field Required: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">';form+='<td colspan="2">';form+='<select property="required" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">';form+='<option value="yes"'+((field.required==="yes")?' selected="selected"':"")+'">Yes (required)</option>';form+='<option value="no"'+((field.required==="no")?' selected="selected"':"")+'">No (optional)</option>';form+="</select><br />";form+='<small>If <code>yes</code>, only Users/Members will be "required" to enter this field.</small><br />';form+="<small>* Administrators are exempt from this requirement.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt">Default Text Value: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<textarea property="deflt" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt" rows="1">'+esc_html(field.deflt)+"</textarea><br />";form+="<small>Default value before user input is received.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+((field.type.match(/^(select|selects|checkboxes|radios)$/))?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+((field.type.match(/^(select|selects|checkboxes|radios)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options">Option Configuration File: * (one option per line)</label><br />';form+="<small>Use a pipe <code>|</code> delimited format: <code>option value|option label</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+((field.type.match(/^(select|selects|checkboxes|radios)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<textarea property="options" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options" rows="3">'+esc_html(field.options)+"</textarea><br />";form+="Here is a quick example:<br />";form+="<small>You can also specify a <em>default</em> option:</small><br />";form+="<code>US|United States|default</code><br />";form+="<code>CA|Canada</code><br />";form+="<code>VI|Virgin Islands (U.S.)</code>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">Expected Format: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<select property="expected" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">';form+='<option value=""'+((field.expected==="")?' selected="selected"':"")+'">Anything Goes</option>';form+='<option disabled="disabled"></option>';form+='<optgroup label="Specific Input Types">';form+='<option value="numeric-wp-commas"'+((field.expected==="numeric-wp-commas")?' selected="selected"':"")+'">Numeric (with or without decimals, commas allowed)</option>';form+='<option value="numeric"'+((field.expected==="numeric")?' selected="selected"':"")+'">Numeric (with or without decimals, no commas)</option>';form+='<option value="integer"'+((field.expected==="integer")?' selected="selected"':"")+'">Integer (whole number, without any decimals)</option>';form+='<option value="integer-gt-0"'+((field.expected==="integer-gt-0")?' selected="selected"':"")+'">Integer > 0 (whole number, no decimals, greater than 0)</option>';form+='<option value="float"'+((field.expected==="float")?' selected="selected"':"")+'">Float (floating point number, decimals required)</option>';form+='<option value="float-gt-0"'+((field.expected==="float-gt-0")?' selected="selected"':"")+'">Float > 0 (floating point number, decimals required, greater than 0)</option>';form+='<option value="date"'+((field.expected==="date")?' selected="selected"':"")+'">Date (required date format: dd/mm/yyyy)</option>';form+='<option value="email"'+((field.expected==="email")?' selected="selected"':"")+'">Email (require valid email)</option>';form+='<option value="url"'+((field.expected==="url")?' selected="selected"':"")+'">Full URL (starting with http or https)</option>';form+='<option value="domain"'+((field.expected==="domain")?' selected="selected"':"")+'">Domain Name (domain name only, without http)</option>';form+='<option value="phone"'+((field.expected==="phone")?' selected="selected"':"")+'">Phone # (10 digits w/possible hyphens,spaces,brackets)</option>';form+='<option value="uszip"'+((field.expected==="uszip")?' selected="selected"':"")+'">US Zipcode (5-9 digits w/possible hyphen)</option>';form+='<option value="cazip"'+((field.expected==="cazip")?' selected="selected"':"")+'">Canadian Zipcode (6 alpha-numerics w/possible space)</option>';form+='<option value="uczip"'+((field.expected==="uczip")?' selected="selected"':"")+'">US/Canadian Zipcode (either a US or Canadian zipcode)</option>';form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Any Character Combination">';for(i=1;i<=25;i++){form+='<option value="any-'+i+'"'+((field.expected==="any-"+i)?' selected="selected"':"")+'">Any Character Combination ('+i+" character minimum)</option>";form+='<option value="any-'+i+'-e"'+((field.expected==="any-"+i+"-e")?' selected="selected"':"")+'">Any Character Combination (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics, Spaces &amp; Punctuation Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-spaces-punctuation-'+i+'"'+((field.expected==="alphanumerics-spaces-punctuation-"+i)?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation ('+i+" character minimum)</option>";form+='<option value="alphanumerics-spaces-punctuation-'+i+'-e"'+((field.expected==="alphanumerics-spaces-punctuation-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics &amp; Spaces Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-spaces-'+i+'"'+((field.expected==="alphanumerics-spaces-"+i)?' selected="selected"':"")+'">Alphanumerics &amp; Spaces ('+i+" character minimum)</option>";form+='<option value="alphanumerics-spaces-'+i+'-e"'+((field.expected==="alphanumerics-spaces-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics &amp; Spaces (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics &amp; Punctuation Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-punctuation-'+i+'"'+((field.expected==="alphanumerics-punctuation-"+i)?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation ('+i+" character minimum)</option>";form+='<option value="alphanumerics-punctuation-'+i+'-e"'+((field.expected==="alphanumerics-punctuation-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-'+i+'"'+((field.expected==="alphanumerics-"+i)?' selected="selected"':"")+'">Alphanumerics ('+i+" character minimum)</option>";form+='<option value="alphanumerics-'+i+'-e"'+((field.expected==="alphanumerics-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphabetics Only">';for(i=1;i<=25;i++){form+='<option value="alphabetics-'+i+'"'+((field.expected==="alphabetics-"+i)?' selected="selected"':"")+'">Alphabetics ('+i+" character minimum)</option>";form+='<option value="alphabetics-'+i+'-e"'+((field.expected==="alphabetics-"+i+"-e")?' selected="selected"':"")+'">Alphabetics (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Numeric Digits Only">';for(i=1;i<=25;i++){form+='<option value="numerics-'+i+'"'+((field.expected==="numerics-"+i)?' selected="selected"':"")+'">Numeric Digits ('+i+" digit minimum)</option>";form+='<option value="numerics-'+i+'-e"'+((field.expected==="numerics-"+i+"-e")?' selected="selected"':"")+'">Numeric Digits (exactly '+i+" digit"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+="</select><br />";form+="<small>Only Users/Members will be required to meet this criteria.</small><br />";form+="<small>* Administrators are exempt from this.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">Applicable Membership Levels: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">';form+='<td colspan="2">';form+='<input type="text" property="levels" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels" value="'+esc_attr(field.levels)+'" /><br />';form+="<small>Please use comma-delimited Level #'s: <code>0,1,2,3,4</code> or type: <code>all</code>.</small><br />";form+="<small>This allows you to enable this field - only at specific Membership Levels.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">Allow Profile Edits: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">';form+='<td colspan="2">';form+='<select property="editable" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">';form+='<option value="yes"'+((field.editable==="yes")?' selected="selected"':"")+'">Yes (editable)</option>';form+='<option value="no"'+((field.editable==="no")?' selected="selected"':"")+'">No (uneditable after registration)</option>';form+='<option value="no-invisible"'+((field.editable==="no-invisible")?' selected="selected"':"")+'">No (uneditable &amp; totally invisible after registration)</option>';form+='<option value="no-always-invisible"'+((field.editable==="no-always-invisible")?' selected="selected"':"")+'">No (uneditable &amp; totally invisible, both during &amp; after registration)</option>';form+='<option value="yes-invisible"'+((field.editable==="yes-invisible")?' selected="selected"':"")+'">Yes (editable after registration / invisible during registration)</option>';form+="</select><br />";form+="<small>If <code>No</code>, this field will be un-editable after registration.</small><br />";form+="<small>* Administrators are exempt from this.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">CSS Classes: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">';form+='<td colspan="2">';form+='<input type="text" property="classes" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes" value="'+esc_attr(field.classes)+'" /><br />';form+="<small>Example: <code>my-style-1 my-style-2</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">CSS Styles: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">';form+='<td colspan="2">';form+='<input type="text" property="styles" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles" value="'+esc_attr(field.styles)+'" /><br />';form+="<small>Example: <code>color:#000000; background:#FFFFFF;</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">Other Attributes: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">';form+='<td colspan="2">';form+='<input type="text" property="attrs" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs" value="'+esc_attr(field.attrs)+'" /><br />';form+='<small>Example: <code>onkeyup="" onblur=""</code></small>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-buttons"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-buttons">';form+='<td align="left">';form+='<input type="button" value="Cancel" onclick="ws_plugin__s2member_customRegFieldCancel();" />';form+="</td>";form+='<td align="right">';form+='<input type="button" value="'+((editing)?"Update This Field":"Create Registration Field")+'" onclick="'+((editing)?"ws_plugin__s2member_customRegFieldUpdate("+index+");":"ws_plugin__s2member_customRegFieldCreate();")+'" />';form+="</td>";form+="</tr>";form+="</tbody>";form+="</table>";form+="<div>";$("body").append(form);tb_show(((editing)?"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()}$tools.html(html)};var buildTable=function(){var l=fields.length,i=0,html="",eo="o";html+="<tbody>";html+="<tr>";html+="<th>Order</th>";html+="<th>Field Type</th>";html+="<th>Unique ID</th>";html+="<th>Required</th>";html+="<th>Levels</th>";html+="<th>- Tools -</th>";html+="</tr>";if(fields.length>0){for(i=0;i<fields.length;i++){html+='<tr class="'+esc_attr((eo=(eo==="o")?"e":"o"))+((fields[i].section==="yes")?" s":"")+" ws-plugin--s2member-custom-reg-field-configuration-table-row-"+i+'">';html+='<td nowrap="nowrap"><a class="ws-plugin--s2member-custom-reg-field-configuration-move-up" href="#" onclick="ws_plugin__s2member_customRegFieldMoveUp('+i+'); return false;"></a><a class="ws-plugin--s2member-custom-reg-field-configuration-move-down" href="#" onclick="ws_plugin__s2member_customRegFieldMoveDown('+i+'); return false;"></a></td>';html+='<td nowrap="nowrap">'+esc_html(fieldTypeDesc(fields[i].type))+"</td>";html+='<td nowrap="nowrap">'+esc_html(fields[i].id)+"</td>";html+='<td nowrap="nowrap">'+esc_html(fields[i].required)+"</td>";html+='<td nowrap="nowrap">'+esc_html(fields[i].levels)+"</td>";html+='<td nowrap="nowrap"><a class="ws-plugin--s2member-custom-reg-field-configuration-edit" href="#" onclick="ws_plugin__s2member_customRegFieldEdit('+i+'); return false;"></a><a class="ws-plugin--s2member-custom-reg-field-configuration-delete" href="#" onclick="ws_plugin__s2member_customRegFieldDelete('+i+'); return false;"></a></td>';html+="</tr>"}}else{html+="<tr>";html+='<td colspan="6">No Custom Fields are configured.</td>';html+="</tr>"}html+="</tbody>";$table.html(html)};buildTools(),buildTable()})()}}if(location.href.match(/page\=ws-plugin--s2member-res-ops/)){$("input#ws-plugin--s2member-brute-force-restrictions-reset-button").click(function(){var $this=$(this);$this.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(response){alert("s2Member's Brute Force Restriction Logs have all been reset."),$this.val("Reset Brute Force Logs")});return false});$("input#ws-plugin--s2member-ip-restrictions-reset-button").click(function(){var $this=$(this);$this.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(response){alert("s2Member's IP Restriction Logs have all been reset."),$this.val("Reset IP Restriction Logs")});return false});$('div.ws-plugin--s2member-query-level-access-section input[type="checkbox"][name="ws_plugin__s2member_filter_wp_query[]"]').change(function(){var thisChange=$(this).val();$('div.ws-plugin--s2member-query-level-access-section input[type="checkbox"][name="ws_plugin__s2member_filter_wp_query[]"]').each(function(){var $this=$(this),val=$this.val(),checkboxes='input[type="checkbox"]';if(val==="all"&&this.checked){$this.nextAll(checkboxes).attr({checked:"checked",disabled:"disabled"})}else{if(val==="all"&&!this.checked){$this.nextAll(checkboxes).removeAttr("disabled");(thisChange==="all")?$this.nextAll(checkboxes).removeAttr("checked"):null}}})}).last().trigger("change")}if(location.href.match(/page\=ws-plugin--s2member-down-ops/)){var updateCloudFrontPrivateKey=function(){var $hiddenPrivateKey=$("input#ws-plugin--s2member-amazon-cf-files-private-key");var $visiblePrivateKeyEntry=$("textarea#ws-plugin--s2member-amazon-cf-files-private-key-entry");var hiddenPrivateKeyValue=$.trim($hiddenPrivateKey.val()),visiblePrivateKeyEntryValue=$.trim($visiblePrivateKeyEntry.val());if((hiddenPrivateKeyValue&&!visiblePrivateKeyEntryValue)||visiblePrivateKeyEntryValue.match(/[^\r\n\u25CF]/)){$hiddenPrivateKey.val(visiblePrivateKeyEntryValue),$visiblePrivateKeyEntry.val(visiblePrivateKeyEntryValue.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 $hiddenPrivateKey=$("input#ws-plugin--s2member-amazon-cf-files-private-key");var $visiblePrivateKeyId=$("input#ws-plugin--s2member-amazon-cf-files-private-key-id");var $autoConfigDistros=$("input#ws-plugin--s2member-amazon-cf-files-auto-configure-distros");var $autoConfigDistrosStatus=$("input#ws-plugin--s2member-amazon-cf-files-distros-auto-config-status");var autoConfigDistrosStatusValue=$.trim($autoConfigDistrosStatus.val());var hiddenPrivateKeyValue=$.trim($hiddenPrivateKey.val()),visiblePrivateKeyIdValue=$.trim($visiblePrivateKeyId.val());var hiddenPrivateKeyPrevConfigValue=$.trim($hiddenPrivateKey.attr("data-s-prev-config-value")),visiblePrivateKeyIdPrevConfigValue=$.trim($visiblePrivateKeyId.attr("data-s-prev-config-value"));if(autoConfigDistrosStatusValue==="configured"&&((visiblePrivateKeyIdPrevConfigValue&&visiblePrivateKeyIdValue!==visiblePrivateKeyIdPrevConfigValue)||(hiddenPrivateKeyPrevConfigValue&&hiddenPrivateKeyValue!==hiddenPrivateKeyPrevConfigValue))){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.");$autoConfigDistros.attr("checked","checked")}else{if(autoConfigDistrosStatusValue!=="configured"&&visiblePrivateKeyIdValue&&hiddenPrivateKeyValue){alert("s2Member will need to auto-configure your Amazon CloudFront distributions for you. When you're done editing, click (Save All Changes) below.");$autoConfigDistros.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 $this=$(this),thisChecked=(this.checked)?true:false;var $autoConfigDistros=$("input#ws-plugin--s2member-amazon-cf-files-auto-configure-distros");var $autoConfigDistroCnames=$("div#ws-plugin--s2member-amazon-cf-files-auto-configure-distro-cnames");(thisChecked)?$autoConfigDistroCnames.show():$autoConfigDistroCnames.hide();(thisChecked)?$autoConfigDistros.attr("checked","checked"):null}).trigger("change")}if(location.href.match(/page\=ws-plugin--s2member-paypal-ops/)){$("select#ws-plugin--s2member-auto-eot-system-enabled").change(function(){var $this=$(this),val=$this.val();var $viaCron=$("p#ws-plugin--s2member-auto-eot-system-enabled-via-cron");if(val==2){$viaCron.show()}else{$viaCron.hide()}})}if(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 button=this.id.replace(/^ws-plugin--s2member-(.+?)-term$/g,"$1");var trialDisabled=($(this).val().split("-")[2].replace(/[^0-1BN]/g,"")==="BN")?1:0;$("p#ws-plugin--s2member-"+button+"-trial-line").css("display",(trialDisabled?"none":""));$("span#ws-plugin--s2member-"+button+"-trial-then").css("display",(trialDisabled?"none":""));$("span#ws-plugin--s2member-"+button+"-20p-rule").css("display",(trialDisabled?"none":""));(trialDisabled)?$("input#ws-plugin--s2member-"+button+"-trial-period").val(0):null;(trialDisabled)?$("input#ws-plugin--s2member-"+button+"-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 value=this.value.replace(/^(-all|-al|-a|-)[;,]*/gi,""),_all=(this.value.match(/^(-all|-al|-a|-)[;,]*/i))?"-all,":"";if(value.match(/[^a-z_0-9,]/)){this.value=_all+$.trim($.trim(value).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");var code=$("textarea#ws-plugin--s2member-"+button+"-button");var modLevel=$("select#ws-plugin--s2member-modification-level");var level=(button==="modification")?modLevel.val().split(":",2)[1]:button.replace(/^level/,"");var label=labels["level"+level].replace(/"/g,"");var desc=$.trim($("input#ws-plugin--s2member-"+button+"-desc").val().replace(/"/g,""));var trialAmount=$("input#ws-plugin--s2member-"+button+"-trial-amount").val().replace(/[^0-9\.]/g,"");var trialPeriod=$("input#ws-plugin--s2member-"+button+"-trial-period").val().replace(/[^0-9]/g,"");var trialTerm=$("select#ws-plugin--s2member-"+button+"-trial-term").val().replace(/[^A-Z]/g,"");var regAmount=$("input#ws-plugin--s2member-"+button+"-amount").val().replace(/[^0-9\.]/g,"");var regPeriod=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[0].replace(/[^0-9]/g,"");var regTerm=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[1].replace(/[^A-Z]/g,"");var regRecur=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[2].replace(/[^0-1BN]/g,"");var regRecurTimes="",regRecurRetry="1";var localeCode="",digital="0",noShipping="1";var pageStyle=$.trim($("input#ws-plugin--s2member-"+button+"-page-style").val().replace(/"/g,""));var currencyCode=$("select#ws-plugin--s2member-"+button+"-currency").val().replace(/[^A-Z]/g,"");var 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=(regRecur==="BN")?"0":trialPeriod;trialAmount=(!trialAmount||isNaN(trialAmount)||trialAmount<0.01||trialPeriod<=0)?"0":trialAmount;var levelCcapsPer=(regRecur==="BN"&&regTerm!=="L")?level+":"+cCaps+":"+regPeriod+" "+regTerm:level+":"+cCaps;levelCcapsPer=levelCcapsPer.replace(/\:+$/g,"");if(trialAmount!=="0"&&(isNaN(trialAmount)||trialAmount<0)){alert("— Oops, a slight problem: —\n\nWhen provided, Trial Amount must be >= 0.00");return false}else{if(trialAmount!=="0"&&trialAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Trial Amount is: 10000.00");return false}else{if(trialTerm==="D"&&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.");return false}else{if(trialTerm==="W"&&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.");return false}else{if(trialTerm==="M"&&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.");return false}else{if(trialTerm==="Y"&&trialPeriod>5){alert("— Oops, a slight problem: —\n\nMax Trial Period Years is: 5.");return false}else{if(!regAmount||isNaN(regAmount)||regAmount<0.01){alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01");return false}else{if(regAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00");return false}else{if(!desc){alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button.");return false}}}}}}}}}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;(regRecur==="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="cmd" value\=")(.*?)(" \/\>)/g," $1_xclick$3")):null;(regRecur==="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="(src|srt|sra|a1|p1|t1|a3|p3|t3)" value\="(.*?)" \/\>)/g," <!--$1-->")):null;(regRecur!=="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="cmd" value\=")(.*?)(" \/\>)/g," $1_xclick-subscriptions$3")):null;(regRecur!=="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="amount" value\="(.*?)" \/\>)/g," <!--$1-->")):null;shortCodeTemplateAttrs+=(button==="modification")?'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="'+((button==="modification")?"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 ($_SERVER["REMOTE_ADDR"])); ?>').replace(/\<\?php echo S2MEMBER_CURRENT_USER_VALUE_FOR_PP_(ON0|OS0|ON1|OS1); \?\>/g,""));(button==="modification")?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()});return false};ws_plugin__s2member_paypalCcapButtonGenerate=function(){var shortCodeTemplate='[s2Member-PayPal-Button %%attrs%% image="default" output="button" /]',shortCodeTemplateAttrs="";var shortCode=$("input#ws-plugin--s2member-ccap-shortcode");var code=$("textarea#ws-plugin--s2member-ccap-button");var desc=$.trim($("input#ws-plugin--s2member-ccap-desc").val().replace(/"/g,""));var regAmount=$("input#ws-plugin--s2member-ccap-amount").val().replace(/[^0-9\.]/g,"");var regPeriod=$("select#ws-plugin--s2member-ccap-term").val().split("-")[0].replace(/[^0-9]/g,"");var regTerm=$("select#ws-plugin--s2member-ccap-term").val().split("-")[1].replace(/[^A-Z]/g,"");var regRecur=$("select#ws-plugin--s2member-ccap-term").val().split("-")[2].replace(/[^0-1BN]/g,"");var localeCode="",digital="0",noShipping="1";var pageStyle=$.trim($("input#ws-plugin--s2member-ccap-page-style").val().replace(/"/g,""));var currencyCode=$("select#ws-plugin--s2member-ccap-currency").val().replace(/[^A-Z]/g,"");var cCaps=$.trim($.trim($("input#ws-plugin--s2member-ccap-ccaps").val()).replace(/^(-all|-al|-a|-)[;,]*/gi,"").replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase());cCaps=($.trim($("input#ws-plugin--s2member-ccap-ccaps").val()).match(/^(-all|-al|-a|-)[;,]*/i))?((cCaps)?"-all,":"-all")+cCaps.toLowerCase():cCaps.toLowerCase();var levelCcapsPer=(regRecur==="BN"&&regTerm!=="L")?"*:"+cCaps+":"+regPeriod+" "+regTerm:"*:"+cCaps;levelCcapsPer=levelCcapsPer.replace(/\:+$/g,"");if(!cCaps||cCaps==="-all"){alert("— Oops, a slight problem: —\n\nPlease provide at least one Custom Capability.");return false}else{if(!regAmount||isNaN(regAmount)||regAmount<0.01){alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01");return false}else{if(regAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00");return false}else{if(!desc){alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button.");return false}}}}shortCodeTemplateAttrs+='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)+'"';shortCodeTemplateAttrs+=' custom="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>" ra="'+esc_attr(regAmount)+'" rp="'+esc_attr(regPeriod)+'" rt="'+esc_attr(regTerm)+'" rr="'+esc_attr(regRecur)+'"';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\="amount" value\="(.*?)"/,' name="amount" value="'+esc_attr(regAmount)+'"'));$("div#ws-plugin--s2member-ccap-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 ($_SERVER["REMOTE_ADDR"])); ?>').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.");shortCode.each(function(){this.focus(),this.select()});return false};ws_plugin__s2member_paypalSpButtonGenerate=function(){var shortCodeTemplate='[s2Member-PayPal-Button %%attrs%% image="default" output="button" /]',shortCodeTemplateAttrs="";var shortCode=$("input#ws-plugin--s2member-sp-shortcode");var code=$("textarea#ws-plugin--s2member-sp-button");var leading=$("select#ws-plugin--s2member-sp-leading-id").val().replace(/[^0-9]/g,"");var additionals=$("select#ws-plugin--s2member-sp-additional-ids").val()||[];var hours=$("select#ws-plugin--s2member-sp-hours").val().replace(/[^0-9]/g,"");var regAmount=$("input#ws-plugin--s2member-sp-amount").val().replace(/[^0-9\.]/g,"");var desc=$.trim($("input#ws-plugin--s2member-sp-desc").val().replace(/"/g,""));var localeCode="",digital="0",noShipping="1";var pageStyle=$.trim($("input#ws-plugin--s2member-sp-page-style").val().replace(/"/g,""));var currencyCode=$("select#ws-plugin--s2member-sp-currency").val().replace(/[^A-Z]/g,"");if(!leading){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.");return false}else{if(!regAmount||isNaN(regAmount)||regAmount<0.01){alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01");return false}else{if(regAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00");return false}else{if(!desc){alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button.");return false}}}}for(var i=0,ids=leading;i<additionals.length;i++){if(additionals[i]&&additionals[i]!==leading){ids+=","+additionals[i]}}var spIdsHours="sp:"+ids+":"+hours;shortCodeTemplateAttrs+='sp="1" ids="'+esc_attr(ids)+'" exp="'+esc_attr(hours)+'" desc="'+esc_attr(desc)+'" ps="'+esc_attr(pageStyle)+'" lc="'+esc_attr(localeCode)+'" cc="'+esc_attr(currencyCode)+'" dg="'+esc_attr(digital)+'" ns="'+esc_attr(noShipping)+'"';shortCodeTemplateAttrs+=' custom="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>" ra="'+esc_attr(regAmount)+'"';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(spIdsHours)+'"'));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\="amount" value\="(.*?)"/,' name="amount" value="'+esc_attr(regAmount)+'"'));$("div#ws-plugin--s2member-sp-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 ($_SERVER["REMOTE_ADDR"])); ?>').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.");shortCode.each(function(){this.focus(),this.select()});return false};ws_plugin__s2member_paypalRegLinkGenerate=function(){var level=$("select#ws-plugin--s2member-reg-link-level").val().replace(/[^0-9]/g,"");var subscrID=$.trim($("input#ws-plugin--s2member-reg-link-subscr-id").val());var custom=$.trim($("input#ws-plugin--s2member-reg-link-custom").val());var cCaps=$.trim($.trim($("input#ws-plugin--s2member-reg-link-ccaps").val()).replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase());var fixedTerm=$.trim($("input#ws-plugin--s2member-reg-link-fixed-term").val().replace(/[^A-Z 0-9]/gi,"").toUpperCase());var $link=$("p#ws-plugin--s2member-reg-link"),$loading=$("img#ws-plugin--s2member-reg-link-loading");var levelCcapsPer=(fixedTerm&&!fixedTerm.match(/L$/))?level+":"+cCaps+":"+fixedTerm:level+":"+cCaps;levelCcapsPer=levelCcapsPer.replace(/\:+$/g,"");if(!subscrID){alert("— Oops, a slight problem: —\n\nPaid Subscr. ID is a required value.");return false}else{if(!custom||custom.indexOf('<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq ($_SERVER["HTTP_HOST"]); ?>')!==0){alert("— Oops, a slight problem: —\n\nThe Custom Value MUST start with your domain name.");return false}else{if(fixedTerm&&!fixedTerm.match(/^[1-9]+ (D|W|M|Y|L)$/)){alert("— Oops, a slight problem: —\n\nThe Fixed Term Length is not formatted properly.");return false}}}$link.hide(),$loading.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:subscrID,s2member_reg_access_link_custom:custom,s2member_reg_access_link_item_number:levelCcapsPer},function(response){$link.show().html('<a href="'+esc_attr(response)+'" target="_blank" rel="external">'+esc_html(response)+"</a>"),$loading.hide()});return false};ws_plugin__s2member_paypalSpLinkGenerate=function(){var leading=$("select#ws-plugin--s2member-sp-link-leading-id").val().replace(/[^0-9]/g,"");var additionals=$("select#ws-plugin--s2member-sp-link-additional-ids").val()||[];var hours=$("select#ws-plugin--s2member-sp-link-hours").val().replace(/[^0-9]/g,"");var $link=$("p#ws-plugin--s2member-sp-link"),$loading=$("img#ws-plugin--s2member-sp-link-loading");if(!leading){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.");return false}for(var i=0,ids=leading;i<additionals.length;i++){if(additionals[i]&&additionals[i]!==leading){ids+=","+additionals[i]}}$link.hide(),$loading.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:ids,s2member_sp_access_link_hours:hours},function(response){$link.show().html('<a href="'+esc_attr(response)+'" target="_blank" rel="external">'+esc_html(response)+"</a>"),$loading.hide()});return false}}if(location.href.match(/page\=ws-plugin--s2member-els-ops/)){$("select#ws-plugin--s2member-custom-reg-opt-in").change(function(){var $this=$(this),val=$this.val();var $rows=$("tr.ws-plugin--s2member-custom-reg-opt-in-label-row");var $prevImg=$("img.ws-plugin--s2member-custom-reg-opt-in-label-prev-img");if(val<=0){$rows.css("display","none"),$prevImg.attr("src",$prevImg.attr("src").replace(/\/checked\.png$/,"/unchecked.png"))}else{if(val==1){$rows.css("display",""),$prevImg.attr("src",$prevImg.attr("src").replace(/\/unchecked\.png$/,"/checked.png"))}else{if(val==2){$rows.css("display",""),$prevImg.attr("src",$prevImg.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 thisChange=$(this).val(),checkedIndexes=[];$('div.ws-plugin--s2member-opt-out-section input[type="checkbox"][name="ws_plugin__s2member_custom_reg_auto_opt_outs[]"]').each(function(){var $this=$(this),val=$this.val(),checkboxes='input[type="checkbox"]';if(val==="removal-deletion"&&this.checked){$this.nextAll(checkboxes).slice(0,2).attr({checked:"checked",disabled:"disabled"})}else{if(val==="removal-deletion"&&!this.checked){$this.nextAll(checkboxes).slice(0,2).removeAttr("disabled");(thisChange==="removal-deletion")?$this.nextAll(checkboxes).slice(0,2).removeAttr("checked"):null}else{if(val==="modification"&&this.checked){$this.nextAll(checkboxes).slice(0,3).attr({checked:"checked",disabled:"disabled"})}else{if(val==="modification"&&!this.checked){(thisChange==="modification")?$this.nextAll(checkboxes).slice(0,3).removeAttr("checked"):null;$this.nextAll(checkboxes).slice(0,3).removeAttr("disabled")}}}}}).each(function(index){(this.checked)?checkedIndexes.push(index):null});$("select#ws-plugin--s2member-custom-reg-auto-opt-out-transitions").removeAttr("disabled");if($.inArray(3,checkedIndexes)===-1&&$.inArray(4,checkedIndexes)===-1&&$.inArray(5,checkedIndexes)===-1&&$.inArray(6,checkedIndexes)===-1){$("select#ws-plugin--s2member-custom-reg-auto-opt-out-transitions").attr("disabled","disabled")}}).last().trigger("change")}});
1
+ jQuery(document).ready(function($){var esc_attr=esc_html=function(string){if(/[&\<\>"']/.test(string=String(string))){string=string.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),string=string.replace(/"/g,"&quot;").replace(/'/g,"&#039;")}return string};ws_plugin__s2member_generateSecurityKey=function(obj){if(!obj){obj="#ws-plugin--s2member-sec-encryption-key"}var mt_rand=function(min,max){min=(arguments.length<1)?0:min;max=(arguments.length<2)?2147483647:max;return Math.floor(Math.random()*(max-min+1))+min};var chars="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#%^&*()";for(var i=0,key="";i<64;i++){key+=chars.substr(mt_rand(0,chars.length-1),1)}$(obj).val(key);return false};if(location.href.match(/page\=ws-plugin--s2member/)){$("input.ws-plugin--s2member-update-roles-button, input.ws-plugin--s2member-reset-roles-button").click(function(){var $this=$(this);$this.val("one moment please ...");var levels='<?php echo (int)$GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; ?>';var resetUpdate=($this.hasClass("ws-plugin--s2member-reset-roles-button"))?"Reset":"Update";$.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(response){if(response==="1"){alert("s2Member's Roles/Capabilities "+((resetUpdate.toLowerCase()==="reset")?"have been successfully reset":"updated successfully")+".\nYour installation of s2Member has Membership Levels 0-"+levels+"."),$this.val(resetUpdate+" Roles/Capabilities")}else{if(response==="l"){alert("Sorry, your request failed.\ns2Member's Roles/Capabilities are locked by Filter:\nws_plugin__s2member_lock_roles_caps"),$this.val(resetUpdate+" Roles/Capabilities")}else{alert("Sorry, your request failed.\nAccess denied. Do you have the ability to `create_users`?"),$this.val(resetUpdate+" Roles/Capabilities")}}});return false})}if(location.href.match(/page\=ws-plugin--s2member-logs/)){$("input#ws-plugin--s2member-gateway-debug-logs-extensive-1").click(function(){var $this=$(this),thisChecked=(this.checked)?true:false;if(thisChecked){$("input#ws-plugin--s2member-gateway-debug-logs-1").attr("checked","checked")}});var $toggles=$("a.ws-plugin--s2member-log-file-viewport-toggle");$toggles.click(function(){$("textarea#ws-plugin--s2member-log-file-viewer").each(function(){var $viewer=$(this);if($viewer.attr("data-state")!=="expanded"){$viewer.css({height:($viewer.prop("scrollHeight")+50)+"px","overflow-y":"auto"});$toggles.html("&#8657; normalize viewport &#10073;");$viewer.attr("data-state","expanded")}else{$viewer.css({height:"auto","overflow-y":"scroll"});$toggles.html("&#8659; expand viewport &#8659;");$viewer.attr("data-state","scrolling")}});return false})}if(location.href.match(/page\=ws-plugin--s2member-mms-ops/)){$("select#ws-plugin--s2member-mms-registration-file").change(function(){if($(this).val()==="wp-signup"){var gv=$("select#ws-plugin--s2member-mms-registration-grants").val(),l0v=$("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")[((gv==="all")?"show":"hide")]();$("input#ws-plugin--s2member-mms-registration-blogs-level0").val(((gv==="all")?((l0v>0)?l0v:"1"):"0"))}else{if($(this).val()==="wp-login"){var gv=$("select#ws-plugin--s2member-mms-registration-grants").val(),l0v=$("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")})}if(location.href.match(/page\=ws-plugin--s2member-gen-ops/)){ws_plugin__s2member_enableSecurityKey=function(){if(confirm("Edit Key? Are you sure?\nThis could break your installation!\n\n*Note* If you've been testing s2Member, feel free to change this Key before you go live. Just don't go live, and then change it. You'll have unhappy Customers. Data corruption WILL occur! For your safety, s2Member keeps a history of the last 10 Keys that you've used. If you get yourself into a real situation, s2Member will let you revert back to a previous Key.")){$("input#ws-plugin--s2member-sec-encryption-key").removeAttr("disabled")}return false};ws_plugin__s2member_securityKeyHistory=function(){$("div#ws-plugin--s2member-sec-encryption-key-history").toggle();return false};$("select#ws-plugin--s2member-new-user-emails-enabled").change(function(){var $pluggable=$("input#ws-plugin--s2member-pluggables-wp-new-user-notification"),$this=$(this),$newUserEmails=$("div#ws-plugin--s2member-new-user-emails");if($pluggable.val()==="0"||$this.val()==="0"){($pluggable.val()==="0")?$this.attr("disabled","disabled"):$this.removeAttr("disabled");$(":input",$newUserEmails).attr("disabled","disabled"),$newUserEmails.css("opacity","0.5")}else{$this.removeAttr("disabled"),$(":input",$newUserEmails).removeAttr("disabled"),$newUserEmails.css("opacity","")}}).trigger("change");$("select#ws-plugin--s2member-login-reg-design-enabled").change(function(){var $this=$(this),$loginRegDesign=$("div#ws-plugin--s2member-login-reg-design");if($this.val()==="0"){$(":input",$loginRegDesign).attr("disabled","disabled"),$loginRegDesign.css("opacity","0.5"),$loginRegDesign.hide()}else{$(":input",$loginRegDesign).removeAttr("disabled"),$loginRegDesign.css("opacity",""),$loginRegDesign.show()}}).trigger("change");if($("input#ws-plugin--s2member-custom-reg-fields").length&&$("div#ws-plugin--s2member-custom-reg-field-configuration").length){(function(){var i,fieldDefaults,tools,table,$tools,$table;var $fields=$("input#ws-plugin--s2member-custom-reg-fields");var $configuration=$("div#ws-plugin--s2member-custom-reg-field-configuration");var fields=($fields.val())?$.JSON.parse($fields.val()):[];fields=(fields instanceof Array)?fields:[];fieldDefaults={section:"no",sectitle:"",id:"",label:"",type:"text",deflt:"",options:"",expected:"",required:"yes",levels:"all",editable:"yes",classes:"",styles:"",attrs:""};for(i=0;i<fields.length;i++){fields[i]=$.extend(true,{},fieldDefaults,fields[i])}tools='<div id="ws-plugin--s2member-custom-reg-field-configuration-tools"></div>',table='<table id="ws-plugin--s2member-custom-reg-field-configuration-table"></table>';$configuration.html(tools+table);$tools=$("div#ws-plugin--s2member-custom-reg-field-configuration-tools"),$table=$("table#ws-plugin--s2member-custom-reg-field-configuration-table");ws_plugin__s2member_customRegFieldSectionChange=function(select){var section=$(select).val();var sectitle_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle";(section==="yes")?$(sectitle_trs).css("display",""):$(sectitle_trs).css("display","none")};ws_plugin__s2member_customRegFieldTypeChange=function(select){var type=$(select).val();var deflt_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt",options_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-options",expected_trs="tr.ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected";(type.match(/^(text|textarea)$/))?$(deflt_trs).css("display",""):$(deflt_trs).css("display","none");(type.match(/^(select|selects|checkboxes|radios)$/))?$(options_trs).css("display",""):$(options_trs).css("display","none");(type.match(/^(text|textarea)$/))?$(expected_trs).css("display",""):$(expected_trs).css("display","none")};ws_plugin__s2member_customRegFieldDelete=function(index){var newFields=new Array();for(var i=0;i<fields.length;i++){if(i!==index){newFields.push(fields[i])}}fields=newFields,updateFields(),buildTable()};ws_plugin__s2member_customRegFieldMoveUp=function(index){if(typeof fields[index]==="object"&&typeof fields[index-1]==="object"){var prevFieldObj=fields[index-1],thisFieldObj=fields[index];fields[index-1]=thisFieldObj,fields[index]=prevFieldObj;updateFields(),buildTable()}};ws_plugin__s2member_customRegFieldMoveDown=function(index){if(typeof fields[index]==="object"&&typeof fields[index+1]==="object"){var nextFieldObj=fields[index+1],thisFieldObj=fields[index];fields[index+1]=thisFieldObj,fields[index]=nextFieldObj;updateFields(),buildTable()}};ws_plugin__s2member_customRegFieldCreate=function(){var $table=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),field={};$(":input[property]",$table).each(function(){var $this=$(this),property=$this.attr("property"),val=$.trim($this.val());field[property]=val});if((field=validateField(field))){fields.push(field),updateFields(),buildTools(),buildTable(),scrollReset();setTimeout(function(){var row="tr.ws-plugin--s2member-custom-reg-field-configuration-table-row-"+(fields.length-1);alert('Field created successfully.\n* Remember to "Save All Changes".')},500)}};ws_plugin__s2member_customRegFieldUpdate=function(index){var $table=$("table#ws-plugin--s2member-custom-reg-field-configuration-tools-form"),field={};$(":input[property]",$table).each(function(){var $this=$(this),property=$this.attr("property"),val=$.trim($this.val());field[property]=val});if((field=validateField(field,index))){fields[index]=field,updateFields(),buildTools(),buildTable(),scrollReset();setTimeout(function(){var row="tr.ws-plugin--s2member-custom-reg-field-configuration-table-row-"+index;alert('Field updated successfully.\n* Remember to "Save All Changes".')},500)}};ws_plugin__s2member_customRegFieldAdd=function(){buildTools(true)};ws_plugin__s2member_customRegFieldEdit=function(index){buildTools(false,index),scrollReset()};ws_plugin__s2member_customRegFieldCancel=function(){buildTools(),scrollReset()};var validateField=function(field,index){var editing=(typeof index==="number"&&typeof fields[index]==="object")?true:false,errors=[],options,i;if(typeof field!=="object"||typeof(field=$.extend(true,{},fieldDefaults,field))!=="object"){alert("Invalid field object. Please try again.");return false}field.sectitle=(field.section==="yes")?field.sectitle:"";field.deflt=(field.type.match(/^(text|textarea)$/))?field.deflt:"";field.deflt=(field.type.match(/^(text)$/))?field.deflt.replace(/[\r\n\t ]+/g," "):field.deflt;field.options=(field.type.match(/^(select|selects|checkboxes|radios)$/))?field.options:"";field.expected=(field.type.match(/^(text|textarea)$/))?field.expected:"";if(!field.id){errors.push("Unique Field ID:\nThis is required. Please try again.")}else{if(fieldIdExists(field.id)&&(!editing||field.id!==fields[index].id)){errors.push("Unique Field ID:\nThat Field ID already exists. Please try again.")}}if(!field.label){errors.push("Field Label/Description:\nThis is required. Please try again.")}if(field.type.match(/^(select|selects|checkboxes|radios)$/)&&!field.options){errors.push("Option Configuration File:\nThis is required. Please try again.")}else{if(field.type.match(/^(select|selects|checkboxes|radios)$/)){for(i=0;i<(options=field.options.split(/[\r\n]+/)).length;i++){if(!(options[i]=$.trim(options[i])).match(/^([^\|]*)(\|)([^\|]*)(\|default)?$/)){errors.push("Option Configuration File:\nInvalid configuration at line #"+(i+1)+".");break}}field.options=$.trim(options.join("\n"))}}if(!(field.levels=field.levels.replace(/ /g,""))){errors.push("Applicable Levels:\nThis is required. Please try again.")}else{if(!field.levels.match(/^(all|[0-9,]+)$/)){errors.push("Applicable Levels:\nShould be comma-delimited Levels, or just type: all.\n(examples: 0,1,2,3,4 or type the word: all)")}}if(field.classes&&field.classes.match(/[^a-z 0-9 _ \-]/i)){errors.push("CSS Classes:\nContains invalid characters. Please try again.\n(only: alphanumerics, underscores, hyphens, spaces)")}if(field.styles&&field.styles.match(/["\=\>\<]/)){errors.push('CSS Styles:\nContains invalid characters. Please try again.\n(do NOT use these characters: = " < >)')}if(field.attrs&&field.attrs.match(/[\>\<]/)){errors.push("Other Attributes:\nContains invalid characters. Please try again.\n(do NOT use these characters: < >)")}if(errors.length>0){alert(errors.join("\n\n"));return false}else{return field}};var updateFields=function(){$fields.val(((fields.length>0)?$.JSON.stringify(fields):""))};var fieldId2Var=function(fieldId){return(typeof fieldId==="string")?$.trim(fieldId).toLowerCase().replace(/[^a-z0-9]/g,"_"):""};var fieldTypeDesc=function(type){var types={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)"};if(typeof types[type]==="string"){return types[type]}return""};var fieldIdExists=function(fieldId){for(var i=0;i<fields.length;i++){if(fields[i].id===fieldId){return true}}};var scrollReset=function(){scrollTo(0,$("div.ws-plugin--s2member-custom-reg-fields-section").offset()["top"]-100)};var buildTools=function(adding,index){var i=0,html="",form="",w=0,h=0,editing=(typeof index==="number"&&typeof fields[index]==="object")?true:false,displayForm=(adding||editing)?true:false,field=(editing)?$.extend(true,{},fieldDefaults,fields[index]):fieldDefaults;html+='<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();if(displayForm){form+='<div id="ws-plugin--s2member-custom-reg-field-configuration-thickbox-tools-form">';form+='<table id="ws-plugin--s2member-custom-reg-field-configuration-tools-form">';form+="<tbody>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">Starts A New Section?</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section">';form+='<td colspan="2">';form+='<select property="section" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-section" onchange="ws_plugin__s2member_customRegFieldSectionChange(this);">';form+='<option value="no"'+((field.section==="no")?' selected="selected"':"")+'">No (this Field flows normally)</option>';form+='<option value="yes"'+((field.section==="yes")?' selected="selected"':"")+'">Yes (this Field begins a new section)</option>';form+="</select><br />";form+="<small>Optional. Allows Fields to be grouped into sections.</small>";form+="</td>";form+="</tr>";form+='<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"'+((field.section==="yes")?"":' style="display:none;"')+'><td colspan="2"><hr /></td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle ws-plugin--s2member-custom-reg-field-configuration-tools-form-section"'+((field.section==="yes")?"":' style="display:none;"')+">";form+='<td colspan="2">';form+="Title for this new section? (optional)<br />";form+='<input type="text" property="sectitle" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-sectitle" value="'+esc_attr(field.sectitle)+'" /><br />';form+="<small>If empty, a simple divider will be used by default.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-type"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">Form Field Type: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type">';form+='<td colspan="2">';form+='<select property="type" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-type" onchange="ws_plugin__s2member_customRegFieldTypeChange(this);">';form+='<option value="text"'+((field.type==="text")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("text"))+"</option>";form+='<option value="textarea"'+((field.type==="textarea")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("textarea"))+"</option>";form+='<option value="select"'+((field.type==="select")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("select"))+"</option>";form+='<option value="selects"'+((field.type==="selects")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("selects"))+"</option>";form+='<option value="checkbox"'+((field.type==="checkbox")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("checkbox"))+"</option>";form+='<option value="pre_checkbox"'+((field.type==="pre_checkbox")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("pre_checkbox"))+"</option>";form+='<option value="checkboxes"'+((field.type==="checkboxes")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("checkboxes"))+"</option>";form+='<option value="radios"'+((field.type==="radios")?' selected="selected"':"")+'">'+esc_html(fieldTypeDesc("radios"))+"</option>";form+="</select><br />";form+="<small>The options below may change, based on the Field Type you choose here.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-label"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">Field Label/Desc: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label">';form+='<td colspan="2">';form+='<input type="text" property="label" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-label" value="'+esc_attr(field.label)+'" /><br />';form+="<small>Examples: <code>Choose Country</code>, <code>Street Address</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-id"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">Unique Field ID: *</label></label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id">';form+='<td colspan="2">';form+='<input type="text" property="id" maxlength="25" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id" value="'+esc_attr(field.id)+'" /><br />';form+="<small>Examples: <code>country_code</code>, <code>street_address</code></small><br />";form+='<small>e.g., <code>[s2Get user_field="country_code" /]</code></small>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-required"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">Field Required: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">';form+='<td colspan="2">';form+='<select property="required" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-required">';form+='<option value="yes"'+((field.required==="yes")?' selected="selected"':"")+'">Yes (required)</option>';form+='<option value="no"'+((field.required==="no")?' selected="selected"':"")+'">No (optional)</option>';form+="</select><br />";form+='<small>If <code>yes</code>, only Users/Members will be "required" to enter this field.</small><br />';form+="<small>* Administrators are exempt from this requirement.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt">Default Text Value: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<textarea property="deflt" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-deflt" rows="1">'+esc_html(field.deflt)+"</textarea><br />";form+="<small>Default value before user input is received.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+((field.type.match(/^(select|selects|checkboxes|radios)$/))?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+((field.type.match(/^(select|selects|checkboxes|radios)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options">Option Configuration File: * (one option per line)</label><br />';form+="<small>Use a pipe <code>|</code> delimited format: <code>option value|option label</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options"'+((field.type.match(/^(select|selects|checkboxes|radios)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<textarea property="options" wrap="off" spellcheck="false" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-options" rows="3">'+esc_html(field.options)+"</textarea><br />";form+="Here is a quick example:<br />";form+="<small>You can also specify a <em>default</em> option:</small><br />";form+="<code>US|United States|default</code><br />";form+="<code>CA|Canada</code><br />";form+="<code>VI|Virgin Islands (U.S.)</code>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+'><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">Expected Format: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected"'+((field.type.match(/^(text|textarea)$/))?"":' style="display:none;"')+">";form+='<td colspan="2">';form+='<select property="expected" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-expected">';form+='<option value=""'+((field.expected==="")?' selected="selected"':"")+'">Anything Goes</option>';form+='<option disabled="disabled"></option>';form+='<optgroup label="Specific Input Types">';form+='<option value="numeric-wp-commas"'+((field.expected==="numeric-wp-commas")?' selected="selected"':"")+'">Numeric (with or without decimals, commas allowed)</option>';form+='<option value="numeric"'+((field.expected==="numeric")?' selected="selected"':"")+'">Numeric (with or without decimals, no commas)</option>';form+='<option value="integer"'+((field.expected==="integer")?' selected="selected"':"")+'">Integer (whole number, without any decimals)</option>';form+='<option value="integer-gt-0"'+((field.expected==="integer-gt-0")?' selected="selected"':"")+'">Integer > 0 (whole number, no decimals, greater than 0)</option>';form+='<option value="float"'+((field.expected==="float")?' selected="selected"':"")+'">Float (floating point number, decimals required)</option>';form+='<option value="float-gt-0"'+((field.expected==="float-gt-0")?' selected="selected"':"")+'">Float > 0 (floating point number, decimals required, greater than 0)</option>';form+='<option value="date"'+((field.expected==="date")?' selected="selected"':"")+'">Date (required date format: dd/mm/yyyy)</option>';form+='<option value="email"'+((field.expected==="email")?' selected="selected"':"")+'">Email (require valid email)</option>';form+='<option value="url"'+((field.expected==="url")?' selected="selected"':"")+'">Full URL (starting with http or https)</option>';form+='<option value="domain"'+((field.expected==="domain")?' selected="selected"':"")+'">Domain Name (domain name only, without http)</option>';form+='<option value="phone"'+((field.expected==="phone")?' selected="selected"':"")+'">Phone # (10 digits w/possible hyphens,spaces,brackets)</option>';form+='<option value="uszip"'+((field.expected==="uszip")?' selected="selected"':"")+'">US Zipcode (5-9 digits w/possible hyphen)</option>';form+='<option value="cazip"'+((field.expected==="cazip")?' selected="selected"':"")+'">Canadian Zipcode (6 alpha-numerics w/possible space)</option>';form+='<option value="uczip"'+((field.expected==="uczip")?' selected="selected"':"")+'">US/Canadian Zipcode (either a US or Canadian zipcode)</option>';form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Any Character Combination">';for(i=1;i<=25;i++){form+='<option value="any-'+i+'"'+((field.expected==="any-"+i)?' selected="selected"':"")+'">Any Character Combination ('+i+" character minimum)</option>";form+='<option value="any-'+i+'-e"'+((field.expected==="any-"+i+"-e")?' selected="selected"':"")+'">Any Character Combination (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics, Spaces &amp; Punctuation Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-spaces-punctuation-'+i+'"'+((field.expected==="alphanumerics-spaces-punctuation-"+i)?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation ('+i+" character minimum)</option>";form+='<option value="alphanumerics-spaces-punctuation-'+i+'-e"'+((field.expected==="alphanumerics-spaces-punctuation-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics, Spaces &amp; Punctuation (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics &amp; Spaces Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-spaces-'+i+'"'+((field.expected==="alphanumerics-spaces-"+i)?' selected="selected"':"")+'">Alphanumerics &amp; Spaces ('+i+" character minimum)</option>";form+='<option value="alphanumerics-spaces-'+i+'-e"'+((field.expected==="alphanumerics-spaces-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics &amp; Spaces (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics &amp; Punctuation Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-punctuation-'+i+'"'+((field.expected==="alphanumerics-punctuation-"+i)?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation ('+i+" character minimum)</option>";form+='<option value="alphanumerics-punctuation-'+i+'-e"'+((field.expected==="alphanumerics-punctuation-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics &amp; Punctuation (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphanumerics Only">';for(i=1;i<=25;i++){form+='<option value="alphanumerics-'+i+'"'+((field.expected==="alphanumerics-"+i)?' selected="selected"':"")+'">Alphanumerics ('+i+" character minimum)</option>";form+='<option value="alphanumerics-'+i+'-e"'+((field.expected==="alphanumerics-"+i+"-e")?' selected="selected"':"")+'">Alphanumerics (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Alphabetics Only">';for(i=1;i<=25;i++){form+='<option value="alphabetics-'+i+'"'+((field.expected==="alphabetics-"+i)?' selected="selected"':"")+'">Alphabetics ('+i+" character minimum)</option>";form+='<option value="alphabetics-'+i+'-e"'+((field.expected==="alphabetics-"+i+"-e")?' selected="selected"':"")+'">Alphabetics (exactly '+i+" character"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+='<option disabled="disabled"></option>';form+='<optgroup label="Numeric Digits Only">';for(i=1;i<=25;i++){form+='<option value="numerics-'+i+'"'+((field.expected==="numerics-"+i)?' selected="selected"':"")+'">Numeric Digits ('+i+" digit minimum)</option>";form+='<option value="numerics-'+i+'-e"'+((field.expected==="numerics-"+i+"-e")?' selected="selected"':"")+'">Numeric Digits (exactly '+i+" digit"+((i>1)?"s":"")+")</option>"}form+="</optgroup>";form+="</select><br />";form+="<small>Only Users/Members will be required to meet this criteria.</small><br />";form+="<small>* Administrators are exempt from this.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">Applicable Membership Levels: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels">';form+='<td colspan="2">';form+='<input type="text" property="levels" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-levels" value="'+esc_attr(field.levels)+'" /><br />';form+="<small>Please use comma-delimited Level #'s: <code>0,1,2,3,4</code> or type: <code>all</code>.</small><br />";form+="<small>This allows you to enable this field - only at specific Membership Levels.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">Allow Profile Edits: *</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">';form+='<td colspan="2">';form+='<select property="editable" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-editable">';form+='<option value="yes"'+((field.editable==="yes")?' selected="selected"':"")+'">Yes (editable)</option>';form+='<option value="no"'+((field.editable==="no")?' selected="selected"':"")+'">No (uneditable after registration)</option>';form+='<option value="no-invisible"'+((field.editable==="no-invisible")?' selected="selected"':"")+'">No (uneditable &amp; totally invisible after registration)</option>';form+='<option value="no-always-invisible"'+((field.editable==="no-always-invisible")?' selected="selected"':"")+'">No (uneditable &amp; totally invisible, both during &amp; after registration)</option>';form+='<option value="yes-invisible"'+((field.editable==="yes-invisible")?' selected="selected"':"")+'">Yes (editable after registration / invisible during registration)</option>';form+="</select><br />";form+="<small>If <code>No</code>, this field will be un-editable after registration.</small><br />";form+="<small>* Administrators are exempt from this.</small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">CSS Classes: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes">';form+='<td colspan="2">';form+='<input type="text" property="classes" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-classes" value="'+esc_attr(field.classes)+'" /><br />';form+="<small>Example: <code>my-style-1 my-style-2</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">CSS Styles: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles">';form+='<td colspan="2">';form+='<input type="text" property="styles" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-styles" value="'+esc_attr(field.styles)+'" /><br />';form+="<small>Example: <code>color:#000000; background:#FFFFFF;</code></small>";form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">';form+='<td colspan="2">';form+='<label for="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">Other Attributes: (optional)</label>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs">';form+='<td colspan="2">';form+='<input type="text" property="attrs" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-attrs" value="'+esc_attr(field.attrs)+'" /><br />';form+='<small>Example: <code>onkeyup="" onblur=""</code></small>';form+="</td>";form+="</tr>";form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-spacer ws-plugin--s2member-custom-reg-field-configuration-tools-form-buttons"><td colspan="2">&nbsp;</td></tr>';form+='<tr class="ws-plugin--s2member-custom-reg-field-configuration-tools-form-buttons">';form+='<td align="left">';form+='<input type="button" value="Cancel" onclick="ws_plugin__s2member_customRegFieldCancel();" />';form+="</td>";form+='<td align="right">';form+='<input type="button" value="'+((editing)?"Update This Field":"Create Registration Field")+'" onclick="'+((editing)?"ws_plugin__s2member_customRegFieldUpdate("+index+");":"ws_plugin__s2member_customRegFieldCreate();")+'" />';form+="</td>";form+="</tr>";form+="</tbody>";form+="</table>";form+="<div>";$("body").append(form);tb_show(((editing)?"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()}$tools.html(html)};var buildTable=function(){var l=fields.length,i=0,html="",eo="o";html+="<tbody>";html+="<tr>";html+="<th>Order</th>";html+="<th>Field Type</th>";html+="<th>Unique ID</th>";html+="<th>Required</th>";html+="<th>Levels</th>";html+="<th>- Tools -</th>";html+="</tr>";if(fields.length>0){for(i=0;i<fields.length;i++){html+='<tr class="'+esc_attr((eo=(eo==="o")?"e":"o"))+((fields[i].section==="yes")?" s":"")+" ws-plugin--s2member-custom-reg-field-configuration-table-row-"+i+'">';html+='<td nowrap="nowrap"><a class="ws-plugin--s2member-custom-reg-field-configuration-move-up" href="#" onclick="ws_plugin__s2member_customRegFieldMoveUp('+i+'); return false;"></a><a class="ws-plugin--s2member-custom-reg-field-configuration-move-down" href="#" onclick="ws_plugin__s2member_customRegFieldMoveDown('+i+'); return false;"></a></td>';html+='<td nowrap="nowrap">'+esc_html(fieldTypeDesc(fields[i].type))+"</td>";html+='<td nowrap="nowrap">'+esc_html(fields[i].id)+"</td>";html+='<td nowrap="nowrap">'+esc_html(fields[i].required)+"</td>";html+='<td nowrap="nowrap">'+esc_html(fields[i].levels)+"</td>";html+='<td nowrap="nowrap"><a class="ws-plugin--s2member-custom-reg-field-configuration-edit" href="#" onclick="ws_plugin__s2member_customRegFieldEdit('+i+'); return false;"></a><a class="ws-plugin--s2member-custom-reg-field-configuration-delete" href="#" onclick="ws_plugin__s2member_customRegFieldDelete('+i+'); return false;"></a></td>';html+="</tr>"}}else{html+="<tr>";html+='<td colspan="6">No Custom Fields are configured.</td>';html+="</tr>"}html+="</tbody>";$table.html(html)};buildTools(),buildTable()})()}}if(location.href.match(/page\=ws-plugin--s2member-res-ops/)){$("input#ws-plugin--s2member-brute-force-restrictions-reset-button").click(function(){var $this=$(this);$this.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(response){alert("s2Member's Brute Force Restriction Logs have all been reset."),$this.val("Reset Brute Force Logs")});return false});$("input#ws-plugin--s2member-ip-restrictions-reset-button").click(function(){var $this=$(this);$this.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(response){alert("s2Member's IP Restriction Logs have all been reset."),$this.val("Reset IP Restriction Logs")});return false});$('div.ws-plugin--s2member-query-level-access-section input[type="checkbox"][name="ws_plugin__s2member_filter_wp_query[]"]').change(function(){var thisChange=$(this).val();$('div.ws-plugin--s2member-query-level-access-section input[type="checkbox"][name="ws_plugin__s2member_filter_wp_query[]"]').each(function(){var $this=$(this),val=$this.val(),checkboxes='input[type="checkbox"]';if(val==="all"&&this.checked){$this.nextAll(checkboxes).attr({checked:"checked",disabled:"disabled"})}else{if(val==="all"&&!this.checked){$this.nextAll(checkboxes).removeAttr("disabled");(thisChange==="all")?$this.nextAll(checkboxes).removeAttr("checked"):null}}})}).last().trigger("change")}if(location.href.match(/page\=ws-plugin--s2member-down-ops/)){var updateCloudFrontPrivateKey=function(){var $hiddenPrivateKey=$("input#ws-plugin--s2member-amazon-cf-files-private-key");var $visiblePrivateKeyEntry=$("textarea#ws-plugin--s2member-amazon-cf-files-private-key-entry");var hiddenPrivateKeyValue=$.trim($hiddenPrivateKey.val()),visiblePrivateKeyEntryValue=$.trim($visiblePrivateKeyEntry.val());if((hiddenPrivateKeyValue&&!visiblePrivateKeyEntryValue)||visiblePrivateKeyEntryValue.match(/[^\r\n\u25CF]/)){$hiddenPrivateKey.val(visiblePrivateKeyEntryValue),$visiblePrivateKeyEntry.val(visiblePrivateKeyEntryValue.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 $hiddenPrivateKey=$("input#ws-plugin--s2member-amazon-cf-files-private-key");var $visiblePrivateKeyId=$("input#ws-plugin--s2member-amazon-cf-files-private-key-id");var $autoConfigDistros=$("input#ws-plugin--s2member-amazon-cf-files-auto-configure-distros");var $autoConfigDistrosStatus=$("input#ws-plugin--s2member-amazon-cf-files-distros-auto-config-status");var autoConfigDistrosStatusValue=$.trim($autoConfigDistrosStatus.val());var hiddenPrivateKeyValue=$.trim($hiddenPrivateKey.val()),visiblePrivateKeyIdValue=$.trim($visiblePrivateKeyId.val());var hiddenPrivateKeyPrevConfigValue=$.trim($hiddenPrivateKey.attr("data-s-prev-config-value")),visiblePrivateKeyIdPrevConfigValue=$.trim($visiblePrivateKeyId.attr("data-s-prev-config-value"));if(autoConfigDistrosStatusValue==="configured"&&((visiblePrivateKeyIdPrevConfigValue&&visiblePrivateKeyIdValue!==visiblePrivateKeyIdPrevConfigValue)||(hiddenPrivateKeyPrevConfigValue&&hiddenPrivateKeyValue!==hiddenPrivateKeyPrevConfigValue))){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.");$autoConfigDistros.attr("checked","checked")}else{if(autoConfigDistrosStatusValue!=="configured"&&visiblePrivateKeyIdValue&&hiddenPrivateKeyValue){alert("s2Member will need to auto-configure your Amazon CloudFront distributions for you. When you're done editing, click (Save All Changes) below.");$autoConfigDistros.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 $this=$(this),thisChecked=(this.checked)?true:false;var $autoConfigDistros=$("input#ws-plugin--s2member-amazon-cf-files-auto-configure-distros");var $autoConfigDistroCnames=$("div#ws-plugin--s2member-amazon-cf-files-auto-configure-distro-cnames");(thisChecked)?$autoConfigDistroCnames.show():$autoConfigDistroCnames.hide();(thisChecked)?$autoConfigDistros.attr("checked","checked"):null}).trigger("change")}if(location.href.match(/page\=ws-plugin--s2member-paypal-ops/)){$("select#ws-plugin--s2member-auto-eot-system-enabled").change(function(){var $this=$(this),val=$this.val();var $viaCron=$("p#ws-plugin--s2member-auto-eot-system-enabled-via-cron");if(val==2){$viaCron.show()}else{$viaCron.hide()}})}if(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 button=this.id.replace(/^ws-plugin--s2member-(.+?)-term$/g,"$1");var trialDisabled=($(this).val().split("-")[2].replace(/[^0-1BN]/g,"")==="BN")?1:0;$("p#ws-plugin--s2member-"+button+"-trial-line").css("display",(trialDisabled?"none":""));$("span#ws-plugin--s2member-"+button+"-trial-then").css("display",(trialDisabled?"none":""));$("span#ws-plugin--s2member-"+button+"-20p-rule").css("display",(trialDisabled?"none":""));(trialDisabled)?$("input#ws-plugin--s2member-"+button+"-trial-period").val(0):null;(trialDisabled)?$("input#ws-plugin--s2member-"+button+"-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 value=this.value.replace(/^(-all|-al|-a|-)[;,]*/gi,""),_all=(this.value.match(/^(-all|-al|-a|-)[;,]*/i))?"-all,":"";if(value.match(/[^a-z_0-9,]/)){this.value=_all+$.trim($.trim(value).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");var code=$("textarea#ws-plugin--s2member-"+button+"-button");var modLevel=$("select#ws-plugin--s2member-modification-level");var level=(button==="modification")?modLevel.val().split(":",2)[1]:button.replace(/^level/,"");var label=labels["level"+level].replace(/"/g,"");var desc=$.trim($("input#ws-plugin--s2member-"+button+"-desc").val().replace(/"/g,""));var trialAmount=$("input#ws-plugin--s2member-"+button+"-trial-amount").val().replace(/[^0-9\.]/g,"");var trialPeriod=$("input#ws-plugin--s2member-"+button+"-trial-period").val().replace(/[^0-9]/g,"");var trialTerm=$("select#ws-plugin--s2member-"+button+"-trial-term").val().replace(/[^A-Z]/g,"");var regAmount=$("input#ws-plugin--s2member-"+button+"-amount").val().replace(/[^0-9\.]/g,"");var regPeriod=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[0].replace(/[^0-9]/g,"");var regTerm=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[1].replace(/[^A-Z]/g,"");var regRecur=$("select#ws-plugin--s2member-"+button+"-term").val().split("-")[2].replace(/[^0-1BN]/g,"");var regRecurTimes="",regRecurRetry="1";var localeCode="",digital="0",noShipping="1";var pageStyle=$.trim($("input#ws-plugin--s2member-"+button+"-page-style").val().replace(/"/g,""));var currencyCode=$("select#ws-plugin--s2member-"+button+"-currency").val().replace(/[^A-Z]/g,"");var 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=(regRecur==="BN")?"0":trialPeriod;trialAmount=(!trialAmount||isNaN(trialAmount)||trialAmount<0.01||trialPeriod<=0)?"0":trialAmount;var levelCcapsPer=(regRecur==="BN"&&regTerm!=="L")?level+":"+cCaps+":"+regPeriod+" "+regTerm:level+":"+cCaps;levelCcapsPer=levelCcapsPer.replace(/\:+$/g,"");if(trialAmount!=="0"&&(isNaN(trialAmount)||trialAmount<0)){alert("— Oops, a slight problem: —\n\nWhen provided, Trial Amount must be >= 0.00");return false}else{if(trialAmount!=="0"&&trialAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Trial Amount is: 10000.00");return false}else{if(trialTerm==="D"&&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.");return false}else{if(trialTerm==="W"&&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.");return false}else{if(trialTerm==="M"&&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.");return false}else{if(trialTerm==="Y"&&trialPeriod>5){alert("— Oops, a slight problem: —\n\nMax Trial Period Years is: 5.");return false}else{if(!regAmount||isNaN(regAmount)||regAmount<0.01){alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01");return false}else{if(regAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00");return false}else{if(!desc){alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button.");return false}}}}}}}}}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;(regRecur==="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="cmd" value\=")(.*?)(" \/\>)/g," $1_xclick$3")):null;(regRecur==="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="(src|srt|sra|a1|p1|t1|a3|p3|t3)" value\="(.*?)" \/\>)/g," <!--$1-->")):null;(regRecur!=="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="cmd" value\=")(.*?)(" \/\>)/g," $1_xclick-subscriptions$3")):null;(regRecur!=="BN")?code.html(code.val().replace(/ (\<input type\="hidden" name\="amount" value\="(.*?)" \/\>)/g," <!--$1-->")):null;shortCodeTemplateAttrs+=(button==="modification")?'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="'+((button==="modification")?"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 ($_SERVER["REMOTE_ADDR"])); ?>').replace(/\<\?php echo S2MEMBER_CURRENT_USER_VALUE_FOR_PP_(ON0|OS0|ON1|OS1); \?\>/g,""));(button==="modification")?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()});return false};ws_plugin__s2member_paypalCcapButtonGenerate=function(){var shortCodeTemplate='[s2Member-PayPal-Button %%attrs%% image="default" output="button" /]',shortCodeTemplateAttrs="";var shortCode=$("input#ws-plugin--s2member-ccap-shortcode");var code=$("textarea#ws-plugin--s2member-ccap-button");var desc=$.trim($("input#ws-plugin--s2member-ccap-desc").val().replace(/"/g,""));var regAmount=$("input#ws-plugin--s2member-ccap-amount").val().replace(/[^0-9\.]/g,"");var regPeriod=$("select#ws-plugin--s2member-ccap-term").val().split("-")[0].replace(/[^0-9]/g,"");var regTerm=$("select#ws-plugin--s2member-ccap-term").val().split("-")[1].replace(/[^A-Z]/g,"");var regRecur=$("select#ws-plugin--s2member-ccap-term").val().split("-")[2].replace(/[^0-1BN]/g,"");var localeCode="",digital="0",noShipping="1";var pageStyle=$.trim($("input#ws-plugin--s2member-ccap-page-style").val().replace(/"/g,""));var currencyCode=$("select#ws-plugin--s2member-ccap-currency").val().replace(/[^A-Z]/g,"");var cCaps=$.trim($.trim($("input#ws-plugin--s2member-ccap-ccaps").val()).replace(/^(-all|-al|-a|-)[;,]*/gi,"").replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase());cCaps=($.trim($("input#ws-plugin--s2member-ccap-ccaps").val()).match(/^(-all|-al|-a|-)[;,]*/i))?((cCaps)?"-all,":"-all")+cCaps.toLowerCase():cCaps.toLowerCase();var levelCcapsPer=(regRecur==="BN"&&regTerm!=="L")?"*:"+cCaps+":"+regPeriod+" "+regTerm:"*:"+cCaps;levelCcapsPer=levelCcapsPer.replace(/\:+$/g,"");if(!cCaps||cCaps==="-all"){alert("— Oops, a slight problem: —\n\nPlease provide at least one Custom Capability.");return false}else{if(!regAmount||isNaN(regAmount)||regAmount<0.01){alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01");return false}else{if(regAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00");return false}else{if(!desc){alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button.");return false}}}}shortCodeTemplateAttrs+='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)+'"';shortCodeTemplateAttrs+=' custom="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>" ra="'+esc_attr(regAmount)+'" rp="'+esc_attr(regPeriod)+'" rt="'+esc_attr(regTerm)+'" rr="'+esc_attr(regRecur)+'"';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\="amount" value\="(.*?)"/,' name="amount" value="'+esc_attr(regAmount)+'"'));$("div#ws-plugin--s2member-ccap-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 ($_SERVER["REMOTE_ADDR"])); ?>').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.");shortCode.each(function(){this.focus(),this.select()});return false};ws_plugin__s2member_paypalSpButtonGenerate=function(){var shortCodeTemplate='[s2Member-PayPal-Button %%attrs%% image="default" output="button" /]',shortCodeTemplateAttrs="";var shortCode=$("input#ws-plugin--s2member-sp-shortcode");var code=$("textarea#ws-plugin--s2member-sp-button");var leading=$("select#ws-plugin--s2member-sp-leading-id").val().replace(/[^0-9]/g,"");var additionals=$("select#ws-plugin--s2member-sp-additional-ids").val()||[];var hours=$("select#ws-plugin--s2member-sp-hours").val().replace(/[^0-9]/g,"");var regAmount=$("input#ws-plugin--s2member-sp-amount").val().replace(/[^0-9\.]/g,"");var desc=$.trim($("input#ws-plugin--s2member-sp-desc").val().replace(/"/g,""));var localeCode="",digital="0",noShipping="1";var pageStyle=$.trim($("input#ws-plugin--s2member-sp-page-style").val().replace(/"/g,""));var currencyCode=$("select#ws-plugin--s2member-sp-currency").val().replace(/[^A-Z]/g,"");if(!leading){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.");return false}else{if(!regAmount||isNaN(regAmount)||regAmount<0.01){alert("— Oops, a slight problem: —\n\nAmount must be >= 0.01");return false}else{if(regAmount>10000&&currencyCode.toUpperCase()==="USD"){alert("— Oops, a slight problem: —\n\nMaximum Amount is: 10000.00");return false}else{if(!desc){alert("— Oops, a slight problem: —\n\nPlease type a Description for this Button.");return false}}}}for(var i=0,ids=leading;i<additionals.length;i++){if(additionals[i]&&additionals[i]!==leading){ids+=","+additionals[i]}}var spIdsHours="sp:"+ids+":"+hours;shortCodeTemplateAttrs+='sp="1" ids="'+esc_attr(ids)+'" exp="'+esc_attr(hours)+'" desc="'+esc_attr(desc)+'" ps="'+esc_attr(pageStyle)+'" lc="'+esc_attr(localeCode)+'" cc="'+esc_attr(currencyCode)+'" dg="'+esc_attr(digital)+'" ns="'+esc_attr(noShipping)+'"';shortCodeTemplateAttrs+=' custom="<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"])); ?>" ra="'+esc_attr(regAmount)+'"';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(spIdsHours)+'"'));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\="amount" value\="(.*?)"/,' name="amount" value="'+esc_attr(regAmount)+'"'));$("div#ws-plugin--s2member-sp-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 ($_SERVER["REMOTE_ADDR"])); ?>').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.");shortCode.each(function(){this.focus(),this.select()});return false};ws_plugin__s2member_paypalRegLinkGenerate=function(){var level=$("select#ws-plugin--s2member-reg-link-level").val().replace(/[^0-9]/g,"");var subscrID=$.trim($("input#ws-plugin--s2member-reg-link-subscr-id").val());var custom=$.trim($("input#ws-plugin--s2member-reg-link-custom").val());var cCaps=$.trim($.trim($("input#ws-plugin--s2member-reg-link-ccaps").val()).replace(/[ \-]/g,"_").replace(/[^a-z_0-9,]/gi,"").toLowerCase());var fixedTerm=$.trim($("input#ws-plugin--s2member-reg-link-fixed-term").val().replace(/[^A-Z 0-9]/gi,"").toUpperCase());var $link=$("p#ws-plugin--s2member-reg-link"),$loading=$("img#ws-plugin--s2member-reg-link-loading");var levelCcapsPer=(fixedTerm&&!fixedTerm.match(/L$/))?level+":"+cCaps+":"+fixedTerm:level+":"+cCaps;levelCcapsPer=levelCcapsPer.replace(/\:+$/g,"");if(!subscrID){alert("— Oops, a slight problem: —\n\nPaid Subscr. ID is a required value.");return false}else{if(!custom||custom.indexOf('<?php echo c_ws_plugin__s2member_utils_strings::esc_js_sq ($_SERVER["HTTP_HOST"]); ?>')!==0){alert("— Oops, a slight problem: —\n\nThe Custom Value MUST start with your domain name.");return false}else{if(fixedTerm&&!fixedTerm.match(/^[1-9]+ (D|W|M|Y|L)$/)){alert("— Oops, a slight problem: —\n\nThe Fixed Term Length is not formatted properly.");return false}}}$link.hide(),$loading.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:subscrID,s2member_reg_access_link_custom:custom,s2member_reg_access_link_item_number:levelCcapsPer},function(response){$link.show().html('<a href="'+esc_attr(response)+'" target="_blank" rel="external">'+esc_html(response)+"</a>"),$loading.hide()});return false};ws_plugin__s2member_paypalSpLinkGenerate=function(){var leading=$("select#ws-plugin--s2member-sp-link-leading-id").val().replace(/[^0-9]/g,"");var additionals=$("select#ws-plugin--s2member-sp-link-additional-ids").val()||[];var hours=$("select#ws-plugin--s2member-sp-link-hours").val().replace(/[^0-9]/g,"");var $link=$("p#ws-plugin--s2member-sp-link"),$loading=$("img#ws-plugin--s2member-sp-link-loading");if(!leading){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.");return false}for(var i=0,ids=leading;i<additionals.length;i++){if(additionals[i]&&additionals[i]!==leading){ids+=","+additionals[i]}}$link.hide(),$loading.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:ids,s2member_sp_access_link_hours:hours},function(response){$link.show().html('<a href="'+esc_attr(response)+'" target="_blank" rel="external">'+esc_html(response)+"</a>"),$loading.hide()});return false}}if(location.href.match(/page\=ws-plugin--s2member-els-ops/)){$("select#ws-plugin--s2member-custom-reg-opt-in").change(function(){var $this=$(this),val=$this.val();var $rows=$("tr.ws-plugin--s2member-custom-reg-opt-in-label-row");var $prevImg=$("img.ws-plugin--s2member-custom-reg-opt-in-label-prev-img");if(val<=0){$rows.css("display","none"),$prevImg.attr("src",$prevImg.attr("src").replace(/\/checked\.png$/,"/unchecked.png"))}else{if(val==1){$rows.css("display",""),$prevImg.attr("src",$prevImg.attr("src").replace(/\/unchecked\.png$/,"/checked.png"))}else{if(val==2){$rows.css("display",""),$prevImg.attr("src",$prevImg.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 thisChange=$(this).val(),checkedIndexes=[];$('div.ws-plugin--s2member-opt-out-section input[type="checkbox"][name="ws_plugin__s2member_custom_reg_auto_opt_outs[]"]').each(function(){var $this=$(this),val=$this.val(),checkboxes='input[type="checkbox"]';if(val==="removal-deletion"&&this.checked){$this.nextAll(checkboxes).slice(0,2).attr({checked:"checked",disabled:"disabled"})}else{if(val==="removal-deletion"&&!this.checked){$this.nextAll(checkboxes).slice(0,2).removeAttr("disabled");(thisChange==="removal-deletion")?$this.nextAll(checkboxes).slice(0,2).removeAttr("checked"):null}else{if(val==="modification"&&this.checked){$this.nextAll(checkboxes).slice(0,3).attr({checked:"checked",disabled:"disabled"})}else{if(val==="modification"&&!this.checked){(thisChange==="modification")?$this.nextAll(checkboxes).slice(0,3).removeAttr("checked"):null;$this.nextAll(checkboxes).slice(0,3).removeAttr("disabled")}}}}}).each(function(index){(this.checked)?checkedIndexes.push(index):null});$("select#ws-plugin--s2member-custom-reg-auto-opt-out-transitions").removeAttr("disabled");if($.inArray(3,checkedIndexes)===-1&&$.inArray(4,checkedIndexes)===-1&&$.inArray(5,checkedIndexes)===-1&&$.inArray(6,checkedIndexes)===-1){$("select#ws-plugin--s2member-custom-reg-auto-opt-out-transitions").attr("disabled","disabled")}}).last().trigger("change")}});
includes/menu-pages/menu-pages-s.js CHANGED
@@ -180,7 +180,7 @@ jQuery(document).ready(function($)
180
 
181
  tools = '<div id="ws-plugin--s2member-custom-reg-field-configuration-tools"></div>', table = '<table id="ws-plugin--s2member-custom-reg-field-configuration-table"></table>';
182
 
183
- $configuration.html /* Add tools div & table div to configuration div (i.e. div#ws-plugin--s2member-custom-reg-field-configuration). */(tools+table);
184
 
185
  $tools = $('div#ws-plugin--s2member-custom-reg-field-configuration-tools'), $table = $('table#ws-plugin--s2member-custom-reg-field-configuration-table');
186
 
@@ -488,7 +488,7 @@ jQuery(document).ready(function($)
488
  form += '<td colspan="2">';
489
  form += '<input type="text" property="id" maxlength="25" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id" value="'+esc_attr(field.id)+'" /><br />';
490
  form += '<small>Examples: <code>country_code</code>, <code>street_address</code></small><br />';
491
- form += '<small>e.g. <code>[s2Get user_field="country_code" /]</code></small>';
492
  form += '</td>';
493
  form += '</tr>';
494
 
@@ -1157,7 +1157,7 @@ jQuery(document).ready(function($)
1157
 
1158
  if /* Must have a Leading Post/Page ID to work with. Otherwise, Link generation will fail. */(!leading)
1159
  {
1160
- 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.');
1161
  return false;
1162
  }
1163
  else if(!regAmount || isNaN(regAmount) || regAmount < 0.01)
@@ -1247,7 +1247,7 @@ jQuery(document).ready(function($)
1247
 
1248
  if /* Must have a Leading Post/Page ID to work with. Otherwise, Link generation will fail. */(!leading)
1249
  {
1250
- 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.');
1251
  return false;
1252
  }
1253
  for(var i = 0, ids = leading; i < additionals.length; i++)
180
 
181
  tools = '<div id="ws-plugin--s2member-custom-reg-field-configuration-tools"></div>', table = '<table id="ws-plugin--s2member-custom-reg-field-configuration-table"></table>';
182
 
183
+ $configuration.html /* Add tools div & table div to configuration div (i.e., div#ws-plugin--s2member-custom-reg-field-configuration). */(tools+table);
184
 
185
  $tools = $('div#ws-plugin--s2member-custom-reg-field-configuration-tools'), $table = $('table#ws-plugin--s2member-custom-reg-field-configuration-table');
186
 
488
  form += '<td colspan="2">';
489
  form += '<input type="text" property="id" maxlength="25" autocomplete="off" id="ws-plugin--s2member-custom-reg-field-configuration-tools-form-id" value="'+esc_attr(field.id)+'" /><br />';
490
  form += '<small>Examples: <code>country_code</code>, <code>street_address</code></small><br />';
491
+ form += '<small>e.g., <code>[s2Get user_field="country_code" /]</code></small>';
492
  form += '</td>';
493
  form += '</tr>';
494
 
1157
 
1158
  if /* Must have a Leading Post/Page ID to work with. Otherwise, Link generation will fail. */(!leading)
1159
  {
1160
+ 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.');
1161
  return false;
1162
  }
1163
  else if(!regAmount || isNaN(regAmount) || regAmount < 0.01)
1247
 
1248
  if /* Must have a Leading Post/Page ID to work with. Otherwise, Link generation will fail. */(!leading)
1249
  {
1250
+ 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.');
1251
  return false;
1252
  }
1253
  for(var i = 0, ids = leading; i < additionals.length; i++)
includes/menu-pages/menu-pages.css CHANGED
@@ -21,30 +21,40 @@ Alter WordPress wrap on s2Member menu pages.
21
  */
22
  body[class*='s2member'] #wpwrap
23
  {
24
- background : #C7C7C7;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
  /*
27
  Align these with menu page.
28
  */
29
  div.updated, div.error
30
  {
31
- font-size : 120%;
32
- margin : 25px 25px 25px 5px;
33
  }
34
  /*
35
  These CSS selectors address common layout styles.
36
  */
37
  div.ws-menu-page
38
  {
39
- font-size : 14px;
40
- padding : 25px;
41
- min-width : 800px;
42
- margin : 25px 25px 25px 5px;
43
-
44
- border-radius : 5px;
45
- border : 1px solid #333333;
46
- box-shadow : 0 0 8px 0 rgba(0, 0, 0, 0.2) !important;
47
- background : #FFFFFF url('<?php echo $i; ?>/trans-bg.png') repeat left top;
48
  }
49
  div.ws-menu-page p,
50
  div.ws-menu-page code
@@ -53,7 +63,7 @@ div.ws-menu-page code
53
  }
54
  div.ws-menu-page a
55
  {
56
- color : #003E75;
57
  }
58
  div.ws-menu-page a:hover,
59
  div.ws-menu-page a:active
@@ -83,15 +93,18 @@ div.ws-menu-page > h2
83
  {
84
  font-size : 200%;
85
  font-weight : bold;
 
86
 
87
  height : 90px;
88
  box-sizing : border-box;
89
  position : relative;
90
  z-index : 0;
91
 
92
- margin-bottom : 25px;
93
- padding : 25px 0 0 120px;
94
  background : url('<?php echo $i; ?>/s2___.png') no-repeat left bottom;
 
 
95
  }
96
  div.ws-menu-page > h2:before
97
  {
@@ -120,7 +133,7 @@ div.ws-menu-page > h2:after
120
  z-index : 0;
121
  position : absolute;
122
 
123
- background : url('<?php echo $i; ?>/s2___end.png') no-repeat right bottom
124
  }
125
  div.ws-menu-page code
126
  {
@@ -321,25 +334,25 @@ div.ws-menu-page input:not([type='image']):not([type='button']):not([type='submi
321
  }
322
  div.ws-menu-page input::-webkit-input-placeholder
323
  {
324
- padding-top : 3px;
325
  font-style : italic;
326
  color : rgba(255, 255, 255, 0.2);
327
  }
328
  div.ws-menu-page input::-moz-placeholder
329
  {
330
- padding-top : 3px;
331
  font-style : italic;
332
  color : rgba(255, 255, 255, 0.2);
333
  }
334
  div.ws-menu-page input:-moz-placeholder
335
  {
336
- padding-top : 3px;
337
  font-style : italic;
338
  color : rgba(255, 255, 255, 0.2);
339
  }
340
  div.ws-menu-page input:-ms-input-placeholder
341
  {
342
- padding-top : 3px;
343
  font-style : italic;
344
  color : rgba(255, 255, 255, 0.2);
345
  }
@@ -417,7 +430,6 @@ div.ws-menu-page .warning,
417
  div.ws-menu-page .error
418
  {
419
  border-radius : 5px;
420
- font-size : 120%;
421
  padding : 10px;
422
  margin : 0 0 25px 0;
423
  }
@@ -473,8 +485,8 @@ div.ws-menu-page div.ws-menu-page-groups-hide
473
  line-height : 1em;
474
  font-size : 150%;
475
  font-weight : bold;
476
- color : #FFFFFF;
477
- background : #0E4152;
478
 
479
  border-radius : 4px;
480
  border : 1px solid rgba(0, 0, 0, 0.5);
@@ -498,7 +510,7 @@ div.ws-menu-page div.ws-menu-page-groups-show
498
  div.ws-menu-page div.ws-menu-page-groups-show:hover,
499
  div.ws-menu-page div.ws-menu-page-groups-hide:hover
500
  {
501
- background : #0D1F2F;
502
  }
503
  div.ws-menu-page div.ws-menu-page-groups-show:active,
504
  div.ws-menu-page div.ws-menu-page-groups-hide:active
@@ -508,11 +520,12 @@ div.ws-menu-page div.ws-menu-page-groups-hide:active
508
  }
509
  div.ws-menu-page div.ws-menu-page-toolbox
510
  {
511
- float : right;
512
- max-width : 320px;
513
 
514
- z-index : 1;
515
- position : relative;
 
516
  }
517
  div.ws-menu-page div.ws-menu-page-toolbox > .links
518
  {
@@ -589,8 +602,8 @@ table.ws-menu-page-table td.ws-menu-page-table-r > .toggler
589
  top : 0;
590
  right : 0;
591
 
592
- background : #0E4152 url('<?php echo $i; ?>/trans-bg.png') repeat left top;
593
- box-shadow : 0 0 0 1px #FFFFFF;
594
  border-radius : 3px;
595
 
596
  cursor : pointer;
@@ -600,7 +613,8 @@ table.ws-menu-page-table td.ws-menu-page-table-r > .toggler
600
  }
601
  table.ws-menu-page-table td.ws-menu-page-table-r > .toggler:hover
602
  {
603
- background-color : #0D1F2F;
 
604
  }
605
  table.ws-menu-page-table td.ws-menu-page-table-r > .wrapper
606
  {
@@ -666,8 +680,8 @@ table.ws-menu-page-table img.ws-menu-page-brand-updates
666
  }
667
  table.ws-menu-page-table p.submit
668
  {
669
- margin : 0;
670
  padding : 0;
 
671
  }
672
  table.ws-menu-page-table a[rel~="external"]
673
  {
@@ -684,13 +698,13 @@ table.ws-menu-page-table div.ws-menu-page-r-group-header
684
  font-size : 150%;
685
  font-weight : bold;
686
  line-height : 1em;
687
- color : #EEEEEE;
688
- background : #173B47;
689
 
690
  border-radius : 5px;
691
  box-shadow : 0 2px 2px 0 rgba(0, 0, 0, 0.25);
692
 
693
- margin-bottom : 25px;
694
 
695
  cursor : pointer;
696
  -webkit-user-select : none;
@@ -702,17 +716,18 @@ table.ws-menu-page-table div.ws-menu-page-r-group-header:hover,
702
  table.ws-menu-page-table div.ws-menu-page-group-header.open,
703
  table.ws-menu-page-table div.ws-menu-page-r-group-header.open
704
  {
705
- color : #FFFFFF;
706
- background : #0D1F2F;
707
  }
708
  table.ws-menu-page-table div.ws-menu-page-group-header.open,
709
  table.ws-menu-page-table div.ws-menu-page-r-group-header.open
710
  {
711
  margin-bottom : 0;
712
  }
713
- table.ws-menu-page-table div.ws-menu-page-group-header:after,
714
- table.ws-menu-page-table div.ws-menu-page-r-group-header:after
715
  {
 
 
716
  font-size : 80%;
717
  content : '\f078';
718
  font-weight : normal;
@@ -722,8 +737,15 @@ table.ws-menu-page-table div.ws-menu-page-r-group-header:after
722
  margin : 0 0 0 10px;
723
  display : inline-block;
724
  }
725
- table.ws-menu-page-table div.ws-menu-page-group-header.open:after,
726
- table.ws-menu-page-table div.ws-menu-page-r-group-header.open:after
 
 
 
 
 
 
 
727
  {
728
  content : '\f077';
729
  }
@@ -738,7 +760,7 @@ table.ws-menu-page-table div.ws-menu-page-r-group
738
  position : relative;
739
  display : none;
740
 
741
- padding : 25px;
742
  color : #333333;
743
  background : #D0DDAD;
744
 
@@ -751,7 +773,7 @@ table.ws-menu-page-table div.ws-menu-page-group.open,
751
  table.ws-menu-page-table div.ws-menu-page-r-group.open
752
  {
753
  display : block;
754
- margin-bottom : 25px;
755
  }
756
  table.ws-menu-page-table div.ws-menu-page-group div.ws-menu-page-hr,
757
  table.ws-menu-page-table div.ws-menu-page-r-group div.ws-menu-page-hr
@@ -830,6 +852,10 @@ table.ws-menu-page-table div.ws-menu-page-group table.form-table > tbody > tr >
830
  background : #73834B;
831
  box-shadow : 0 0 1px 0 rgba(0, 0, 0, 0.5) inset, 0 0 1px 1px rgba(223, 245, 165, 0.5);
832
  }
 
 
 
 
833
  /*
834
  Coupons table.
835
  */
21
  */
22
  body[class*='s2member'] #wpwrap
23
  {
24
+ position : relative;
25
+ background : #BEBEBE url('<?php echo $i; ?>/trans-bg.png');
26
+ }
27
+ body[class*='s2member'] #wpwrap::before
28
+ {
29
+ content : '';
30
+ top : 0;
31
+ left : 0;
32
+ z-index : 0;
33
+ width : 100%;
34
+ height : 500px;
35
+ position : absolute;
36
+ background : -moz-linear-gradient(top, rgba(241, 241, 241, 1) 0%, rgba(241, 241, 241, 0.8) 20%, rgba(241, 241, 241, 0) 100%);
37
+ background : -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(241, 241, 241, 1)), color-stop(20%, rgba(241, 241, 241, 0.8)), color-stop(100%, rgba(241, 241, 241, 0))); /* Chrome,Safari4+ */
38
+ background : -webkit-linear-gradient(top, rgba(241, 241, 241, 1) 0%, rgba(241, 241, 241, 0.8) 20%, rgba(241, 241, 241, 0) 100%);
39
+ background : -o-linear-gradient(top, rgba(241, 241, 241, 1) 0%, rgba(241, 241, 241, 0.8) 20%, rgba(241, 241, 241, 0) 100%);
40
+ background : -ms-linear-gradient(top, rgba(241, 241, 241, 1) 0%, rgba(241, 241, 241, 0.8) 20%, rgba(241, 241, 241, 0) 100%);
41
+ background : linear-gradient(to bottom, rgba(241, 241, 241, 1) 0%, rgba(241, 241, 241, 0.8) 20%, rgba(241, 241, 241, 0) 100%);
42
  }
43
  /*
44
  Align these with menu page.
45
  */
46
  div.updated, div.error
47
  {
48
+ margin : 25px 25px 25px 5px;
 
49
  }
50
  /*
51
  These CSS selectors address common layout styles.
52
  */
53
  div.ws-menu-page
54
  {
55
+ font-size : 14px;
56
+ min-width : 800px;
57
+ margin : 25px 25px 25px 5px;
 
 
 
 
 
 
58
  }
59
  div.ws-menu-page p,
60
  div.ws-menu-page code
63
  }
64
  div.ws-menu-page a
65
  {
66
+ color : #336695;
67
  }
68
  div.ws-menu-page a:hover,
69
  div.ws-menu-page a:active
93
  {
94
  font-size : 200%;
95
  font-weight : bold;
96
+ color : #05141E;
97
 
98
  height : 90px;
99
  box-sizing : border-box;
100
  position : relative;
101
  z-index : 0;
102
 
103
+ margin-bottom : 20px;
104
+ padding : 30px 0 0 120px;
105
  background : url('<?php echo $i; ?>/s2___.png') no-repeat left bottom;
106
+
107
+ text-shadow : 0 -1px 5px rgba(255, 255, 255, 0.25), 0 1px 5px rgba(255, 255, 255, 0.25), 0 2px 5px rgba(0, 0, 0, 0.3);
108
  }
109
  div.ws-menu-page > h2:before
110
  {
133
  z-index : 0;
134
  position : absolute;
135
 
136
+ background : url('<?php echo $i; ?>/s2___end.png') no-repeat right bottom;
137
  }
138
  div.ws-menu-page code
139
  {
334
  }
335
  div.ws-menu-page input::-webkit-input-placeholder
336
  {
337
+ padding-top : .1em;
338
  font-style : italic;
339
  color : rgba(255, 255, 255, 0.2);
340
  }
341
  div.ws-menu-page input::-moz-placeholder
342
  {
343
+ padding-top : .1em;
344
  font-style : italic;
345
  color : rgba(255, 255, 255, 0.2);
346
  }
347
  div.ws-menu-page input:-moz-placeholder
348
  {
349
+ padding-top : .1em;
350
  font-style : italic;
351
  color : rgba(255, 255, 255, 0.2);
352
  }
353
  div.ws-menu-page input:-ms-input-placeholder
354
  {
355
+ padding-top : .1em;
356
  font-style : italic;
357
  color : rgba(255, 255, 255, 0.2);
358
  }
430
  div.ws-menu-page .error
431
  {
432
  border-radius : 5px;
 
433
  padding : 10px;
434
  margin : 0 0 25px 0;
435
  }
485
  line-height : 1em;
486
  font-size : 150%;
487
  font-weight : bold;
488
+ color : #627689;
489
+ background : #05141E;
490
 
491
  border-radius : 4px;
492
  border : 1px solid rgba(0, 0, 0, 0.5);
510
  div.ws-menu-page div.ws-menu-page-groups-show:hover,
511
  div.ws-menu-page div.ws-menu-page-groups-hide:hover
512
  {
513
+ color : #FFFFFF;
514
  }
515
  div.ws-menu-page div.ws-menu-page-groups-show:active,
516
  div.ws-menu-page div.ws-menu-page-groups-hide:active
520
  }
521
  div.ws-menu-page div.ws-menu-page-toolbox
522
  {
523
+ float : right;
524
+ max-width : 320px;
525
 
526
+ z-index : 1;
527
+ position : relative;
528
+ margin-right : 25px;
529
  }
530
  div.ws-menu-page div.ws-menu-page-toolbox > .links
531
  {
602
  top : 0;
603
  right : 0;
604
 
605
+ color : #627689;
606
+ background : #053769;
607
  border-radius : 3px;
608
 
609
  cursor : pointer;
613
  }
614
  table.ws-menu-page-table td.ws-menu-page-table-r > .toggler:hover
615
  {
616
+ color : #FFFFFF;
617
+ background-color : #05141E;
618
  }
619
  table.ws-menu-page-table td.ws-menu-page-table-r > .wrapper
620
  {
680
  }
681
  table.ws-menu-page-table p.submit
682
  {
 
683
  padding : 0;
684
+ margin : 20px 0 0 0;
685
  }
686
  table.ws-menu-page-table a[rel~="external"]
687
  {
698
  font-size : 150%;
699
  font-weight : bold;
700
  line-height : 1em;
701
+ color : #DDDDDD;
702
+ background : #05141E;
703
 
704
  border-radius : 5px;
705
  box-shadow : 0 2px 2px 0 rgba(0, 0, 0, 0.25);
706
 
707
+ margin-bottom : 15px;
708
 
709
  cursor : pointer;
710
  -webkit-user-select : none;
716
  table.ws-menu-page-table div.ws-menu-page-group-header.open,
717
  table.ws-menu-page-table div.ws-menu-page-r-group-header.open
718
  {
719
+ color : #FFFFFF;
 
720
  }
721
  table.ws-menu-page-table div.ws-menu-page-group-header.open,
722
  table.ws-menu-page-table div.ws-menu-page-r-group-header.open
723
  {
724
  margin-bottom : 0;
725
  }
726
+ table.ws-menu-page-table div.ws-menu-page-group-header::after,
727
+ table.ws-menu-page-table div.ws-menu-page-r-group-header::after
728
  {
729
+ color : #627689;
730
+
731
  font-size : 80%;
732
  content : '\f078';
733
  font-weight : normal;
737
  margin : 0 0 0 10px;
738
  display : inline-block;
739
  }
740
+ table.ws-menu-page-table div.ws-menu-page-group-header:hover::after,
741
+ table.ws-menu-page-table div.ws-menu-page-group-header.open::after,
742
+ table.ws-menu-page-table div.ws-menu-page-r-group-header:hover::after,
743
+ table.ws-menu-page-table div.ws-menu-page-r-group-header.open::after
744
+ {
745
+ color : #FFFFFF;
746
+ }
747
+ table.ws-menu-page-table div.ws-menu-page-group-header.open::after,
748
+ table.ws-menu-page-table div.ws-menu-page-r-group-header.open::after
749
  {
750
  content : '\f077';
751
  }
760
  position : relative;
761
  display : none;
762
 
763
+ padding : 15px;
764
  color : #333333;
765
  background : #D0DDAD;
766
 
773
  table.ws-menu-page-table div.ws-menu-page-r-group.open
774
  {
775
  display : block;
776
+ margin-bottom : 15px;
777
  }
778
  table.ws-menu-page-table div.ws-menu-page-group div.ws-menu-page-hr,
779
  table.ws-menu-page-table div.ws-menu-page-r-group div.ws-menu-page-hr
852
  background : #73834B;
853
  box-shadow : 0 0 1px 0 rgba(0, 0, 0, 0.5) inset, 0 0 1px 1px rgba(223, 245, 165, 0.5);
854
  }
855
+ table.ws-menu-page-table div.ws-menu-page-group table.form-table > tbody > tr > td > div.ws-menu-page-scrollbox a
856
+ {
857
+ color : #FFFFFF;
858
+ }
859
  /*
860
  Coupons table.
861
  */
includes/menu-pages/mms-ops.inc.php CHANGED
@@ -44,7 +44,7 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_mms_ops"))
44
 
45
  if(is_multisite() && is_main_site()) // These panels will ONLY be available on the Main Site; with Multisite Networking.
46
  {
47
- echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form">'."\n";
48
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
49
  echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
50
 
@@ -131,20 +131,20 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_mms_ops"))
131
  echo '<div class="ws-menu-page-section ws-plugin--s2member-mms-registration-section">'."\n";
132
  echo '<img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/large-icon.png" title="s2Member (a Membership management system for WordPress)" alt="" style="float:right; margin:0 0 0 25px; border:0;" />'."\n";
133
  echo '<h3>Multisite Registration (Main Site Configuration)</h3>'."\n";
134
- echo '<p>s2Member supports Free Subscribers <em>(at Level #0)</em>, and several Primary Roles created by the s2Member plugin (<em> i.e. s2Member Levels 1-4, or up to the number of configured Levels )</em>. If you want your visitors to be capable of registering absolutely free, you will want to "allow" Open Registration. Whenever a visitor registers without paying, they\'ll automatically become a Free Subscriber, at Level #0.</p>'."\n";
135
  echo '<p><strong>Running A Multisite Blog Farm?</strong> With Multisite Networking enabled, your Main Site could ALSO offer a Customer access to create a Blog of their own <em>(optional)</em>, where a Customer becomes a "Member" of your Main Site, and also a Blog Owner/Administrator of at least one other Blog on your Network. With s2Member installed <em>(Network wide)</em>, each of your Blog Owners could offer Membership too, using a single copy of the s2Member plugin, which is a great selling point<em>!</em> We refer to this type of installation as a Multisite Blog Farm.</p>'."\n";
136
- echo '<p>Multisite Networking makes a new Registration Form available <em>(driven by your theme)</em>; which we refer to as: <code>/wp-signup.php</code>. If, and only if, you\'re planning to offer Blogs, you MUST use <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Multisite Registration Form.\\n* s2Member makes this form available to logged-in Super Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">/wp-signup.php</a>, instead of using the Standard Login/Registration Form. In a Multisite installation, we refer to the Standard Login/Registration Form, as: <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">/wp-login.php?action=register</a>. If you\'re planning to offer Membership Access only, and NOT Blogs, you can simply use the <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">Standard Login/Registration Form</a>, which is easily customized through <code>s2Member -› General Options -› Login/Registration Design</code>.</p>'."\n";
137
- echo '<p>In either case, s2Member Pro Forms are possible too. If you\'ve purchased s2Member Pro, you could use Pro Forms instead of these WordPress defaults. That being said, even with s2Member Pro Forms, if you are offering Blogs, you will still need to facilitate the actual creation of each Blog through <code>/wp-signup.php</code>. In other words, Customers can register through s2Member Pro Forms, and even checkout. But when it comes time to setup a new Blog, you will need to redirect your Customer to <code>/wp-signup.php</code>, while they are logged-in. This will allow them to create a new Blog on your Network. That is, if they are allowed to <em>(based on your configuration below)</em>.</p>'."\n";
138
  echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form, powered by your theme.<br />(BuddyPress can handle both Membership and Blog creation in its integration)<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.\');">'.esc_html(c_ws_plugin__s2member_utils_urls::bp_register_url()).'</a>)</em></p>'."\n" : '';
139
  do_action("ws_plugin__s2member_during_mms_ops_page_during_left_sections_during_mms_registration", get_defined_vars());
140
 
141
  echo '<div id="ws-plugin--s2member-mms-registration-support-package-details-wrapper">'."\n";
142
  echo '<h4 style="margin-bottom:0;">Running a Multisite Blog Farm? (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-mms-registration-support-package-details\').toggle(); return false;" class="ws-dotted-link">click here / please read</a>)</h4>'."\n";
143
  echo '<div id="ws-plugin--s2member-mms-registration-support-package-details" style="display:none;">'."\n";
144
- echo '<p>The most important thing to do when setting up a Blog Farm with s2Member, is to add this line to your <code>/wp-config.php</code> file: <code><span style="color:#0000BB;">define</span><span style="color:#007700;">(</span><span style="color:#DD0000;">"MULTISITE_FARM"</span>, <span style="color:#0000BB;">true</span><span style="color:#007700;">);</span></code>. This will add a default layer of security, to all Blogs within your Network, with respect to s2Member. <strong>But, before you go live</strong>, please contact <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Module / Prices")).'" target="_blank" rel="external">s2Member.com</a> for full documentation. There is some additional functionality that can be enabled for security on a Blog Farm installation; and also some menus/documentation/functionality that can be disabled. You will be asked to purchase our <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Module / Prices")).'" target="_blank" rel="external">Network Support Package</a> when you need assistance in this regard.</p>'."\n";
145
- echo '<p>Multisite Blog Farms require a site owner that fully understands the potential security risks associated with Blog Farming. s2Member\'s <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Module / Prices")).'" target="_blank" rel="external">Network Support Package</a> provides you with the information you need, and priority support for anything about s2Member that you don\'t understand. In addition, our Network Support Package includes a lengthy PDF file that details a list of things affected by <code><span style="color:#0000BB;">define</span><span style="color:#007700;">(</span><span style="color:#DD0000;">"MULTISITE_FARM"</span>, <span style="color:#0000BB;">true</span><span style="color:#007700;">);</span></code>, best practices, and other supplemental documentation focused on Blog Farms.</p>'."\n";
146
- echo '<p><em><strong>Definition of a Multisite Blog Farm:</strong> If your Network is making it possible for "Members" of your Main Site, to create and/or manage Blogs (in any way), s2Member will consider your installation to be a Multisite Blog Farm. That being said, some site owners run a Multisite Network for the purpose of maintaining their own sites. The term Multisite Blog Farm does NOT apply to a Network that hosts multiple Child Blogs, all of which are operated by a single site owner and/or a single company. Again, a Multisite Blog Farm (in the eyes of s2Member), is any Network that is making it possible for "Members" of its Main Site, to create and/or manage Blogs; where one or more of these Child Blogs is being administered by a Customer (i.e. if you offer both Membership and Blog creation, as configured below).</em></p>'."\n";
147
- echo '<p><em><strong>When NOT to run a Multisite Blog Farm:</strong> If you run a Multisite Network for the purpose of maintaining your own sites. You should NOT run a Multisite Blog Farm. You can still activate s2Member Network-wide, if you like (optional), but the advanced security considerations offered through s2Member\'s Multisite Blog Farm functionality are NOT needed in this case; because all of the Child Blogs in your Network belong to trusted Administrators (i.e. your Customers are NOT going to run Child Blogs on your Network in this case).</em></p>'."\n";
148
  echo '</div>'."\n";
149
  echo '</div>'."\n";
150
 
@@ -298,16 +298,14 @@ if(!class_exists("c_ws_plugin__s2member_menu_page_mms_ops"))
298
  }
299
  do_action("ws_plugin__s2member_during_mms_ops_page_after_left_sections", get_defined_vars());
300
 
301
- echo '<div class="ws-menu-page-hr"></div>'."\n";
302
-
303
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
304
 
305
  echo '</form>'."\n";
306
  }
307
  else // Otherwise, we can display a simple notation; leading into Multisite Networking.
308
  {
309
- echo '<p style="margin-top:0;"><span class="ws-menu-page-hilite">Your WordPress installation does not have Multisite Networking enabled.<br />Which is perfectly OK :-) Multisite Networking is 100% completely optional.</span></p>'."\n";
310
- echo '<img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/large-icon.png" title="s2Member (a Membership management system for WordPress)" alt="" style="float:right; margin:0 0 0 25px; border:0;" />'."\n";
311
 
312
  if(file_exists($ws_plugin__s2member_temp = dirname(dirname(dirname(__FILE__)))."/readme-ms.txt"))
313
  {
44
 
45
  if(is_multisite() && is_main_site()) // These panels will ONLY be available on the Main Site; with Multisite Networking.
46
  {
47
+ echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" autocomplete="off">'."\n";
48
  echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
49
  echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
50
 
131
  echo '<div class="ws-menu-page-section ws-plugin--s2member-mms-registration-section">'."\n";
132
  echo '<img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/large-icon.png" title="s2Member (a Membership management system for WordPress)" alt="" style="float:right; margin:0 0 0 25px; border:0;" />'."\n";
133
  echo '<h3>Multisite Registration (Main Site Configuration)</h3>'."\n";
134
+ echo '<p>s2Member supports Free Subscribers <em>(at Level #0)</em>, and several Primary Roles created by the s2Member plugin (<em> i.e., s2Member Levels 1-4, or up to the number of configured Levels )</em>. If you want your visitors to be capable of registering absolutely free, you will want to "allow" Open Registration. Whenever a visitor registers without paying, they\'ll automatically become a Free Subscriber, at Level #0.</p>'."\n";
135
  echo '<p><strong>Running A Multisite Blog Farm?</strong> With Multisite Networking enabled, your Main Site could ALSO offer a Customer access to create a Blog of their own <em>(optional)</em>, where a Customer becomes a "Member" of your Main Site, and also a Blog Owner/Administrator of at least one other Blog on your Network. With s2Member installed <em>(Network wide)</em>, each of your Blog Owners could offer Membership too, using a single copy of the s2Member plugin, which is a great selling point<em>!</em> We refer to this type of installation as a Multisite Blog Farm.</p>'."\n";
136
+ echo '<p>Multisite Networking makes a new Registration Form available <em>(driven by your theme)</em>; which we refer to as: <code>/wp-signup.php</code>. If, and only if, you\'re planning to offer Blogs, you MUST use <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_signup_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Multisite Registration Form.\\n* s2Member makes this form available to logged-in Super Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">/wp-signup.php</a>, instead of using the Standard Login/Registration Form. In a Multisite installation, we refer to the Standard Login/Registration Form, as: <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">/wp-login.php?action=register</a>. If you\'re planning to offer Membership Access only, and NOT Blogs, you can simply use the <a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::wp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your Standard Registration Form.\\n* s2Member makes this form available to logged-in Administrators, at all times (for testing purposes), regardless of configuration.'.((c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '\\n\\nBuddyPress: * BuddyPress will use its own Registration Form. Please note, you will probably be redirected away from the BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.' : '').'\');">Standard Login/Registration Form</a>, which is easily customized through <strong>s2Member General Options Login/Registration Design</strong>.</p>'."\n";
137
+ echo '<p>In either case, s2Member Pro-Forms are possible too. If you\'ve purchased s2Member Pro, you could use Pro-Forms instead of these WordPress defaults. That being said, even with s2Member Pro-Forms, if you are offering Blogs, you will still need to facilitate the actual creation of each Blog through <code>/wp-signup.php</code>. In other words, Customers can register through s2Member Pro-Forms, and even checkout. But when it comes time to setup a new Blog, you will need to redirect your Customer to <code>/wp-signup.php</code>, while they are logged-in. This will allow them to create a new Blog on your Network. That is, if they are allowed to <em>(based on your configuration below)</em>.</p>'."\n";
138
  echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '<p><em><strong>BuddyPress:</strong> BuddyPress will use its own Registration Form, powered by your theme.<br />(BuddyPress can handle both Membership and Blog creation in its integration)<br />(<a href="'.esc_attr(c_ws_plugin__s2member_utils_urls::bp_register_url()).'" target="_blank" rel="external" onclick="alert(\'s2Member will now open your BuddyPress Registration Form.\\n* However, you will probably be redirected away from this BuddyPress Registration Form ( '.c_ws_plugin__s2member_utils_strings::esc_js_sq(c_ws_plugin__s2member_utils_urls::bp_register_url()).' ), because you\\\'re ALREADY logged-in. Please log out before testing BuddyPress registration.\');">'.esc_html(c_ws_plugin__s2member_utils_urls::bp_register_url()).'</a>)</em></p>'."\n" : '';
139
  do_action("ws_plugin__s2member_during_mms_ops_page_during_left_sections_during_mms_registration", get_defined_vars());
140
 
141
  echo '<div id="ws-plugin--s2member-mms-registration-support-package-details-wrapper">'."\n";
142
  echo '<h4 style="margin-bottom:0;">Running a Multisite Blog Farm? (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-mms-registration-support-package-details\').toggle(); return false;" class="ws-dotted-link">click here / please read</a>)</h4>'."\n";
143
  echo '<div id="ws-plugin--s2member-mms-registration-support-package-details" style="display:none;">'."\n";
144
+ echo '<p>The most important thing to do when setting up a Blog Farm with s2Member, is to add this line to your <code>/wp-config.php</code> file: <code><span style="color:#0000BB;">define</span><span style="color:#007700;">(</span><span style="color:#DD0000;">"MULTISITE_FARM"</span>, <span style="color:#0000BB;">true</span><span style="color:#007700;">);</span></code>. This will add a default layer of security, to all Blogs within your Network, with respect to s2Member. <strong>But, before you go live</strong>, please contact <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Add-on / Prices")).'" target="_blank" rel="external">s2Member.com</a> for full documentation. There is some additional functionality that can be enabled for security on a Blog Farm installation; and also some menus/documentation/functionality that can be disabled. You will be asked to purchase our <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Add-on / Prices")).'" target="_blank" rel="external">Network Support Package</a> when you need assistance in this regard.</p>'."\n";
145
+ echo '<p>Multisite Blog Farms require a site owner that fully understands the potential security risks associated with Blog Farming. s2Member\'s <a href="'.esc_attr(c_ws_plugin__s2member_readmes::parse_readme_value("Pro Add-on / Prices")).'" target="_blank" rel="external">Network Support Package</a> provides you with the information you need, and priority support for anything about s2Member that you don\'t understand. In addition, our Network Support Package includes a lengthy PDF file that details a list of things affected by <code><span style="color:#0000BB;">define</span><span style="color:#007700;">(</span><span style="color:#DD0000;">"MULTISITE_FARM"</span>, <span style="color:#0000BB;">true</span><span style="color:#007700;">);</span></code>, best practices, and other supplemental documentation focused on Blog Farms.</p>'."\n";
146
+ echo '<p><em><strong>Definition of a Multisite Blog Farm:</strong> If your Network is making it possible for "Members" of your Main Site, to create and/or manage Blogs (in any way), s2Member will consider your installation to be a Multisite Blog Farm. That being said, some site owners run a Multisite Network for the purpose of maintaining their own sites. The term Multisite Blog Farm does NOT apply to a Network that hosts multiple Child Blogs, all of which are operated by a single site owner and/or a single company. Again, a Multisite Blog Farm (in the eyes of s2Member), is any Network that is making it possible for "Members" of its Main Site, to create and/or manage Blogs; where one or more of these Child Blogs is being administered by a Customer (i.e., if you offer both Membership and Blog creation, as configured below).</em></p>'."\n";
147
+ echo '<p><em><strong>When NOT to run a Multisite Blog Farm:</strong> If you run a Multisite Network for the purpose of maintaining your own sites. You should NOT run a Multisite Blog Farm. You can still activate s2Member Network-wide, if you like (optional), but the advanced security considerations offered through s2Member\'s Multisite Blog Farm functionality are NOT needed in this case; because all of the Child Blogs in your Network belong to trusted Administrators (i.e., your Customers are NOT going to run Child Blogs on your Network in this case).</em></p>'."\n";
148
  echo '</div>'."\n";
149
  echo '</div>'."\n";
150
 
298
  }
299
  do_action("ws_plugin__s2member_during_mms_ops_page_after_left_sections", get_defined_vars());
300
 
 
 
301
  echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
302
 
303
  echo '</form>'."\n";
304
  }
305
  else // Otherwise, we can display a simple notation; leading into Multisite Networking.
306
  {
307
+ echo '<p style="margin-top:0;">Your WordPress installation does not have Multisite Networking enabled.<br />Which is perfectly OK :-) Multisite Networking is 100% completely optional.</p>'."\n";
308
+ echo '<img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/images/large-icon.png" title="s2Member (a Membership management system for WordPress)" alt="" style="float:right; margin:25px 0 0 25px; border:0;" />'."\n";
309
 
310
  if(file_exists($ws_plugin__s2member_temp = dirname(dirname(dirname(__FILE__)))."/readme-ms.txt"))
311
  {
includes/menu-pages/paypal-buttons.inc.php CHANGED
@@ -73,12 +73,12 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
73
  echo '</th>' . "\n";
74
 
75
  echo '<td>' . "\n";
76
- echo '<form onsubmit="return false;">' . "\n";
77
  echo '<p id="ws-plugin--s2member-level' . $n . '-trial-line">I\'ll offer the first <input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-trial-period" value="0" size="6" /> <select id="ws-plugin--s2member-level' . $n . '-trial-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-trial-terms.php"))) . '</select> @ $<input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-trial-amount" value="0.00" size="4" /></p>' . "\n";
78
  echo '<p><span id="ws-plugin--s2member-level' . $n . '-trial-then">Then, </span>I want to charge: $<input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-amount" value="0.01" size="4" /> / <select id="ws-plugin--s2member-level' . $n . '-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-regular-terms.php"))) . '</select></p>' . "\n";
79
  echo '<p>Checkout Page Style <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can enter that Page Style here.\\n\\nIn addition. The Shortcode below, provided by s2Member; supports an image attribute: image=\\\'\\\'default\\\'\\\'. This can be changed to a full URL, pointing to a custom image of your own; instead of the default PayPal Button image.\'); return false;" tabindex="-1">[?]</a>: <input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-page-style" value="paypal" size="18" /> <select id="ws-plugin--s2member-level' . $n . '-currency">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-currencies.php"))) . '</select> <input type="button" value="Generate Button Code" onclick="ws_plugin__s2member_paypalButtonGenerate(\'level' . $n . '\');" /></p>' . "\n";
80
  echo '<p>Description: <input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-desc" value="' . format_to_edit ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level" . $n . "_label"]) . ' / description and pricing details here." size="73" /></p>' . "\n";
81
- echo '<p' . ((is_multisite () && c_ws_plugin__s2member_utils_conds::is_multisite_farm () && !is_main_site ()) ? ' style="display:none;"' : '') . '>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member -› API Scripting -› Custom Capabilities.\\n\\n*ADVANCED TIP: You can specifiy a list of Custom Capabilities that will be (Added) with this purchase. Or, you could tell s2Member to (Remove All) Custom Capabilities that may or may not already exist for a particular Member, and (Add) only the new ones that you specify. To do this, just start your list of Custom Capabilities with `-all`.\\n\\nSo instead of just (Adding) Custom Capabilities:\\nmusic,videos,archives,gifts\\n\\nYou could (Remove All) that may already exist, and then (Add) new ones:\\n-all,calendar,forums,tools\\n\\nOr to just (Remove All) and (Add) nothing:\\n-all\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-ccaps" size="40" /></p>' . "\n";
82
  echo '</form>' . "\n";
83
  echo '</td>' . "\n";
84
 
@@ -86,7 +86,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
86
  echo '<tr>' . "\n";
87
 
88
  echo '<td colspan="2">' . "\n";
89
- echo '<form onsubmit="return false;">' . "\n";
90
 
91
  if (($ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_levelN_buttons_before_shortcode = "ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_level" . $n . "_buttons_before_shortcode"))
92
  do_action($ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_levelN_buttons_before_shortcode, get_defined_vars ());
@@ -142,12 +142,12 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
142
  echo '<div class="ws-menu-page-section ws-plugin--s2member-modification-buttons-section">' . "\n";
143
  echo '<h3>Button Code Generator For Subscription Modifications</h3>' . "\n";
144
  echo '<p>If you\'d like to give your Members <em>(and/or your Free Subscribers)</em> the ability to modify their billing plan, by switching to a more expensive option, or a less expensive option; generate a new PayPal Modification Button here. Configure the updated Level, pricing, terms, etc. Then, make that new Modification Button available to Members who are logged into their existing account with you. For example, you might want to insert a "Level #2" Upgrade Button into your Login Welcome Page, which would up-sell existing Level #1 Members to a more expensive plan that you offer.</p>' . "\n";
145
- echo '<p><em><strong>*Important Note*</strong> Modification Buttons should be displayed to existing Users/Members, and they should be logged-in, BEFORE clicking this Button. Otherwise, post-processing of their transaction will fail to recognize the Customer\'s existing account within WordPress. Please display this Button only to Users/Members that are already logged into their account (perhaps in your Login Welcome Page for s2Member), or in another location where you can be absolutely sure that a User/Member is logged in. s2Member\'s Simple Conditionals could also be used to ensure a User/Member is logged in, by wrapping your Shortcode within a Conditional test. For further details, please see: <code>s2Member -› API Scripting -› Simple Conditionals</code>.</em></p>' . "\n";
146
- echo '<p><em><strong>*Modification Process*</strong> When you send a Member to PayPal using a Subscription Modification Button, PayPal will ask them to login. Once they\'re logged in, instead of being able to signup for a new Membership, PayPal will provide them with the ability to upgrade and/or downgrade their existing Membership with you, by allowing them to switch to the Membership Plan that was specified in the Subscription Modification Button. PayPal handles this nicely, and you\'ll be happy to know that s2Member has been pre-configured to deal with this scenario as well, so that everything remains automated. Their Membership Access Level will either be promoted, or demoted, based on the actions they took at PayPal during the modification process. Once an existing Member completes their Subscription Modification at PayPal, they\'ll be brought back to their Login Welcome Page, instead of to the registration screen.</em></p>' . "\n";
147
- echo '<p><em><strong>*Also Works For Free Subscribers*</strong> Although a Free Subscriber does not have an existing PayPal Subscription, s2Member is capable of adapting to this scenario gracefully. Just make sure that your existing Free Subscribers <em>(the ones who wish to upgrade)</em> pay for their Membership through a Modification Button generated by s2Member. That will allow them to continue using their existing account with you. In other words, they can keep their existing Username <em>(and anything already associated with that Username)</em>, rather than being forced to re-register after checkout.</em></p>' . "\n";
148
- echo '<p><em><strong>*Make It More User-Friendly*</strong> You can make the Subscription Modification Process, more user-friendly, by setting up a <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can tell s2Member to use that Page Style whenever you generate your Button Code.\'); return false;">Custom Page Style at PayPal</a>, specifically for Subscription Modification Buttons. Use a custom header image, with a brief explanation to the Customer. Something like, "Log into PayPal", "You can Modify your Subscription!".</em></p>' . "\n";
149
- echo '<p><em><strong>*Integrating Conditionals*</strong> Since each Modification Button is configured for a specific Level, you may want to create multiple Modification Buttons, one for each combination you intend to make available. s2Member\'s API Conditionals can help you display the proper Button to each Customer, based on the status of their existing account. For further details, see: <code>s2Member -› API Scripting</code>.</em></p>' . "\n";
150
- echo (!is_multisite () || !c_ws_plugin__s2member_utils_conds::is_multisite_farm () || is_main_site ()) ? '<p><em><strong>*Independent Custom Capabilities*</strong> If you just want to sell an existing Member new Custom Capabilities, without affecting their paid Subscription in any way, please see the next Button Generator: <code>Capability (Buy Now) Buttons</code>. Independent Capability Buttons facilitate Buy Now functionality, specifically for Custom Capabilities, without affecting the Customer\'s primary Subscription and Membership Level Access.</em></p>' . "\n" : '';
151
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_modification_buttons", get_defined_vars ());
152
 
153
  echo '<table class="form-table">' . "\n";
@@ -162,7 +162,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
162
  echo '</th>' . "\n";
163
 
164
  echo '<td>' . "\n";
165
- echo '<form onsubmit="return false;">' . "\n";
166
 
167
  echo '<p>Modification: <select id="ws-plugin--s2member-modification-level">' . "\n";
168
 
@@ -182,7 +182,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
182
  echo '<p><span id="ws-plugin--s2member-modification-trial-then">Then, </span>I want to charge: $<input type="text" autocomplete="off" id="ws-plugin--s2member-modification-amount" value="0.01" size="4" /> / <select id="ws-plugin--s2member-modification-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-regular-terms.php"))) . '</select></p>' . "\n";
183
  echo '<p>Checkout Page Style <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can enter that Page Style here.\\n\\nIn addition. The Shortcode below, provided by s2Member; supports an image attribute: image=\\\'\\\'default\\\'\\\'. This can be changed to a full URL, pointing to a custom image of your own; instead of the default PayPal Button image.\'); return false;" tabindex="-1">[?]</a>: <input type="text" autocomplete="off" id="ws-plugin--s2member-modification-page-style" value="paypal" size="18" /> <select id="ws-plugin--s2member-modification-currency">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-currencies.php"))) . '</select> <input type="button" value="Generate Button Code" onclick="ws_plugin__s2member_paypalButtonGenerate(\'modification\');" /></p>' . "\n";
184
  echo '<p>Description: <input type="text" autocomplete="off" id="ws-plugin--s2member-modification-desc" value="Description and pricing details here." size="73" /></p>' . "\n";
185
- echo '<p' . ((is_multisite () && c_ws_plugin__s2member_utils_conds::is_multisite_farm () && !is_main_site ()) ? ' style="display:none;"' : '') . '>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member -› API Scripting -› Custom Capabilities.\\n\\n*ADVANCED TIP: You can specifiy a list of Custom Capabilities that will be (Added) with this purchase. Or, you could tell s2Member to (Remove All) Custom Capabilities that may or may not already exist for a particular Member, and (Add) only the new ones that you specify. To do this, just start your list of Custom Capabilities with `-all`.\\n\\nSo instead of just (Adding) Custom Capabilities:\\nmusic,videos,archives,gifts\\n\\nYou could (Remove All) that may already exist, and then (Add) new ones:\\n-all,calendar,forums,tools\\n\\nOr to just (Remove All) and (Add) nothing:\\n-all\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-modification-ccaps" size="40" /></p>' . "\n";
186
  echo '</form>' . "\n";
187
  echo '</td>' . "\n";
188
 
@@ -190,7 +190,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
190
  echo '<tr>' . "\n";
191
 
192
  echo '<td colspan="2">' . "\n";
193
- echo '<form onsubmit="return false;">' . "\n";
194
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_modification_buttons_before_shortcode", get_defined_vars ());
195
  echo '<strong>WordPress Shortcode:</strong> (recommended for both the WordPress Visual &amp; HTML Editors)<br />' . "\n";
196
  $ws_plugin__s2member_temp_s = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/shortcodes/paypal-checkout-button-shortcode.php")));
@@ -242,11 +242,11 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
242
 
243
  echo '<div class="ws-menu-page-section ws-plugin--s2member-ccap-buttons-section">' . "\n";
244
  echo '<h3>Button Code Generator For Independent Custom Capabilities</h3>' . "\n";
245
- echo '<p>This is VERY advanced. For further details, please check your Dashboard: <code>s2Member -› API Scripting -› Custom Capabiities</code>.</p>' . "\n";
246
- echo '<p>With s2Member, you can sell one or more Custom Capabilities using Buy Now functionality, to "existing" Users/Members, regardless of which Membership Level they have on your site <em>(i.e. you could even sell Independent Custom Capabilities to Users at Membership Level #0, normally referred to as Free Subscribers, if you like)</em>. So this is quite flexible. Independent Custom Capabilities do NOT rely on any specific Membership Level. That\'s why s2Member refers to these as `Independent` Custom Capabilities, because you can sell Capabilities this way, through Buy Now functionality, and the Customer\'s Membership Level Access, along with any existing paid Subscription they may already have with you, will remain completely unaffected. That being said, if you intend to charge a recurring fee for Custom Capabilities, please use a <code>Subscr. Modification Button</code> instead; because Independent Custom Capabilities can only be sold through Buy Now functionality.</p>' . "\n";
247
  echo '<p>Independent Custom Capabilities are added to a Customer\'s account immediately after checkout, and the Customer will have the Custom Capabilities for as long as their Membership lasts, based on their primary Subscription with your site, and/or forever, if they have a Lifetime account with you. In other words, Independent Custom Capabilities will exist on the Customer\'s account forever, or until an EOT <em>(End Of Term)</em> occurs on their primary Subscription with you; in which case s2Member would demote or delete the Customer\'s account <em>(based on your EOT configuration)</em>, and all Custom Capabilities are removed as well.</p>' . "\n";
248
  echo '<p>Very simple. All you do is customize the form fields provided, for each set of Custom Capabilities that you plan to sell. Then press (Generate Button Code). These special PayPal Buttons are customized to work with s2Member seamlessly. The Customer will be granted additional access to one or more Custom Capabilities that you specify; while the Customer\'s Membership Level Access and any existing paid Subscription they may already have with you, will remain completely unaffected.</p>' . "\n";
249
- echo '<p><em><strong>*Important Note*</strong> Independent Custom Capability Buttons should ONLY be displayed to existing Users/Members, and they MUST be logged-in, BEFORE clicking this Button. Otherwise, post-processing of their transaction will fail to recognize the Customer\'s existing account within WordPress. Please display this Button only to Users/Members that are already logged into their account (perhaps in your Login Welcome Page for s2Member), or in another location where you can be absolutely sure that a User/Member is logged in. s2Member\'s Simple Conditionals could also be used to ensure a User/Member is logged in, by wrapping your Shortcode within a Conditional test. For further details, please see: <code>s2Member -› API Scripting -› Simple Conditionals</code>.</em></p>' . "\n";
250
  echo '<p><em><strong>Please note:</strong> buttons are NOT saved here. This is only a Button Generator. Once you\'ve generated your Button, copy/paste it into your WordPress Editor. If you lose your Button Code, you\'ll need to come back &amp; re-generate a new one. If you\'re in Sandbox Test-Mode, and you\'re NOT using the Shortcode Format, please remember to come back and re-generate your Buttons before you go live.</em></p>' . "\n";
251
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_ccap_buttons", get_defined_vars ());
252
 
@@ -262,11 +262,11 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
262
  echo '</th>' . "\n";
263
 
264
  echo '<td>' . "\n";
265
- echo '<form onsubmit="return false;">' . "\n";
266
  echo '<p>I want to charge: $<input type="text" autocomplete="off" id="ws-plugin--s2member-ccap-amount" value="0.01" size="4" /> / <select id="ws-plugin--s2member-ccap-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-ccap-terms.php"))) . '</select></p>' . "\n";
267
  echo '<p>Checkout Page Style <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can enter that Page Style here.\\n\\nIn addition. The Shortcode below, provided by s2Member; supports an image attribute: image=\\\'\\\'default\\\'\\\'. This can be changed to a full URL, pointing to a custom image of your own; instead of the default PayPal Button image.\'); return false;" tabindex="-1">[?]</a>: <input type="text" autocomplete="off" id="ws-plugin--s2member-ccap-page-style" value="paypal" size="18" /> <select id="ws-plugin--s2member-ccap-currency">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-currencies.php"))) . '</select> <input type="button" value="Generate Button Code" onclick="ws_plugin__s2member_paypalCcapButtonGenerate();" /></p>' . "\n";
268
  echo '<p>Description: <input type="text" autocomplete="off" id="ws-plugin--s2member-ccap-desc" value="Description and pricing details here." size="73" /></p>' . "\n";
269
- echo '<p>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member -› API Scripting -› Custom Capabilities.\\n\\n*ADVANCED TIP: You can specifiy a list of Custom Capabilities that will be (Added) with this purchase. Or, you could tell s2Member to (Remove All) Custom Capabilities that may or may not already exist for a particular Member, and (Add) only the new ones that you specify. To do this, just start your list of Custom Capabilities with `-all`.\\n\\nSo instead of just (Adding) Custom Capabilities:\\nmusic,videos,archives,gifts\\n\\nYou could (Remove All) that may already exist, and then (Add) new ones:\\n-all,calendar,forums,tools\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-ccap-ccaps" size="40" /></p>' . "\n";
270
  echo '</form>' . "\n";
271
  echo '</td>' . "\n";
272
 
@@ -274,7 +274,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
274
  echo '<tr>' . "\n";
275
 
276
  echo '<td colspan="2">' . "\n";
277
- echo '<form onsubmit="return false;">' . "\n";
278
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_ccap_buttons_before_shortcode", get_defined_vars ());
279
  echo '<strong>WordPress Shortcode:</strong> (recommended for both the WordPress Visual &amp; HTML Editors)<br />' . "\n";
280
  $ws_plugin__s2member_temp_s = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/shortcodes/paypal-ccaps-checkout-button-shortcode.php")));
@@ -321,9 +321,9 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
321
  echo '<div class="ws-menu-page-section ws-plugin--s2member-cancellation-buttons-section">' . "\n";
322
  echo '<h3>One Button Does It All For Cancellations (copy/paste)</h3>' . "\n";
323
  echo '<p>Since all recurring charges are associated with a PayPal Subscription; and every PayPal Subscription is associated with a PayPal Account; your Members will always have a PayPal Account of their own, which is tied to their Membership with you. So... a Member can simply log into their own PayPal account and cancel their Subscription(s) with you at anytime, all on their own. However, some Customers do not realize this. So, if you would like to make it clearer (easier) for Members to cancel their own Subscription(s), you can provide this Cancellation Button for them on your Login Welcome Page, or somewhere in the support section of your website. Note... you don\'t have to use this Cancellation Button at all, if you don\'t want to. It\'s completely optional.</p>' . "\n";
324
- echo '<p><em><strong>*Cancellation Process*</strong> Very simple. A Member clicks the Cancellation Button. PayPal asks them to log into their PayPal account. Once they\'re logged in, PayPal will display a list of all active Subscriptions they have with you. They choose which ones they want to cancel, and s2Member is notified silently behind-the-scene, through the PayPal IPN service.</em></p>' . "\n";
325
- echo '<p><em><strong>*Understanding Cancellations*</strong> It\'s important to realize that a Cancellation is not an EOT (End Of Term). All that happens during a Cancellation event, is that billing is stopped, and it\'s understood that the Customer is going to lose access, at some point in the future. This does NOT mean, that access will be revoked immediately. A separate EOT event will automatically handle a (demotion or deletion) later, at the appropriate time; which could be several days, or even a year after the Cancellation took place.</em></p>' . "\n";
326
- echo '<p><em><strong>*Some Hairy Details*</strong> There might be times whenever you notice that a Member\'s Subscription has been cancelled through PayPal... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only remove the User\'s Membership privileges when an EOT (End Of Term) is processed, a refund occurs, a chargeback occurs, or when a cancellation occurs - which would later result in a delayed Auto-EOT by s2Member.</em></p>' . "\n";
327
  echo '<p><em>s2Member will not process an EOT (End Of Term) until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will remove their Membership privileges; by either demoting them to a Free Subscriber, or deleting their account from the system (based on your configuration). s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.</em></p>' . "\n";
328
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_cancellation_buttons", get_defined_vars ());
329
 
@@ -347,7 +347,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
347
  echo '</th>' . "\n";
348
 
349
  echo '<td>' . "\n";
350
- echo '<form onsubmit="return false;">' . "\n";
351
  echo '<p>No configuration necessary.</p>' . "\n";
352
  echo '</form>' . "\n";
353
  echo '</td>' . "\n";
@@ -356,7 +356,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
356
  echo '<tr>' . "\n";
357
 
358
  echo '<td colspan="2">' . "\n";
359
- echo '<form onsubmit="return false;">' . "\n";
360
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_cancellation_buttons_before_shortcode", get_defined_vars ());
361
  echo '<strong>WordPress Shortcode:</strong> (recommended for both the WordPress Visual &amp; HTML Editors)<br />' . "\n";
362
  $ws_plugin__s2member_temp_s = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/shortcodes/paypal-cancellation-button-shortcode.php")));
@@ -397,7 +397,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
397
 
398
  echo '<div class="ws-menu-page-section ws-plugin--s2member-reg-links-section">' . "\n";
399
  echo '<h3>Registration Access Link Generator (for Customer Service)</h3>' . "\n";
400
- echo '<p>s2Member automatically generates Registration Access Links for your Customers after checkout, and also sends them a link in a Confirmation Email. However, if you ever need to deal with a Customer Service issue that requires a new Registration Access Link to be created manually, you can use this tool for that. Alternatively, you can create their account yourself/manually by going to <code>Users -› Add New</code>. Either of these methods will work fine.</p>' . "\n";
401
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_reg_links", get_defined_vars ());
402
 
403
  echo '<table class="form-table">' . "\n";
@@ -405,15 +405,15 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
405
  echo '<tr>' . "\n";
406
 
407
  echo '<td>' . "\n";
408
- echo '<form onsubmit="return false;">' . "\n";
409
  echo '<p>Paid Membership Level#: <select id="ws-plugin--s2member-reg-link-level" style="min-width:200px;">' . "\n";
410
  for ($n = 1; $n <= $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; $n++)
411
  echo '<option value="' . $n . '">s2Member Level #' . $n . '</option>' . "\n";
412
  echo '</select></p>' . "\n";
413
- echo '<p>Paid Subscr. ID: <input type="text" autocomplete="off" id="ws-plugin--s2member-reg-link-subscr-id" value="" size="50" /> <a href="#" onclick="alert(\'The Customer\\\'s Paid Subscr. ID (aka: Recurring Profile ID, Transaction ID) must be unique. This value can be obtained from inside your PayPal account under the History tab. Each paying Customer MUST be associated with a unique Paid Subscr. ID. If the Customer is NOT associated with a Paid Subscr. ID, you will need to generate a unique value for this field on your own. But keep in mind, s2Member will be unable to maintain future communication with the PayPal IPN (i.e. Notification) service if this value does not reflect a real Paid Subscr. ID that exists in your PayPal History log.\'); return false;" tabindex="-1">[?]</a></p>' . "\n";
414
  echo '<p>Custom String Value: <input type="text" autocomplete="off" id="ws-plugin--s2member-reg-link-custom" value="' . esc_attr ($_SERVER["HTTP_HOST"]) . '" size="30" /> <a href="#" onclick="alert(\'A Paid Subscription is always associated with a Custom String that is passed through the custom=\\\'\\\'' . c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"]), 3) . '\\\'\\\' attribute of your Shortcode. This Custom Value, MUST always start with your domain name. However, you can also pipe delimit additional values after your domain, if you need to.\\n\\nFor example:\n' . c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"]), 3) . '|cv1|cv2|cv3\'); return false;" tabindex="-1">[?]</a> <input type="button" value="Generate Access Link" onclick="ws_plugin__s2member_paypalRegLinkGenerate();" /> <img id="ws-plugin--s2member-reg-link-loading" src="' . esc_attr ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]) . '/images/ajax-loader.gif" alt="" style="display:none;" /></p>' . "\n";
415
- echo '<p' . ((is_multisite () && c_ws_plugin__s2member_utils_conds::is_multisite_farm () && !is_main_site ()) ? ' style="display:none;"' : '') . '>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member -› API Scripting -› Custom Capabilities.\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-reg-link-ccaps" size="40" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" /></p>' . "\n";
416
- echo '<p>Fixed Term Length (for Buy Now transactions): <input type="text" autocomplete="off" id="ws-plugin--s2member-reg-link-fixed-term" value="" size="10" /> <a href="#" onclick="alert(\'If the Customer purchased Membership through a Buy Now transaction (i.e. there is no Initial/Trial Period and no recurring charges for ongoing access), you may configure a Fixed Term Length in this field. This way the Customer\\\'s Membership Access is automatically revoked by s2Member at the appropriate time. This will be a numeric value, followed by a space, then a single letter.\\n\\nHere are some examples:\\n\\n1 D (this means 1 Day)\\n1 W (this means 1 Week)\\n1 M (this means 1 Month)\\n1 Y (this means 1 Year)\\n1 L (this means 1 Lifetime)\'); return false;">[?]</a></p>' . "\n";
417
  echo '<p id="ws-plugin--s2member-reg-link" style="display:none;"></p>' . "\n";
418
  echo '</form>' . "\n";
419
  echo '</td>' . "\n";
@@ -437,8 +437,8 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
437
  echo '<div class="ws-menu-page-section ws-plugin--s2member-sp-buttons-section">' . "\n";
438
  echo '<h3>Button Code Generator For Specific Post/Page Buttons</h3>' . "\n";
439
  echo '<p>s2Member now supports an additional layer of functionality (very powerful), which allows you to sell access to specific Posts/Pages that you\'ve created in WordPress. Specific Post/Page Access works independently from Member Level Access. That is, you can sell an unlimited number of Posts/Pages using "Buy Now" Buttons, and your Customers will NOT be required to have a Membership Account with your site in order to receive access. If they are already a Member, that\'s fine, but they won\'t need to be.</p>' . "\n";
440
- echo '<p>In other words, Customers will NOT need to login, just to receive access to the Specific Post/Page they purchased access to. s2Member will immediately redirect the Customer to the Specific Post/Page after checkout is completed successfully. An email is also sent to the Customer with a link (see: <code>s2Member -› PayPal Options -› Specific Post/Page Email</code>). Authentication is handled automatically through self-expiring links, good for 72 hours by default.</p>' . "\n";
441
- echo '<p>Specific Post/Page Access, is sort of like selling a product. Only, instead of shipping anything to the Customer, you just give them access to a specific Post/Page on your site; one that you created in WordPress. A Specific Post/Page that is protected by s2Member, might contain a download link for your eBook, access to file &amp; music downloads, access to additional support services, and the list goes on and on. The possibilities with this are endless; as long as your digital product can be delivered through access to a WordPress Post/Page that you\'ve created. To protect Specific Posts/Pages, please see: <code>s2Member -› Restriction Options -› Specific Post/Page Access</code>. Once you\'ve configured your Specific Post/Page Restrictions, those Posts/Pages will be available in the menus below.</p>' . "\n";
442
  echo '<p>Very simple. All you do is customize the form fields provided, for each Post/Page that you plan to sell. Then press (Generate Button Code). These special PayPal Buttons are customized to work with s2Member seamlessly. You can even Package Additional Posts/Pages together into one transaction.</p>' . "\n";
443
  echo '<p><em><strong>Please note:</strong> buttons are NOT saved here. This is only a Button Generator. Once you\'ve generated your Button, copy/paste it into your WordPress Editor. If you lose your Button Code, you\'ll need to come back &amp; re-generate a new one. If you\'re in Sandbox Test-Mode, and you\'re NOT using the Shortcode Format, please remember to come back and re-generate your Buttons before you go live.</em></p>' . "\n";
444
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_sp_buttons", get_defined_vars ());
@@ -455,7 +455,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
455
  echo '</th>' . "\n";
456
 
457
  echo '<td>' . "\n";
458
- echo '<form onsubmit="return false;">' . "\n";
459
 
460
  echo '<p><select id="ws-plugin--s2member-sp-leading-id">' . "\n";
461
  echo '<option value="">&mdash; Select a Leading Post/Page that you\'ve protected &mdash;</option>' . "\n";
@@ -465,7 +465,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
465
  foreach ($ws_plugin__s2member_temp_a_singulars as $ws_plugin__s2member_temp_o)
466
  echo '<option value="' . esc_attr ($ws_plugin__s2member_temp_o->ID) . '">' . esc_html ($ws_plugin__s2member_temp_o->post_title) . '</option>' . "\n";
467
 
468
- echo '</select> <a href="#" onclick="alert(\'Required. The Leading Post/Page, is what your Customers will land on after checkout.\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.\'); return false;" tabindex="-1">[?]</a></p>' . "\n";
469
 
470
  echo '<p><select id="ws-plugin--s2member-sp-additional-ids" multiple="multiple" style="height:100px;">' . "\n";
471
  echo '<optgroup label="&mdash; Package Additional Posts/Pages that you\'ve protected &mdash;">' . "\n";
@@ -473,7 +473,7 @@ if (!class_exists ("c_ws_plugin__s2member_menu_page_paypal_buttons"))
473
  foreach ($ws_plugin__s2member_temp_a_singulars as $ws_plugin__s2member_temp_o)
474
  echo '<option value="' . esc_attr ($ws_plugin__s2member_temp_o->ID) . '">' . esc_html ($ws_plugin__s2member_temp_o->post_title) . '</option>' . "\n";
475
 
476
- echo '</optgroup></select> <a href="#" onclick="alert(\'Hold down your `Ctrl` key to select multiples.\\n\\nOptional. If you include Additional Posts/Pages, Customers will still land on your Leading Post/Page; BUT, they\\\'ll ALSO have access to some Additional Posts/Pages that you\\\'ve protected. This gives you the ability to create Post/Page Packages.\\n\\nIn other words, a Customer is sold a Specific Post/Page (they\\\'ll land on your Leading Post/Page after checkout), which might contain links to some other Posts/Pages that you\\\'ve packaged together under one transaction.\\n\\nBundling Additional Posts/Pages into one Package, authenticates the Customer for access to the Additional Posts/Pages automatically (i.e. only one Access Link is needed, and s2Member generates this automatically). However, you will STILL need to design your Leading Post/Page (which is what a Customer will actually land on), with links pointing to the other Posts/Pages. This way your Customers will have clickable links to everything they\\\'ve paid for.\\n\\n*Quick Summary* s2Member sends Customers to your Leading Post/Page, and also authenticates them for access to any Additional Posts/Pages automatically. You handle it from there.\\n\\n*Tip* If there are no Posts/Pages in this menu, it\\\'s because you\\\'ve not configured s2Member for Specific Post/Page Access yet. See: s2Member -› Restriction Options -› Specific Post/Page Access.\'); return false;" tabindex="-1">[?]</a></p>' . "\n";
73
  echo '</th>' . "\n";
74
 
75
  echo '<td>' . "\n";
76
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
77
  echo '<p id="ws-plugin--s2member-level' . $n . '-trial-line">I\'ll offer the first <input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-trial-period" value="0" size="6" /> <select id="ws-plugin--s2member-level' . $n . '-trial-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-trial-terms.php"))) . '</select> @ $<input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-trial-amount" value="0.00" size="4" /></p>' . "\n";
78
  echo '<p><span id="ws-plugin--s2member-level' . $n . '-trial-then">Then, </span>I want to charge: $<input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-amount" value="0.01" size="4" /> / <select id="ws-plugin--s2member-level' . $n . '-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-regular-terms.php"))) . '</select></p>' . "\n";
79
  echo '<p>Checkout Page Style <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can enter that Page Style here.\\n\\nIn addition. The Shortcode below, provided by s2Member; supports an image attribute: image=\\\'\\\'default\\\'\\\'. This can be changed to a full URL, pointing to a custom image of your own; instead of the default PayPal Button image.\'); return false;" tabindex="-1">[?]</a>: <input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-page-style" value="paypal" size="18" /> <select id="ws-plugin--s2member-level' . $n . '-currency">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-currencies.php"))) . '</select> <input type="button" value="Generate Button Code" onclick="ws_plugin__s2member_paypalButtonGenerate(\'level' . $n . '\');" /></p>' . "\n";
80
  echo '<p>Description: <input type="text" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-desc" value="' . format_to_edit ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["level" . $n . "_label"]) . ' / description and pricing details here." size="73" /></p>' . "\n";
81
+ echo '<p' . ((is_multisite () && c_ws_plugin__s2member_utils_conds::is_multisite_farm () && !is_main_site ()) ? ' style="display:none;"' : '') . '>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member API Scripting Custom Capabilities.\\n\\n*ADVANCED TIP: You can specifiy a list of Custom Capabilities that will be (Added) with this purchase. Or, you could tell s2Member to (Remove All) Custom Capabilities that may or may not already exist for a particular Member, and (Add) only the new ones that you specify. To do this, just start your list of Custom Capabilities with `-all`.\\n\\nSo instead of just (Adding) Custom Capabilities:\\nmusic,videos,archives,gifts\\n\\nYou could (Remove All) that may already exist, and then (Add) new ones:\\n-all,calendar,forums,tools\\n\\nOr to just (Remove All) and (Add) nothing:\\n-all\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-level' . $n . '-ccaps" size="40" /></p>' . "\n";
82
  echo '</form>' . "\n";
83
  echo '</td>' . "\n";
84
 
86
  echo '<tr>' . "\n";
87
 
88
  echo '<td colspan="2">' . "\n";
89
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
90
 
91
  if (($ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_levelN_buttons_before_shortcode = "ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_level" . $n . "_buttons_before_shortcode"))
92
  do_action($ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_levelN_buttons_before_shortcode, get_defined_vars ());
142
  echo '<div class="ws-menu-page-section ws-plugin--s2member-modification-buttons-section">' . "\n";
143
  echo '<h3>Button Code Generator For Subscription Modifications</h3>' . "\n";
144
  echo '<p>If you\'d like to give your Members <em>(and/or your Free Subscribers)</em> the ability to modify their billing plan, by switching to a more expensive option, or a less expensive option; generate a new PayPal Modification Button here. Configure the updated Level, pricing, terms, etc. Then, make that new Modification Button available to Members who are logged into their existing account with you. For example, you might want to insert a "Level #2" Upgrade Button into your Login Welcome Page, which would up-sell existing Level #1 Members to a more expensive plan that you offer.</p>' . "\n";
145
+ echo '<p><em><strong>Important Note:</strong> Modification Buttons should be displayed to existing Users/Members, and they should be logged-in, BEFORE clicking this Button. Otherwise, post-processing of their transaction will fail to recognize the Customer\'s existing account within WordPress. Please display this Button only to Users/Members that are already logged into their account (perhaps in your Login Welcome Page for s2Member), or in another location where you can be absolutely sure that a User/Member is logged in. s2Member\'s Simple Conditionals could also be used to ensure a User/Member is logged in, by wrapping your Shortcode within a Conditional test. For further details, please see: <strong>s2Member API Scripting Simple Conditionals</strong>.</em></p>' . "\n";
146
+ echo '<p><em><strong>Modification Process:</strong> When you send a Member to PayPal using a Subscription Modification Button, PayPal will ask them to login. Once they\'re logged in, instead of being able to signup for a new Membership, PayPal will provide them with the ability to upgrade and/or downgrade their existing Membership with you, by allowing them to switch to the Membership Plan that was specified in the Subscription Modification Button. PayPal handles this nicely, and you\'ll be happy to know that s2Member has been pre-configured to deal with this scenario as well, so that everything remains automated. Their Membership Access Level will either be promoted, or demoted, based on the actions they took at PayPal during the modification process. Once an existing Member completes their Subscription Modification at PayPal, they\'ll be brought back to their Login Welcome Page, instead of to the registration screen.</em></p>' . "\n";
147
+ echo '<p><em><strong>Also Works For Free Subscribers:</strong> Although a Free Subscriber does not have an existing PayPal Subscription, s2Member is capable of adapting to this scenario gracefully. Just make sure that your existing Free Subscribers <em>(the ones who wish to upgrade)</em> pay for their Membership through a Modification Button generated by s2Member. That will allow them to continue using their existing account with you. In other words, they can keep their existing Username <em>(and anything already associated with that Username)</em>, rather than being forced to re-register after checkout.</em></p>' . "\n";
148
+ echo '<p><em><strong>Make It More User-Friendly:</strong> You can make the Subscription Modification Process, more user-friendly, by setting up a <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can tell s2Member to use that Page Style whenever you generate your Button Code.\'); return false;">Custom Page Style at PayPal</a>, specifically for Subscription Modification Buttons. Use a custom header image, with a brief explanation to the Customer. Something like, "Log into PayPal", "You can Modify your Subscription!".</em></p>' . "\n";
149
+ echo '<p><em><strong>Integrating Conditionals:</strong> Since each Modification Button is configured for a specific Level, you may want to create multiple Modification Buttons, one for each combination you intend to make available. s2Member\'s API Conditionals can help you display the proper Button to each Customer, based on the status of their existing account. For further details, see: <strong>s2Member API Scripting</strong>.</em></p>' . "\n";
150
+ echo (!is_multisite () || !c_ws_plugin__s2member_utils_conds::is_multisite_farm () || is_main_site ()) ? '<p><em><strong>Independent Custom Capabilities:</strong> If you just want to sell an existing Member new Custom Capabilities, without affecting their paid Subscription in any way, please see the next Button Generator: <code>Capability (Buy Now) Buttons</code>. Independent Capability Buttons facilitate Buy Now functionality, specifically for Custom Capabilities, without affecting the Customer\'s primary Subscription and Membership Level Access.</em></p>' . "\n" : '';
151
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_modification_buttons", get_defined_vars ());
152
 
153
  echo '<table class="form-table">' . "\n";
162
  echo '</th>' . "\n";
163
 
164
  echo '<td>' . "\n";
165
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
166
 
167
  echo '<p>Modification: <select id="ws-plugin--s2member-modification-level">' . "\n";
168
 
182
  echo '<p><span id="ws-plugin--s2member-modification-trial-then">Then, </span>I want to charge: $<input type="text" autocomplete="off" id="ws-plugin--s2member-modification-amount" value="0.01" size="4" /> / <select id="ws-plugin--s2member-modification-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-regular-terms.php"))) . '</select></p>' . "\n";
183
  echo '<p>Checkout Page Style <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can enter that Page Style here.\\n\\nIn addition. The Shortcode below, provided by s2Member; supports an image attribute: image=\\\'\\\'default\\\'\\\'. This can be changed to a full URL, pointing to a custom image of your own; instead of the default PayPal Button image.\'); return false;" tabindex="-1">[?]</a>: <input type="text" autocomplete="off" id="ws-plugin--s2member-modification-page-style" value="paypal" size="18" /> <select id="ws-plugin--s2member-modification-currency">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-currencies.php"))) . '</select> <input type="button" value="Generate Button Code" onclick="ws_plugin__s2member_paypalButtonGenerate(\'modification\');" /></p>' . "\n";
184
  echo '<p>Description: <input type="text" autocomplete="off" id="ws-plugin--s2member-modification-desc" value="Description and pricing details here." size="73" /></p>' . "\n";
185
+ echo '<p' . ((is_multisite () && c_ws_plugin__s2member_utils_conds::is_multisite_farm () && !is_main_site ()) ? ' style="display:none;"' : '') . '>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member API Scripting Custom Capabilities.\\n\\n*ADVANCED TIP: You can specifiy a list of Custom Capabilities that will be (Added) with this purchase. Or, you could tell s2Member to (Remove All) Custom Capabilities that may or may not already exist for a particular Member, and (Add) only the new ones that you specify. To do this, just start your list of Custom Capabilities with `-all`.\\n\\nSo instead of just (Adding) Custom Capabilities:\\nmusic,videos,archives,gifts\\n\\nYou could (Remove All) that may already exist, and then (Add) new ones:\\n-all,calendar,forums,tools\\n\\nOr to just (Remove All) and (Add) nothing:\\n-all\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-modification-ccaps" size="40" /></p>' . "\n";
186
  echo '</form>' . "\n";
187
  echo '</td>' . "\n";
188
 
190
  echo '<tr>' . "\n";
191
 
192
  echo '<td colspan="2">' . "\n";
193
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
194
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_modification_buttons_before_shortcode", get_defined_vars ());
195
  echo '<strong>WordPress Shortcode:</strong> (recommended for both the WordPress Visual &amp; HTML Editors)<br />' . "\n";
196
  $ws_plugin__s2member_temp_s = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/shortcodes/paypal-checkout-button-shortcode.php")));
242
 
243
  echo '<div class="ws-menu-page-section ws-plugin--s2member-ccap-buttons-section">' . "\n";
244
  echo '<h3>Button Code Generator For Independent Custom Capabilities</h3>' . "\n";
245
+ echo '<p>This is VERY advanced. For further details, please check your Dashboard: <strong>s2Member API Scripting Custom Capabiities</strong>.</p>' . "\n";
246
+ echo '<p>With s2Member, you can sell one or more Custom Capabilities using Buy Now functionality, to "existing" Users/Members, regardless of which Membership Level they have on your site <em>(i.e., you could even sell Independent Custom Capabilities to Users at Membership Level #0, normally referred to as Free Subscribers, if you like)</em>. So this is quite flexible. Independent Custom Capabilities do NOT rely on any specific Membership Level. That\'s why s2Member refers to these as `Independent` Custom Capabilities, because you can sell Capabilities this way, through Buy Now functionality, and the Customer\'s Membership Level Access, along with any existing paid Subscription they may already have with you, will remain completely unaffected. That being said, if you intend to charge a recurring fee for Custom Capabilities, please use a <code>Subscr. Modification Button</code> instead; because Independent Custom Capabilities can only be sold through Buy Now functionality.</p>' . "\n";
247
  echo '<p>Independent Custom Capabilities are added to a Customer\'s account immediately after checkout, and the Customer will have the Custom Capabilities for as long as their Membership lasts, based on their primary Subscription with your site, and/or forever, if they have a Lifetime account with you. In other words, Independent Custom Capabilities will exist on the Customer\'s account forever, or until an EOT <em>(End Of Term)</em> occurs on their primary Subscription with you; in which case s2Member would demote or delete the Customer\'s account <em>(based on your EOT configuration)</em>, and all Custom Capabilities are removed as well.</p>' . "\n";
248
  echo '<p>Very simple. All you do is customize the form fields provided, for each set of Custom Capabilities that you plan to sell. Then press (Generate Button Code). These special PayPal Buttons are customized to work with s2Member seamlessly. The Customer will be granted additional access to one or more Custom Capabilities that you specify; while the Customer\'s Membership Level Access and any existing paid Subscription they may already have with you, will remain completely unaffected.</p>' . "\n";
249
+ echo '<p><em><strong>Important Note:</strong> Independent Custom Capability Buttons should ONLY be displayed to existing Users/Members, and they MUST be logged-in, BEFORE clicking this Button. Otherwise, post-processing of their transaction will fail to recognize the Customer\'s existing account within WordPress. Please display this Button only to Users/Members that are already logged into their account (perhaps in your Login Welcome Page for s2Member), or in another location where you can be absolutely sure that a User/Member is logged in. s2Member\'s Simple Conditionals could also be used to ensure a User/Member is logged in, by wrapping your Shortcode within a Conditional test. For further details, please see: <strong>s2Member API Scripting Simple Conditionals</strong>.</em></p>' . "\n";
250
  echo '<p><em><strong>Please note:</strong> buttons are NOT saved here. This is only a Button Generator. Once you\'ve generated your Button, copy/paste it into your WordPress Editor. If you lose your Button Code, you\'ll need to come back &amp; re-generate a new one. If you\'re in Sandbox Test-Mode, and you\'re NOT using the Shortcode Format, please remember to come back and re-generate your Buttons before you go live.</em></p>' . "\n";
251
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_ccap_buttons", get_defined_vars ());
252
 
262
  echo '</th>' . "\n";
263
 
264
  echo '<td>' . "\n";
265
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
266
  echo '<p>I want to charge: $<input type="text" autocomplete="off" id="ws-plugin--s2member-ccap-amount" value="0.01" size="4" /> / <select id="ws-plugin--s2member-ccap-term">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-membership-ccap-terms.php"))) . '</select></p>' . "\n";
267
  echo '<p>Checkout Page Style <a href="#" onclick="alert(\'Optional. This can be configured inside your PayPal account. PayPal allows you to create Custom Page Styles, and assign a unique name to them. You can add your own header image and color selection to the checkout form. Once you\\\'ve created a Custom Page Style at PayPal, you can enter that Page Style here.\\n\\nIn addition. The Shortcode below, provided by s2Member; supports an image attribute: image=\\\'\\\'default\\\'\\\'. This can be changed to a full URL, pointing to a custom image of your own; instead of the default PayPal Button image.\'); return false;" tabindex="-1">[?]</a>: <input type="text" autocomplete="off" id="ws-plugin--s2member-ccap-page-style" value="paypal" size="18" /> <select id="ws-plugin--s2member-ccap-currency">' . trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/options/paypal-currencies.php"))) . '</select> <input type="button" value="Generate Button Code" onclick="ws_plugin__s2member_paypalCcapButtonGenerate();" /></p>' . "\n";
268
  echo '<p>Description: <input type="text" autocomplete="off" id="ws-plugin--s2member-ccap-desc" value="Description and pricing details here." size="73" /></p>' . "\n";
269
+ echo '<p>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member API Scripting Custom Capabilities.\\n\\n*ADVANCED TIP: You can specifiy a list of Custom Capabilities that will be (Added) with this purchase. Or, you could tell s2Member to (Remove All) Custom Capabilities that may or may not already exist for a particular Member, and (Add) only the new ones that you specify. To do this, just start your list of Custom Capabilities with `-all`.\\n\\nSo instead of just (Adding) Custom Capabilities:\\nmusic,videos,archives,gifts\\n\\nYou could (Remove All) that may already exist, and then (Add) new ones:\\n-all,calendar,forums,tools\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-ccap-ccaps" size="40" /></p>' . "\n";
270
  echo '</form>' . "\n";
271
  echo '</td>' . "\n";
272
 
274
  echo '<tr>' . "\n";
275
 
276
  echo '<td colspan="2">' . "\n";
277
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
278
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_ccap_buttons_before_shortcode", get_defined_vars ());
279
  echo '<strong>WordPress Shortcode:</strong> (recommended for both the WordPress Visual &amp; HTML Editors)<br />' . "\n";
280
  $ws_plugin__s2member_temp_s = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/shortcodes/paypal-ccaps-checkout-button-shortcode.php")));
321
  echo '<div class="ws-menu-page-section ws-plugin--s2member-cancellation-buttons-section">' . "\n";
322
  echo '<h3>One Button Does It All For Cancellations (copy/paste)</h3>' . "\n";
323
  echo '<p>Since all recurring charges are associated with a PayPal Subscription; and every PayPal Subscription is associated with a PayPal Account; your Members will always have a PayPal Account of their own, which is tied to their Membership with you. So... a Member can simply log into their own PayPal account and cancel their Subscription(s) with you at anytime, all on their own. However, some Customers do not realize this. So, if you would like to make it clearer (easier) for Members to cancel their own Subscription(s), you can provide this Cancellation Button for them on your Login Welcome Page, or somewhere in the support section of your website. Note... you don\'t have to use this Cancellation Button at all, if you don\'t want to. It\'s completely optional.</p>' . "\n";
324
+ echo '<p><em><strong>Cancellation Process:</strong> Very simple. A Member clicks the Cancellation Button. PayPal asks them to log into their PayPal account. Once they\'re logged in, PayPal will display a list of all active Subscriptions they have with you. They choose which ones they want to cancel, and s2Member is notified silently behind-the-scenes, through the PayPal IPN service.</em></p>' . "\n";
325
+ echo '<p><em><strong>Understanding Cancellations:</strong> It\'s important to realize that a Cancellation is not an EOT (End Of Term). All that happens during a Cancellation event, is that billing is stopped, and it\'s understood that the Customer is going to lose access, at some point in the future. This does NOT mean, that access will be revoked immediately. A separate EOT event will automatically handle a (demotion or deletion) later, at the appropriate time; which could be several days, or even a year after the Cancellation took place.</em></p>' . "\n";
326
+ echo '<p><em><strong>Some Hairy Details:</strong> There might be times whenever you notice that a Member\'s Subscription has been cancelled through PayPal... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only remove the User\'s Membership privileges when an EOT (End Of Term) is processed, a refund occurs, a chargeback occurs, or when a cancellation occurs - which would later result in a delayed Auto-EOT by s2Member.</em></p>' . "\n";
327
  echo '<p><em>s2Member will not process an EOT (End Of Term) until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will remove their Membership privileges; by either demoting them to a Free Subscriber, or deleting their account from the system (based on your configuration). s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.</em></p>' . "\n";
328
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_cancellation_buttons", get_defined_vars ());
329
 
347
  echo '</th>' . "\n";
348
 
349
  echo '<td>' . "\n";
350
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
351
  echo '<p>No configuration necessary.</p>' . "\n";
352
  echo '</form>' . "\n";
353
  echo '</td>' . "\n";
356
  echo '<tr>' . "\n";
357
 
358
  echo '<td colspan="2">' . "\n";
359
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
360
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_cancellation_buttons_before_shortcode", get_defined_vars ());
361
  echo '<strong>WordPress Shortcode:</strong> (recommended for both the WordPress Visual &amp; HTML Editors)<br />' . "\n";
362
  $ws_plugin__s2member_temp_s = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/shortcodes/paypal-cancellation-button-shortcode.php")));
397
 
398
  echo '<div class="ws-menu-page-section ws-plugin--s2member-reg-links-section">' . "\n";
399
  echo '<h3>Registration Access Link Generator (for Customer Service)</h3>' . "\n";
400
+ echo '<p>s2Member automatically generates Registration Access Links for your Customers after checkout, and also sends them a link in a Confirmation Email. However, if you ever need to deal with a Customer Service issue that requires a new Registration Access Link to be created manually, you can use this tool for that. Alternatively, you can create their account yourself/manually by going to <strong>Users Add New</strong>. Either of these methods will work fine.</p>' . "\n";
401
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_reg_links", get_defined_vars ());
402
 
403
  echo '<table class="form-table">' . "\n";
405
  echo '<tr>' . "\n";
406
 
407
  echo '<td>' . "\n";
408
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
409
  echo '<p>Paid Membership Level#: <select id="ws-plugin--s2member-reg-link-level" style="min-width:200px;">' . "\n";
410
  for ($n = 1; $n <= $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; $n++)
411
  echo '<option value="' . $n . '">s2Member Level #' . $n . '</option>' . "\n";
412
  echo '</select></p>' . "\n";
413
+ echo '<p>Paid Subscr. ID: <input type="text" autocomplete="off" id="ws-plugin--s2member-reg-link-subscr-id" value="" size="50" /> <a href="#" onclick="alert(\'The Customer\\\'s Paid Subscr. ID (aka: Recurring Profile ID, Transaction ID) must be unique. This value can be obtained from inside your PayPal account under the History tab. Each paying Customer MUST be associated with a unique Paid Subscr. ID. If the Customer is NOT associated with a Paid Subscr. ID, you will need to generate a unique value for this field on your own. But keep in mind, s2Member will be unable to maintain future communication with the PayPal IPN (i.e., Notification) service if this value does not reflect a real Paid Subscr. ID that exists in your PayPal History log.\'); return false;" tabindex="-1">[?]</a></p>' . "\n";
414
  echo '<p>Custom String Value: <input type="text" autocomplete="off" id="ws-plugin--s2member-reg-link-custom" value="' . esc_attr ($_SERVER["HTTP_HOST"]) . '" size="30" /> <a href="#" onclick="alert(\'A Paid Subscription is always associated with a Custom String that is passed through the custom=\\\'\\\'' . c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"]), 3) . '\\\'\\\' attribute of your Shortcode. This Custom Value, MUST always start with your domain name. However, you can also pipe delimit additional values after your domain, if you need to.\\n\\nFor example:\n' . c_ws_plugin__s2member_utils_strings::esc_js_sq (esc_attr ($_SERVER["HTTP_HOST"]), 3) . '|cv1|cv2|cv3\'); return false;" tabindex="-1">[?]</a> <input type="button" value="Generate Access Link" onclick="ws_plugin__s2member_paypalRegLinkGenerate();" /> <img id="ws-plugin--s2member-reg-link-loading" src="' . esc_attr ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]) . '/images/ajax-loader.gif" alt="" style="display:none;" /></p>' . "\n";
415
+ echo '<p' . ((is_multisite () && c_ws_plugin__s2member_utils_conds::is_multisite_farm () && !is_main_site ()) ? ' style="display:none;"' : '') . '>Custom Capabilities (comma-delimited) <a href="#" onclick="alert(\'Optional. This is VERY advanced.\\nSee: s2Member API Scripting Custom Capabilities.\'); return false;" tabindex="-1">[?]</a> <input type="text" maxlength="125" autocomplete="off" id="ws-plugin--s2member-reg-link-ccaps" size="40" onkeyup="if(this.value.match(/[^a-z_0-9,]/)) this.value = jQuery.trim (jQuery.trim (this.value).replace (/[ \-]/g, \'_\').replace (/[^a-z_0-9,]/gi, \'\').toLowerCase ());" /></p>' . "\n";
416
+ echo '<p>Fixed Term Length (for Buy Now transactions): <input type="text" autocomplete="off" id="ws-plugin--s2member-reg-link-fixed-term" value="" size="10" /> <a href="#" onclick="alert(\'If the Customer purchased Membership through a Buy Now transaction (i.e., there is no Initial/Trial Period and no recurring charges for ongoing access), you may configure a Fixed Term Length in this field. This way the Customer\\\'s Membership Access is automatically revoked by s2Member at the appropriate time. This will be a numeric value, followed by a space, then a single letter.\\n\\nHere are some examples:\\n\\n1 D (this means 1 Day)\\n1 W (this means 1 Week)\\n1 M (this means 1 Month)\\n1 Y (this means 1 Year)\\n1 L (this means 1 Lifetime)\'); return false;">[?]</a></p>' . "\n";
417
  echo '<p id="ws-plugin--s2member-reg-link" style="display:none;"></p>' . "\n";
418
  echo '</form>' . "\n";
419
  echo '</td>' . "\n";
437
  echo '<div class="ws-menu-page-section ws-plugin--s2member-sp-buttons-section">' . "\n";
438
  echo '<h3>Button Code Generator For Specific Post/Page Buttons</h3>' . "\n";
439
  echo '<p>s2Member now supports an additional layer of functionality (very powerful), which allows you to sell access to specific Posts/Pages that you\'ve created in WordPress. Specific Post/Page Access works independently from Member Level Access. That is, you can sell an unlimited number of Posts/Pages using "Buy Now" Buttons, and your Customers will NOT be required to have a Membership Account with your site in order to receive access. If they are already a Member, that\'s fine, but they won\'t need to be.</p>' . "\n";
440
+ echo '<p>In other words, Customers will NOT need to login, just to receive access to the Specific Post/Page they purchased access to. s2Member will immediately redirect the Customer to the Specific Post/Page after checkout is completed successfully. An email is also sent to the Customer with a link (see: <strong>s2Member PayPal Options Specific Post/Page Email</strong>). Authentication is handled automatically through self-expiring links, good for 72 hours by default.</p>' . "\n";
441
+ echo '<p>Specific Post/Page Access, is sort of like selling a product. Only, instead of shipping anything to the Customer, you just give them access to a specific Post/Page on your site; one that you created in WordPress. A Specific Post/Page that is protected by s2Member, might contain a download link for your eBook, access to file &amp; music downloads, access to additional support services, and the list goes on and on. The possibilities with this are endless; as long as your digital product can be delivered through access to a WordPress Post/Page that you\'ve created. To protect Specific Posts/Pages, please see: <strong>s2Member Restriction Options Specific Post/Page Access</strong>. Once you\'ve configured your Specific Post/Page Restrictions, those Posts/Pages will be available in the menus below.</p>' . "\n";
442
  echo '<p>Very simple. All you do is customize the form fields provided, for each Post/Page that you plan to sell. Then press (Generate Button Code). These special PayPal Buttons are customized to work with s2Member seamlessly. You can even Package Additional Posts/Pages together into one transaction.</p>' . "\n";
443
  echo '<p><em><strong>Please note:</strong> buttons are NOT saved here. This is only a Button Generator. Once you\'ve generated your Button, copy/paste it into your WordPress Editor. If you lose your Button Code, you\'ll need to come back &amp; re-generate a new one. If you\'re in Sandbox Test-Mode, and you\'re NOT using the Shortcode Format, please remember to come back and re-generate your Buttons before you go live.</em></p>' . "\n";
444
  do_action("ws_plugin__s2member_during_paypal_buttons_page_during_left_sections_during_sp_buttons", get_defined_vars ());
455
  echo '</th>' . "\n";
456
 
457
  echo '<td>' . "\n";
458
+ echo '<form onsubmit="return false;" autocomplete="off">' . "\n";
459
 
460
  echo '<p><select id="ws-plugin--s2member-sp-leading-id">' . "\n";
461
  echo '<option value="">&mdash; Select a Leading Post/Page that you\'ve protected &mdash;</option>' . "\n";
465
  foreach ($ws_plugin__s2member_temp_a_singulars as $ws_plugin__s2member_temp_o)
466
  echo '<option value="' . esc_attr ($ws_plugin__s2member_temp_o->ID) . '">' . esc_html ($ws_plugin__s2member_temp_o->post_title) . '</option>' . "\n";
467
 
468
+ echo '</select> <a href="#" onclick="alert(\'Required. The Leading Post/Page, is what your Customers will land on after checkout.\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.\'); return false;" tabindex="-1">[?]</a></p>' . "\n";
469
 
470
  echo '<p><select id="ws-plugin--s2member-sp-additional-ids" multiple="multiple" style="height:100px;">' . "\n";
471
  echo '<optgroup label="&mdash; Package Additional Posts/Pages that you\'ve protected &mdash;">' . "\n";
473
  foreach ($ws_plugin__s2member_temp_a_singulars as $ws_plugin__s2member_temp_o)
474
  echo '<option value="' . esc_attr ($ws_plugin__s2member_temp_o->ID) . '">' . esc_html ($ws_plugin__s2member_temp_o->post_title) . '</option>' . "\n";
475
 
476
+ echo '</optgroup></select> <a href="#" onclick="alert(\'Hold down your `Ctrl` key to select multiples.\\n\\nOptional. If you include Additional Posts/Pages, Customers will still land on your Leading Post/Page; BUT, they\\\'ll ALSO have access to some Additional Posts/Pages that you\\\'ve protected. This gives you the ability to create Post/Page Packages.\\n\\nIn other words, a Customer is sold a Specific Post/Page (they\\\'ll land on your Leading Post/Page after checkout), which might contain links to some other Posts/Pages that you\\\'ve packaged together under one transaction.\\n\\nBundling Additional Posts/Pages into one Package, authenticates the Customer for access to the Additional Posts/Pages automatically (i.e., only one Access Link is needed, and s2Member generates this automatically). However, you will STILL need to design your Leading Post/Page (which is what a Customer will actually land on), with links pointing to the other Posts/Pages. This way your Customers will have clickable links to everything they\\\'ve paid for.\\n\\n*Quick Summary* s2Member sends Customers to your Leading Post/Page, and also authenticates them for access to any Additional Posts/Pages automatically. You handle it from there.\\n\\n*Tip* If there are no Posts/Pages in this menu, it\\\'s because you\\\'ve not configured s2Member for Specific Post/Page Access yet. See: s2Member Restriction Options Specific Post/Page Ac