Paid Memberships Pro - Version 2.7.1

Version Description

  • 2022-01-13 =
  • BUG FIX: Fixed issue on some MySQL setups that would throw an error about the primary key in the pmpro_memberships_pages and pmpro_memberships_categories tables.
Download this release

Release Info

Developer strangerstudios
Plugin Icon 128x128 Paid Memberships Pro
Version 2.7.1
Comparing to
See all releases

Code changes from version 2.4.5 to 2.7.1

Files changed (144) hide show
  1. CHANGELOG.txt +294 -1
  2. adminpages/admin_header.php +9 -4
  3. adminpages/advancedsettings.php +34 -17
  4. adminpages/dashboard.php +11 -7
  5. adminpages/discountcodes.php +147 -49
  6. adminpages/emailsettings.php +6 -38
  7. adminpages/emailtemplates.php +207 -0
  8. adminpages/functions.php +26 -5
  9. adminpages/license.php +22 -31
  10. adminpages/membershiplevels.php +149 -24
  11. adminpages/memberslist.php +1 -1
  12. adminpages/orders-csv.php +8 -0
  13. adminpages/orders.php +207 -111
  14. adminpages/pagesettings.php +84 -15
  15. adminpages/paymentsettings.php +12 -5
  16. adminpages/reports/login.php +10 -5
  17. adminpages/reports/memberships.php +2 -19
  18. adminpages/reports/sales.php +24 -9
  19. adminpages/templates/orders-email.php +25 -21
  20. adminpages/templates/orders-print.php +18 -14
  21. blocks/blocks.php +23 -3
  22. blocks/membership/block.js +32 -19
  23. classes/class-deny-network-activation.php +16 -4
  24. classes/class-pmpro-admin-activity-email.php +7 -5
  25. classes/class-pmpro-members-list-table.php +7 -8
  26. classes/class-pmpro-site-health.php +548 -0
  27. classes/class.memberorder.php +193 -17
  28. classes/class.pmproemail.php +180 -180
  29. classes/gateways/class.pmprogateway.php +6 -6
  30. classes/gateways/class.pmprogateway_authorizenet.php +8 -8
  31. classes/gateways/class.pmprogateway_braintree.php +49 -11
  32. classes/gateways/class.pmprogateway_check.php +6 -6
  33. classes/gateways/class.pmprogateway_cybersource.php +8 -8
  34. classes/gateways/class.pmprogateway_payflowpro.php +42 -4
  35. classes/gateways/class.pmprogateway_paypal.php +18 -10
  36. classes/gateways/class.pmprogateway_paypalexpress.php +261 -90
  37. classes/gateways/class.pmprogateway_paypalstandard.php +5 -4
  38. classes/gateways/class.pmprogateway_stripe.php +3201 -1773
  39. classes/gateways/class.pmprogateway_twocheckout.php +6 -4
  40. css/admin.css +646 -284
  41. css/blocks.editor.css +9 -1
  42. css/frontend.css +64 -1
  43. email/admin_change.html +0 -7
  44. email/admin_change_admin.html +0 -5
  45. email/billable_invoice.html +0 -6
  46. email/billing.html +0 -16
  47. email/billing_admin.html +0 -17
  48. email/billing_failure.html +0 -3
  49. email/billing_failure_admin.html +0 -6
  50. email/cancel.html +0 -6
  51. email/cancel_admin.html +0 -8
  52. email/checkout_check.html +0 -19
  53. email/checkout_check_admin.html +0 -17
  54. email/checkout_express.html +0 -15
  55. email/checkout_express_admin.html +0 -14
  56. email/checkout_free.html +0 -9
  57. email/checkout_free_admin.html +0 -8
  58. email/checkout_freetrial.html +0 -20
  59. email/checkout_freetrial_admin.html +0 -19
  60. email/checkout_paid.html +0 -24
  61. email/checkout_paid_admin.html +0 -23
  62. email/checkout_trial.html +0 -24
  63. email/checkout_trial_admin.html +0 -23
  64. email/credit_card_expiring.html +0 -13
  65. email/default.html +0 -1
  66. email/footer.html +0 -4
  67. email/header.html +0 -1
  68. email/invoice.html +0 -19
  69. email/membership_expired.html +0 -7
  70. email/membership_expiring.html +0 -6
  71. email/payment_action.html +0 -4
  72. email/payment_action_admin.html +0 -7
  73. email/trial_ending.html +0 -8
  74. includes/addons.php +3 -3
  75. includes/admin.php +29 -9
  76. includes/adminpages.php +16 -9
  77. includes/capabilities.php +1 -0
  78. includes/compatibility.php +106 -39
  79. includes/compatibility/beaver-builder.php +4 -0
  80. includes/compatibility/divi.php +93 -0
  81. includes/compatibility/elementor/class-pmpro-elementor-content-restriction.php +8 -2
  82. includes/compatibility/jetpack.php +31 -0
  83. includes/content.php +25 -15
  84. includes/currencies.php +9 -1
  85. includes/deprecated.php +133 -2
  86. includes/email-templates.php +536 -0
  87. includes/email.php +460 -29
  88. includes/functions.php +702 -127
  89. includes/init.php +11 -8
  90. includes/lib/Braintree/README.md +0 -134
  91. includes/lib/Braintree/composer.json +0 -36
  92. includes/lib/Braintree/lib/Braintree/Util.php +1 -1
  93. includes/lib/Stripe/README.md +0 -266
  94. includes/lib/name-parser.php +28 -9
  95. includes/lib/stripe-apple-pay/apple-developer-merchantid-domain-association +1 -0
  96. includes/lib/stripe-apple-pay/stripe-apple-pay.php +46 -0
  97. includes/license.php +100 -146
  98. includes/localization.php +10 -5
  99. includes/login.php +121 -78
  100. includes/metaboxes.php +23 -3
  101. includes/notifications.php +9 -5
  102. includes/profile.php +281 -34
  103. includes/recaptcha.php +137 -84
  104. includes/rest-api.php +285 -37
  105. includes/scripts.php +65 -56
  106. includes/services.php +11 -2
  107. includes/setup.sql +20 -4
  108. includes/spam.php +192 -0
  109. includes/updates/upgrade_2_4.php +46 -20
  110. includes/updates/upgrade_2_6.php +46 -0
  111. includes/upgradecheck.php +82 -37
  112. js/blocks.build.js +1883 -1
  113. js/pmpro-admin.js +290 -13
  114. js/pmpro-login.js +8 -2
  115. js/pmpro-stripe.js +128 -35
  116. languages/email/de_DE/admin_change.html +3 -3
  117. languages/email/de_DE/admin_change_admin.html +1 -1
  118. languages/email/de_DE/billing.html +4 -4
  119. languages/email/de_DE/billing_admin.html +2 -2
  120. languages/email/de_DE/billing_failure.html +2 -2
  121. languages/email/de_DE/billing_failure_admin.html +1 -1
  122. languages/email/de_DE/cancel.html +2 -2
  123. languages/email/de_DE/cancel_admin.html +2 -2
  124. languages/email/de_DE/checkout_check.html +3 -3
  125. languages/email/de_DE/checkout_check_admin.html +2 -2
  126. languages/email/de_DE/checkout_express.html +3 -3
  127. languages/email/de_DE/checkout_express_admin.html +2 -2
  128. languages/email/de_DE/checkout_free.html +2 -2
  129. languages/email/de_DE/checkout_free_admin.html +1 -1
  130. languages/email/de_DE/checkout_freetrial.html +3 -3
  131. languages/email/de_DE/checkout_freetrial_admin.html +2 -2
  132. languages/email/de_DE/checkout_paid.html +4 -4
  133. languages/email/de_DE/checkout_paid_admin.html +2 -2
  134. languages/email/de_DE/checkout_trial.html +4 -4
  135. languages/email/de_DE/checkout_trial_admin.html +2 -2
  136. languages/email/de_DE/credit_card_expiring.html +2 -2
  137. languages/email/de_DE/footer.html +1 -1
  138. languages/email/de_DE/invoice.html +4 -4
  139. languages/email/de_DE/membership_expired.html +2 -2
  140. languages/email/de_DE/membership_expiring.html +2 -2
  141. languages/email/de_DE/trial_ending.html +3 -3
  142. languages/gettext.sh +6 -0
  143. languages/paid-memberships-pro-ca.mo +0 -0
  144. languages/paid-memberships-pro-ca.po +167 -59
CHANGELOG.txt CHANGED
@@ -1,7 +1,300 @@
1
  == Changelog ==
2
- = 2.4.5 - 2022-01-06 =
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  * SECURITY: Updated escaping in the pmpro_getLevelAtCheckout and pmpro_checkDiscountCode functions as extra precaution against SQL injections. (Thanks, WPScan)
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  = 2.4.4 - 2020-09-02 =
6
  * BUG FIX: Fixed fatal error that sometimes occurred on the payment settings page when using PHP 5.6 or earlier.
7
  * BUG FIX: Fixed fatal errors that showed up on the frontend invoice page.
1
  == Changelog ==
2
+ = 2.7.1 - 2022-01-13 =
3
+ * BUG FIX: Fixed issue on some MySQL setups that would throw an error about the primary key in the pmpro_memberships_pages and pmpro_memberships_categories tables.
4
+
5
+ = 2.7 - 2022-01-13 =
6
+ * FEATURE: Added a "Spam Protection" option to the advanced settings page. When used, IP addresses are blocked from checkout if there are more than 10 failsures within 15 minutes. (@ideadude)
7
+ * ENHANCEMENT: Checkouts with Stripe will now reuse Stripe Products and Prices. (@dparker1005)
8
+ * ENHANCEMENT: User profile now links to Stripe customer so that subscriptions can easily be directly updated in Stripe rather than through Subscriptions Updates which is now deprecated. (@dparker1005)
9
+ * ENHANCEMENT: Improved usability of the Require Membership metabox for sites with a large number of levels. #1885 #1692 (@kimcoleman)
10
+ * ENHANCEMENT: Include reCAPTCHA on Billing Page form. #1884 (@ideadude)
11
+ * ENHANCEMENT: Membership pages URLs are now included in the Site Health Information. (@JarrydLong)
12
+ * BUG FIX/ENHANCEMENT: Set the default for the `pmpro_send_200_http_response` filter to `false` instead of `true` to reduce impact of the functionality on sites that don't need it. #1868 (@dparker1005)
13
+ * BUG FIX/ENHANCEMENT: Consolidated the "Free memberships only" option into the "All memberships" option for enabling reCAPTCHA to ensure that paid memberships with discounts that make the membership $0 cost show reCAPTCHA consistently. #1878 #1840 (@JarrydLong)
14
+ * BUG FIX: Resolved PHP 8 fatal error with Authorize.net Silent Post handling. #1899 (@ZebulanStanphill)
15
+ * BUG FIX: The new filter `pmpro_admin_pagesetting_post_type` replaces the now deprecated filter `pmpro_admin_pagesetting_post_type_array` to allows setting one specific post type instead of always getting the first from an array. #1866 #1865 (@ipokkel)
16
+ * BUG FIX: Resolved issue where users may not be linked to their Stripe customer. (@dparker1005)
17
+ * BUG FIX: Now including all levels (public and hidden) in the Paid Memberships Pro section of Site Health information. #1898 (@kimcoleman)
18
+ * BUG FIX: Set cancelled/error status from PayPal Express IPN even when an order has no user. #1897 (@mircobabini)
19
+ * BUG FIX: Ensure styles are removed from excerpts generated for protected content. #1894 (@sc0ttkclark)
20
+ * BUG FIX: Various text adjustments for readability. #1892 (@kimcoleman)
21
+ * BUG FIX: Set primary key for `wp_pmpro_memberships_categories` and `wp_pmpro_memberships_pages` tables. #1891 (@ideadude)
22
+ * BUG FIX: Fixes around the `pmpro_manage_memberslist_columns` filter to accommodate sites that aren't in English. #1879 #1876 (@JarrydLong)
23
+ * BUG FIX: Use the correct cancel method when cancelling an oder through PayPal Standard IPN requests. Fixes compatibility with PMPro Cancel on Next Payment Date add on. #1882 (@mircobabini)
24
+ * BUG FIX: Discount code expiration date is now properly being used in the REST API. #1877 (@JarrydLong)
25
+ * BUG FIX: Remove the `SHIPTOPHONENUM` parameter sent to PayPal Website Payments API and encode all parameters passed into API updates. #1883 (@dparker1005)
26
+ * BUG FIX: Resolve timezone issue with Stripe subscriptions to resolve problems where trials would receive an extra day depending on the time of checkout. #1874 (@dparker1005)
27
+ * BUG FIX: Now using a single product per level and a single Stripe price per payment amount to prevent duplicate products and prices. #1824 (@dparker1005)
28
+ * REFACTOR: Organized and simplified Stripe gateway class. (@dparker1005)
29
+
30
+ = 2.6.7 - 2022-01-06 =
31
  * SECURITY: Updated escaping in the pmpro_getLevelAtCheckout and pmpro_checkDiscountCode functions as extra precaution against SQL injections. (Thanks, WPScan)
32
 
33
+ = 2.6.6 - 2021-11-18 =
34
+ * SECURITY: Updated escaping on the discount codes page in the dashboard to prevent XSS attacks. #1867 (Thanks, Erwan from WPScan)
35
+ * BUG FIX/ENHANCEMENT: Added code to remove duplicate active rows in the pmpro_memberships_users table after level change. This might have happened e.g. if users were purchasing a level via the WooCommerce Add On multiple times. #1860 (@dlparker1005)
36
+ * BUG FIX/ENHANCEMENT: Improved the REST API endpoints to better support Zapier native requirements. #1862 (@andrewlimaza)
37
+ * BUG FIX: Fixed PHP notices in the name parser library. #1861 (@sc0ttkclark)
38
+
39
+ = 2.6.5 - 2021-11-12 =
40
+ * ENHANCEMENT: Introduced new action `pmpro_before_commit_express_checkout` to allow additional changes after an order has been saved but before sending customer to PayPal Express checkout. #1852 (@mircobabini)
41
+ * BUG FIX/ENHANCEMENT: Added login compatibility for Jetpack WordPress.com SSO when using the PMPro login page. #1848 (@sc0ttkclark)
42
+ * BUG FIX: Fixed PHP notices from status headers when server protocol information is unavailable. #1849 (@dparker1005)
43
+ * BUG FIX: Fixed metadata compatibility for membership levels and orders when calling `get_pmpro_membership_level_meta()` and `get_pmpro_membership_order_meta()` so they support getting all meta values for all keys. #1853 (@mircobabini)
44
+ * BUG FIX: Escape all Webhook communication debug output coming from gateways. #1855 (@ideadude, Victor Garcia)
45
+
46
+ = 2.6.4 - 2021-11-02 =
47
+ * ENHANCEMENT: Now including some information from the htaccess file in Site Health, including whether a getfile.php script is defined or if caching is being used. (@sc0ttkclark)
48
+ * ENHANCEMENT: Now including some of the PMPro-related PHP constants in the Site Health. (@sc0ttkclark)
49
+ * ENHNACEMENT: Now including the minimum PHP version (5.6 currently) in the readme.txt so it's shared on the WordPress.org page. (@sc0ttkclark)
50
+ * ENHANCEMENT: Added scrollable classes to the member history shown on user profile edit page in the dashboard. (@kimcoleman)
51
+ * BUG FIX/ENHANCEMENT: Now sending a 200 OK status message early when the Stripe webhook is running to avoid timeout issues. We may use this new pmpro_send_200_http_response in the other webhook/IPN handlers later on. (@dparker1005)
52
+ * BUG FIX/EHNANCEMENT: Removed the "fee" info from the edit user page. This was often misleading. The fee is still shown on the members list and frontend account page. Future updates will include work to make sure the fee is more accurate in cases where subscriptions or levels are being changed by admins after checkout. (@ideadude)
53
+ * BUG FIX/ENHANCEMENT: Once again enqueuing the admin.css file on all WP admin pages. This fixes issues where styles weren't being applied to the edit user/profile page in the dashboard. (@ideadude)
54
+ * BUG FIX/ENHANCEMENT: Removed the "Member Value Report". We didn't intend to move this over from the old Member History add on. The report was inaccurate and had optimization issues. (@kimcoleman)
55
+ * BUG FIX/ENHANCEMENT: Added login compatibility for wordpress.com hosted sites. This fixes some issues with wordpress.com's SSO when using the PMPro login page. (@sc0ttkclark)
56
+ * BUG FIX: Fixed issue introduced in 2.6.3 where memberships were not being cancelled when cancelled at PayPal. (@mircobabini)
57
+ * BUG FIX: Now including time when calculating profile start date. In the past, we would set it to 00:00:00 which could add or remove a few hours from the subscription. (@dparker1005)
58
+ * BUG FIX: Fixed issue where enddates were incorrectly set sometimes when expiration period was "Hour". (@kimwhite)
59
+
60
+ = 2.6.3 - 2021-10-11 =
61
+ * ENHANCEMENT: Now passing "app" information to Stripe through API calls. (@dparker1005)
62
+ * ENHANCEMENT: Updated PayPal IPN to detect messages for refunds to at least log it. (@mircobabini)
63
+ * ENHANCEMENT: Updated PayPal IPN to differentiate between cases where the initial payment failed vs a subscription was cancelled. (@mircobabini)
64
+ * ENHANCEMENT: Better styling of the membership levels history when empty. (@mircobabini)
65
+ * BUG FIX/ENHANCEMENT: Now showing better error messages when license key checks fail due to connection issues. (@ideadude)
66
+ * BUG FIX: Improved PayPal API integration to handle cases where PayPal is returning encoding errors but still processing payments and subscriptions. (@mircobabini)
67
+ * BUG FIX: Fixed issue where the Stripe sandbox key wasn't saved properly when using Stripe connect. (@dparker1005)
68
+ * BUG FIX: Fixed issue where a double $$ was showing up in specific emails. (@andrewlimaza)
69
+ * BUG FIX: Fixed warnings in various webhook and IPN handlers. (@ideadude)
70
+ * BUG FIX: Fixed warning in the admin activity email cron job. (@andrewlimaza)
71
+
72
+ = 2.6.2 - 2021-09-17 =
73
+ * ENHANCEMENT: Made username the first column in the members list. This helps with the mobile view. #1764 (@dparker1005)
74
+ * BUG FIX/ENHANCEMENT: Will now block uninstall.php from running if an older version of PMPro is deleted from the plugins page. #1773 (@mircobabini)
75
+ * BUG FIX/ENAHCEMENT: Expanded the allowed HTML for pmpro_kses to support email templates and added a pmpro_kses filter. #1770 (@sc0ttkclark)
76
+ * BUG FIX/ENHANCEMENT: Updated the CSS for "clickable" labels in checkbox lists. #1752 (@kimcoleman)
77
+ * BUG FIX: Fixed bug with sending test emails from the email templates page. #1765 (@ideadude)
78
+ * BUG FIX: Added the !!membership_level_confirmation_message!! var to the list on the email tempaltes page. #1783 (@kimwhite)
79
+ * BUG FIX: Updated the SendWP link per their new dashboard area. #1777 (@kimcoleman)
80
+ * BUG FIX: Fixed bug where PMPro-related usermeta was sometimes blanked out if those fields weren't present at checkout, e.g. when a logged in user was checking out. #1762 (@andrewlimaza)
81
+ * BUG FIX: Fixed issue where gateway-related notices weren't showing up on the discount codes page. #1757 (@mircobabini)
82
+ * BUG FIX: Fixed some design issues with the member history tables. #1753 (@mircobabini)
83
+ * BUG FIX: Fixed issues where the option to block subscribers from the dashboard would interfere with other plugins, e.g. the MailPoet plugin. #1749 (@sc0ttkclark)
84
+ * BUG FIX: Fixed issues where the RTL stylesheets might not load if your theme overrode frontend.css or admin.css but didn't have the RTL equivalents. (@ideadude, @sc0ttkclark)
85
+
86
+ = 2.6.1.1 - 2021-08-25 =
87
+ * BUG FIX: Fixed issue with PMPro blocks not showing up in the block editor.
88
+ * REFACTOR: Some JS functions and element IDs and names have been prefixed with pmpro_ to avoid conflicts.
89
+
90
+ = 2.6.1 - 2021-08-24 =
91
+ * SECURITY: Added capability checks to further tighten security around email template settings. (@ideadude, @sc0ttkclark)
92
+ * SECURITY: Added a pmpro_kses function and using that to sanitize email template bodies and all email bodies before sending. (@ideadude, @sc0ttkclark)
93
+ * ENHANCEMENT: Added Email Templates link to PMPro Dashboard for getting started. #1722 (@kimcoleman)
94
+ * ENHANCEMENT: All actions in the admin list tables are now filterable for Discount Codes (`pmpro_discountcodes_row_actions`), Membership Levels (`pmpro_membershiplevels_row_actions`), and Orders (`pmpro_orders_user_row_actions`). #1686 (@sc0ttkclark, @mircobabini)
95
+ * BUG FIX: Ensure our admin scripts/styles only load on PMPro admin pages. #1724 (@sc0ttkclark)
96
+ * BUG FIX: Remove unused code in `pmpro_comments_filter()` that was triggering a PHP warning. #1730 (@freax)
97
+ * BUG FIX: Stop turning on autoloading for PMPro options when saving them. #1719 (@freax)
98
+ * BUG FIX: Prevent fatal error for PHP 8 in `pmpro_email_templates_email_data()` to strictly check for `WP_User` objects. #1729 (@ZebulanStanphill)
99
+ * BUG FIX: Fix problem where `pmpro_round_price()` would not take into account currencies with decimals set to 0. #1732 (@dparker1005, @ipokkel, @sc0ttkclark)
100
+ * BUG FIX: Clarify that Stripe Legacy keys remain connected and will continue to work. #1735 (@dparker1005, @sc0ttkclark)
101
+
102
+ = 2.6 - 2021-08-12 =
103
+ * FEATURE: Updated Stripe integration to use Stripe Connect. See [Gateway Fees](https://www.paidmembershipspro.com/gateway/stripe/#tab-fees) for information about transaction fees for Stripe Connect and our platform fee for those without an active Plus/Unlimited license.
104
+ * FEATURE: Improved REST API endpoints to support Zapier integration natively.
105
+ * FEATURE: You can now set levels to expire after a certain number of hours, and can set users to expire at a specific time down to the minute.
106
+ * FEATURE: The Member History Add On has been merged into the core PMPro plugin. A table of the user's membership and order history is shown on the edit user page of the admin dashboard.
107
+ * FEATURE: The Email Templates Add On has been merged into the core PMPro plugin. You can edit PMPro-related email templates from the Memberships -> Settings -> Email Templates page in the admin dashboard.
108
+ * FEATURE: You can now use PMPro blocks in the new widget area of WP 5.8.
109
+ * BUG FIX/ENHANCEMENT: Establishing style for scrollable boxes throughout core plugin.
110
+ * BUG FIX/ENHANCEMENT: Using HTTPS to set the pmpro_visit cookie if over HTTPS. (Thanks, freax on GitHub)
111
+ * BUG FIX: Fixed fatal error in PHP 8 when deleting a Stripe webhook. (Thanks, Zebulan Stanphill)
112
+ * BUG FIX: Fixed warnings shown on the widget page when using WP 5.8+.
113
+
114
+ = 2.5.10.2 - 2021-08-02 =
115
+ * ENHANCEMENT: New scripts to use WP CLI to update pot and po/mo files.
116
+ * BUG FIX/ENHANCEMENT: Updated cancellation logic to support upcoming Cancel on Next Payment Date Add On changes.
117
+ * BUG FIX/ENHANCEMENT: Making sure to use the correct security setting when calling setcookie from an HTTPS site. (Thanks, freax on GitHub)
118
+ * BUG FIX: Now archiving Stripe products after checkout. We create a unique product for each checkout, and these would clutter up the Stripe reports.
119
+ * BUG FIX: Fixing data erasure and data export request action for login page.
120
+ * BUG FIX: Fixed issue where PMPro settings on Elementor elements could override the "should_render" setting incorrectly. (Thanks, codezz on GitHub)
121
+ * BUG FIX: Now catching the case where you try to email an invoice for an order that has no user.
122
+
123
+ = 2.5.10.1 - 2021-07-05 =
124
+ * BUG FIX/ENHANCEMENT: The 'Edit Code: %s' string on the discount codes page is now wrapped for translation.
125
+ * BUG FIX: Fixed issue with the getfile.php script introduced in 2.5.10.
126
+
127
+ = 2.5.10 - 2021-06-25 =
128
+ * SECURITY: Fixed XSS vulnerability on the edit order page in the dashboard. (Thanks, Scott Kingsley Clark)
129
+ * ENHANCEMENT: Improved escaping and localization for the message returned when clicking to apply discount code.
130
+ * ENHANCEMENT: Now hiding gateway setting API keys behind asterisks.
131
+ * ENHANCEMENT: Added some extra hooks to the edit membership levels page in the dashboard: pmpro_membership_level_after_billing_details_settings, pmpro_membership_level_after_other_settings, pmpro_membership_level_after_content_settings.
132
+ * ENHANCEMENT: Added a pmpro_after_order_settings_table hook to the edit order page in the dashboard.
133
+ * BUG FIX/ENHANCEMENT: Now passing a CARDONFILE parameter with PayPal Payflow payment and subscription transactions.
134
+ * BUG FIX/ENHANCEMENT: Using the wp.passwordStrength.userInputDisallowedList function from WP 4.5 if available.
135
+ * BUG FIX/EHNANCEMENT: Now making sure that the pmpro_update_order and pmpro_updated_order hooks fire whenever an order is updated in the DB.
136
+ * BUG FIX: Fixed issue in getfile script where parameters in the URL would cause File not found errors.
137
+ * BUG FIX: Fixed how the PayPal IPN handler handles cases where a subscription is set up correctly but the initial payment failed. We now correctly cancel these users and mark their order as error.
138
+ * BUG FIX: Improved error handling in the PayPal Express integration, particularly when a subscriptions PROFILESTATUS is missing.
139
+ * BUG FIX: User registered date is now shown in local time.
140
+ * BUG FIX: Fixed issue where the deprecated pmpro_getClassForField function wasn't returning a value properly. (Thanks, Elena Draculet)
141
+ * BUG FIX: Updated the pmpro_sort_levels_by_order function to use level IDs for keys, since some code expects that for level arrays. This matches the behavior we had before introducing this function.
142
+ * BUG FIX: Updated the pmpro_changeMembershipLevel function always set the order status to error if that was passed in as the "old level status".
143
+ * BUG FIX: Fixed warning in searches/pages when PMPro pages is not set.
144
+ * BUG FIX: Fixed warnings being generated when using PHP 8 and Divi
145
+ * BUG FIX: Fixed warnings related to PayPal Express session variables.
146
+
147
+ = 2.5.9.1 - 2021-05-12 =
148
+ * BUG FIX/ENHANCEMENT: Updated pmpro_changeMembershipLevel() to return null if the user's level is not changed. For the past 2 vesions, we've been returning true in these cases, which caused PMPro to send emails to the admin when the edit use page was saved, even if there was no level change. This change has been backported to versions 2.5.8 and 2.5.9.
149
+
150
+ = 2.5.9 - 2021-05-05 =
151
+ * ENHANCEMENT: Adjusting style for prices and price parts shown on the frontend.
152
+ * ENHANCEMENT: Adjusting HTML for links in the Orders table in the dashboard.
153
+ * BUG FIX: Reverted the change to the pmpro_is_checkout() function. Since we default to the first available level, calling pmpro_getLevelForCheckout() was causing pmpro_is_checkout to return true on ALL pages. This disrupted a lot of functionality.
154
+ * BUG FIX: Fixed warnings in the pmpro_getLevelAtCheckout() function.
155
+ * BUG FIX: Fixed issue where "All Time Sales" was showing up as 0, even when there were sales.
156
+
157
+ = 2.5.8 - 2021-04-30 =
158
+ * ENHANCEMENT: Added `pmpro_membership_content_filter` filter to let other plugins change how PMPro filters member content.
159
+ * ENHANCEMENT: Improved de_DE email template translation. (Thanks, biker238 on GitHub)
160
+ * ENHANCEMENT: Added `pmpro_change_level` filter.
161
+ * ENHANCEMENT: Improved display of prices on invoices and added pmpro_display_price_parts function and filters so plugins like the upcoming AvaTax add on can add subtotals to the price displays.
162
+ * ENHANCEMENT: Added a pmpro_after_all_membesrhip_level_changes hook that fires at the end of the page load and can be used to process all membership changes in bulk.
163
+ * ENHANCEMENT: The "User" column on the orders page now shows the username and email.
164
+ * ENHANCEMENT: Added a pmpro_stripe_create_subscription_array filter. (Thanks, ermGit on GitHub)
165
+ * BUG FIX/ENHANCEMENT: pmpro_change_level returns true now if the function is called to change a user’s level to one they already have.
166
+ * BUG FIX/ENHANCEMENT: No longer calling $order->updateTimestamp() on orders adminpage.
167
+ * BUG FIX/ENHANCEMENT: Updated conditional to check ‘street’ instead of ‘name’ when displaying billing address on Invoice/Confirmation.
168
+ * BUG FIX/ENHANCEMENT: Improved localization and added missing strings to translation.
169
+ * BUG FIX/ENHANCEMENT: Updated to use `get_user_locale1 to load localization.
170
+ * BUG FIX/ENHANCEMENT: Now Preserving existing values for `post__not_in` and `category__not_in` when filtering search and archive queries.
171
+ * BUG FIX/ENHANCEMENT: Fixed sorting of the Membership Level column on the Users List table in the WP admin dashboard.
172
+ * BUG FIX/ENHANCEMENT: Added a pmpro_sort_levels_by_order function and using it in various places to make sure levels are listed in the order they are in on the PMPro settings page.
173
+ * BUG FIX/ENHANCEMENT: Added an extra check in the pmpro_is_checkout function that helps with issues that were coming up in some add ons.
174
+ * BUG FIX/ENHANCEMENT: The level cache now takes into account the $include_active parameter.
175
+ * BUG FIX/ENHANCEMENT: The CSS class is now properly added to the body tag when a PMPro page block is used on a page.
176
+ * BUG FIX/EHNANCEMENT: Better timezone handling in sales reports.
177
+ * BUG FIX/ENHANCEMENT: Fixed a few places where we might think a free order was paid if using a currency with more or less than 2 decimal places.
178
+ * BUG FIX: Fixed deprecated jQuery functions in pmpro-admin.js.
179
+ * BUG FIX: Fixed warning for a missing/deleted level in the pmpro_post_classes function.
180
+ * BUG FIX: Default `pmpro_longform_address` to true on Billing Information page.
181
+ * BUG FIX: Fixed `pmpro_twocheckout_validate` filter.
182
+ * BUG FIX: Fixed variables passed to the `pmpro_discount_code_used` filter.
183
+ * BUG FIX: CZK currency should have 2 decimals.
184
+ * BUG FIX: Avoiding a redirect loop if the login page is deleted. (Thanks, George Stephanis)
185
+ * BUG FIX: Fixed the password reset link in new user notification email when not using pretty permalinks.
186
+ * BUG FIX: Fixed issues with password reset URLs on multisite networks.
187
+ * BUG FIX: Fixed the issue where sales weren't showing up on report charts sometimes on the 31st of the month.
188
+
189
+ = 2.5.7 - 2021-03-10 =
190
+ * ENHANCEMENT: Added a pmpro_checkout_message filter that can be used to filter error messages shown at checkout.
191
+ * BUG FIX/ENHANCEMENT: Now making sure some billing address fields are available for the billing failure emails sent during the PayPal IPN handler.
192
+ * BUG FIX/ENHANCEMENT: Fixed issues where HTML entities were shown in level prices in some places when using certain currencies. All prices are sent through a special pmpro_escape_price function that allows div, span, and sup tags with id and class attributes. Also removed from unneeded small tags and grey coloring of prices in certain spots.
193
+ * BUG FIX: Now cancelling membership when a SUBSCRIPTION_CANCELED message is sent to the Braintree webhook handler. In the past, we incorrectly sent the payment failed email instead.
194
+ * BUG FIX: Fixed display issues with the Require Membership block. The level select field has been swapped with a list of checkboxes.
195
+ * BUG FIX: Fixed warnings that occurred when processing failed payments in webhook and IPN handlers.
196
+ * BUG FIX: Fixed our Braintree class so we will only attempt to update a user's credit card and address when the getCustomer method is called at checkout or during a billing update.
197
+ * BUG FIX: Fixed issue where refreshing the checkout review page when using PayPal Express caused the associated order to be updated again. Now the order status is updated to review and only updates again when the user confirms.
198
+ * BUG FIX: Avoiding warnings when the pmpro_url function is used if the PMPro pages haven't been set up yet. (Thanks, Thomas Sjolshagen)
199
+ * REFACTOR: Updated the pmpro_getSpecificMembershipLevelForUser( $user_id, $level_id ) function so both fields are required. Will still default to the current user if null is passed for the $user_id.
200
+
201
+ = 2.5.6 - 2021-03-05 =
202
+ * SECURITY: Now sanitizing and escaping the `order` parameter when filtering the users table in the dashboard. (Thanks, Gen Sato)
203
+ * BUG FIX/ENHANCEMENT: Now hiding the ApplePay/GooglePay "Payment Request" buttons when the main checkout form is submitted. This helps to prevent double checkouts.
204
+ * BUG FIX: Fixed missing membership data in the billing failed email.
205
+
206
+ = 2.5.5 - 2021-02-22 =
207
+ * SECURITY: Better sanitization of parameters on some REST API endpoints.
208
+ * SECURITY: Now showing reCAPTCHA field at checkout even for logged in users.
209
+ * ENHANCEMENT: Added find_billing_address() method to the MemberOrder class. This will look for the address on the last order with the same sub id or in user meta.
210
+ * ENHANCEMENT: Better styling for invoices shown on the frontend.
211
+ * ENHANCEMENT: No longer forcing column width % in the members list table.
212
+ * ENHANCEMENT: Added a pmpro_doing_webhook action that is fired at the beginning of our webhook/IPN handlers.
213
+ * ENHANCEMENT: Added a pmpro_membership_level_after_billing_details_settings hook to the edit membership level page. This hook should now be used to add billing related settings.
214
+ * BUG FIX/ENHANCEMENT: Allowing order total to be set to 0, even if there is a subtotal and tax amount.
215
+ * BUG FIX/ENHANCEMENT: Stripe checkout fields will now use the language set in the Stripe settings.
216
+ * BUG FIX/ENHANCEMENT: The URL check in our notifications code now accepts arrays (e.g. to see if a URL has one of a group of top level domains). This fixes a warning some may have seen in error logs.
217
+ * BUG FIX: Fixed issues where totals on PayPal recurring payments were sometimes incorrect if both an mt_gross and amount field were passed via IPN.
218
+
219
+ = 2.5.4 - 2021-01-28 =
220
+ * ENHANCEMENT: Bump license year 2021 - 10 years.
221
+ * ENHANCEMENT: Now passing billing street in `pmpro_tax` filter.
222
+ * ENHANCEMENT: Prefixed our pmpro_stripeResponseHandler function to avoid conflicts.
223
+ * ENHANCEMENT: Added getRealPaymentTransactionId method to PayPal Express gateway class to recover a missing transaction ID.
224
+ * ENHANCEMENT: Added `pmpro_checkout_before_form` action to hook anything before the membership checkout form.
225
+ * ENHANCEMENT: Added avatar as a valid field type for the [pmpro_member] shortcode.
226
+ * ENHANCEMENT: Changed license key field to text type and unmasked. Masking implied the key was hashed before saving which is not true.
227
+ * ENHANCEMENT: Added`pmpro_discount_code_used` action hook for when a discount code is used.
228
+ * ENHANCEMENT: Stripe will now pull billing address info for recurring orders from webhooks.
229
+ * BUG FIX/ENHANCEMENT: Improved user interface, error handling, and messages in the frontend password reset process.
230
+ * BUG FIX/ENHANCEMENT: Added a space between state and zip code in billing info.
231
+ * BUG FIX/ENHANCEMENT: Now rounding amount sent with Stripe payment request button.
232
+ * BUG FIX/ENHANCEMENT: Improved `pmpro_check_plugin_version` function to also check a specific value of the `get_plugin_data` array.
233
+ * BUG FIX/ENHANCEMENT: Added `pmpro_membership_levelmeta` and `pmpro_membership_ordermeta` tables to uninstall process.
234
+ * BUG FIX/ENHANCEMENT: Escaped things in SQL queries in 2Checkout INS service handler.
235
+ * BUG FIX/ENHANCEMENT: Cleaned up levels page template and added MMPU compatibility.
236
+ * BUG FIX/ENHANCEMENT: Fixed pagination and export issues with a discount code filter on the Orders admin page.
237
+ * BUG FIX/ENHANCEMENT: Prefixed our `pmpro_stripeResponseHandler` function to avoid conflicts with other Stripe code that may not be prefixed.
238
+ * BUG FIX/ENHANCEMENT: Cleaned up conditionals and escaping improvements in the `pmpro_redirect_to_logged_in` function.
239
+ * BUG FIX/ENHANCEMENT: Fixed deprecation notices for sites running PHP 8.
240
+ * BUG FIX/ENHANCEMENT: Improved SQL query format in the applydiscountcode service.
241
+ * BUG FIX: Fixed issues with ReCAPTCHA v2 and certain gateways.
242
+ * BUG FIX: Fixed bug where blog name was not showing in Admin Activity email.
243
+ * BUG FIX: Improved incorrect PHP doc blocks.
244
+ * BUG FIX: Fixed an issue on some sites where password reset link in email was incorrect.
245
+ * BUG FIX: Fixed level change issues during 2Checkout checkout.
246
+ * BUG FIX: Fixed issue where `checkout_levels` REST API endpoint could return the wrong initial payment
247
+ * BUG FIX: Fixed undefined notice for timestamp variable in the Stripe gateway class.
248
+ * BUG FIX: Avoiding warnings when user ids are in the memberships_users table, but a user doesn't exist.
249
+ * BUG FIX: Now setting the correct value for membership_id in the admin change emails.
250
+
251
+ = 2.5.3 - 2021-01-26 =
252
+ * SECURITY: Fixed indirect object reference vulnerability where order information, including customer names, email addresses, and order numbers could be accessed by non-admin WordPress users. (Thanks, WP Plugins Team)
253
+ * SECURITY: Now checking ReCAPTCHA validation before enabling the submit button on the checkout form when using ReCAPTCHA v2. This helps to keep bad actors from testing credit cards on your checkout page. We were already doing a similar check when using ReCAPTCHA v3. Further updates to rate limit credit card failures are planned.
254
+
255
+ = 2.5.2 - 2020-10-23 =
256
+ * BUG FIX: Fixed issue where the RECAPTCHA library wasn't being loaded early enough to validate at checkout.
257
+ * BUG FIX: Fixed issue where code in the Stripe class was unsetting some required fields, even if Stripe was not being used at checkout.
258
+
259
+ = 2.5.1 - 2020-10-16 =
260
+ * SECURITY: Fixed XSS vulnerability on the Members List page of the dashboard. (Thanks, Ron Masas from Checkmarx.com)
261
+ * ENHANCEMENT: Add Ukrainian Hryvnia currency. (Thanks, Mirco Babini)
262
+ * ENHANCEMENT: Added a "non-members" option to the Beaver Build module.
263
+ * BUG FIX: Fixed issue where only USD and US were allowed with Stripe's GooglePay/ApplePay buttons.
264
+ * BUG FIX: Fixed issue where some profile fields, e.g. those added with Register Helper, were accidentally updated or removed when accessing the frontend profile page.
265
+ * BUG FIX: Fixed issue with tracking discount code uses when using the 2Checkout gateway. (Thanks, karambk on GitHub)
266
+ * BUG FIX: No longer running excerpts through wpautop when a more tag is used.
267
+
268
+ = 2.5 - 2020-10-02 =
269
+ * FEATURE: When using the Stripe Gateway, you may now allow users to pay using Apple Pay, Google Pay, or Microsoft Pay depending on their browser. Enable this feature from the payment settings page.
270
+ * FEATURE: Added Divi Builder compatibility.
271
+ * FEATURE: Updated the Braintree Gateway class to be able to use the Braintree API for the pmpro_next_payment() function. Note, for performance reasons, you must call this method directly or enable it by hooking it up with code like `add_filter('pmpro_next_payment', array('PMProGateway_braintree', 'pmpro_next_payment'), 10, 3);`
272
+ * FEATURE: Added ordermeta tables and functions. We will wait about a year for all users to upgrade before using these widespread. (Thanks, Mirco Babini)
273
+ * ENHANCEMENT: The "short" version of the level cost text for a free level is now "Free" instead of "0.00 now".
274
+ * ENHANCEMENT: Added a `get_original_subscription_order` method to the MemberOrder class. This will return the first order in a subscription when called from a recurring order.
275
+ * ENHANCEMENT: Removed the old style license nags.
276
+ * BUG FIX/ENHANCEMENT: Using microtime and a static counter int to make sure our order and discount codes are unique. In the past very high traffic sites could run into duplicates if two checkouts happened at the exact same second.
277
+ * BUG FIX/ENHANCEMENT: Adjust order delete prompt to support other locales.
278
+ * BUG FIX/ENHANCEMENT: Better handling of tax amounts in recurring payments, e.g. when using the PMPro VAT Tax add on.
279
+ * BUG FIX/ENHANCEMENT: Optimized how often we hit the Stripe API when events on the checkout page could potentially update the price of checkout.
280
+ * BUG FIX/ENHANCEMENT: The checkout_levels api call now takes `level` as param.
281
+ * BUG FIX/ENHANCEMENT: No longer running sanitize_text_field on password fields. This would break passwords that had strings of characters resembling html tags.
282
+ * BUG FIX/ENHANCEMENT: Now warning admins if the Stripe billing period is longer than 1 year. Billing periods greater than 1 year are not allowed by Stripe.
283
+ * BUG FIX/ENHANCEMENT: Now detecting when a Stripe webhook is set up for an older version of the Stripe API and showing a notice with a link to update.
284
+ * BUG FIX/ENHANCEMENT: Adding MAXFAILEDPAYMENTS=1 to PayPal add subscription requests. This tells PayPal to cancel a subscription after the first failed payment. In our experience, the automatic retries rarely worked well. This change fixes issues with subscriptions going out of sync or users retaining access to your site when their payment has failed. Members still receive the payment failed email, which prompts users to return to the site to renew.
285
+ * BUG FIX/ENHANCEMENT: Fixing some issues where we are adding extra break tags into the password reset email. There are still some issues like this when using certain plugins. We are working on a general fix.
286
+ * BUG FIX/ENHANCEMENT: Removed the "coupon amount" field from the edit order page. These were hold outs from the 2007! ecommerce plugin PMPro was forked from. You can set the pmpro_orders_show_coupon_amounts filter to __return_true to show these fields again if you were using them for tracking things in your custom code.
287
+ * BUG FIX: Fixed MMPU compatibility when using discount codes.
288
+ * BUG FIX: No longer filtering the wp login url when on wp-login.php. This fixes issues with iThemes Security 2FA.
289
+ * BUG FIX: Fixed issues where the Stripe webhook was not being updated sometimes when clicking the button to update.
290
+ * BUG FIX: Fixed some notices and warnings when using Braintree.
291
+ * BUG FIX: Now resetting memberslist page number when changing shown level.
292
+ * BUG FIX: Now ensuring that the discount code field updates, update the Request Button price.
293
+ * BUG FIX: Fixed issue where non-pretty permalinks may break frontend password resets.
294
+ * BUG FIX: Fixed invoice links on the account page. (Thanks, Mateusz Hołtyn)
295
+ * BUG FIX: Fixed incorrect label "for" attribute for uninstall setting.
296
+ * BUG FIX: Fixed issue where some free plugins distributed by PMPro would show warnings about requiring a Plus license.
297
+
298
  = 2.4.4 - 2020-09-02 =
299
  * BUG FIX: Fixed fatal error that sometimes occurred on the payment settings page when using PHP 5.6 or earlier.
300
  * BUG FIX: Fixed fatal errors that showed up on the frontend invoice page.
adminpages/admin_header.php CHANGED
@@ -193,7 +193,7 @@
193
  ?>
194
  <script>
195
  jQuery(document).ready(function() {
196
- jQuery.get('<?php echo get_admin_url(NULL, "/admin-ajax.php?action=pmpro_notifications" . $specific_notification ); ?>', function(data) {
197
  if(data && data != 'NULL')
198
  jQuery('#pmpro_notifications').html(data);
199
  });
@@ -211,6 +211,7 @@
211
  'pmpro-pagesettings',
212
  'pmpro-paymentsettings',
213
  'pmpro-emailsettings',
 
214
  'pmpro-advancedsettings',
215
  'pmpro-addons',
216
  'pmpro-license'
@@ -234,7 +235,7 @@
234
  <?php } ?>
235
 
236
  <?php if(current_user_can('pmpro_membershiplevels')) { ?>
237
- <a href="<?php echo admin_url('admin.php?page=pmpro-membershiplevels');?>" class="nav-tab<?php if( in_array( $view, array( 'pmpro-membershiplevels', 'pmpro-discountcodes', 'pmpro-pagesettings', 'pmpro-paymentsettings', 'pmpro-emailsettings', 'pmpro-advancedsettings' ) ) ) { ?> nav-tab-active<?php } ?>"><?php _e('Settings', 'paid-memberships-pro' );?></a>
238
  <?php } ?>
239
 
240
  <?php if(current_user_can('pmpro_addons')) { ?>
@@ -246,7 +247,7 @@
246
  <?php } ?>
247
  </nav>
248
 
249
- <?php if( $view == 'pmpro-membershiplevels' || $view == 'pmpro-discountcodes' || $view == 'pmpro-pagesettings' || $view == 'pmpro-paymentsettings' || $view == 'pmpro-emailsettings' || $view == 'pmpro-advancedsettings' ) { ?>
250
  <ul class="subsubsub">
251
  <?php if(current_user_can('pmpro_membershiplevels')) { ?>
252
  <li><a href="<?php echo admin_url('admin.php?page=pmpro-membershiplevels');?>" title="<?php _e('Membership Levels', 'paid-memberships-pro' );?>" class="<?php if($view == 'pmpro-membershiplevels') { ?>current<?php } ?>"><?php _e('Levels', 'paid-memberships-pro' );?></a>&nbsp;|&nbsp;</li>
@@ -265,7 +266,11 @@
265
  <?php } ?>
266
 
267
  <?php if(current_user_can('pmpro_emailsettings')) { ?>
268
- <li><a href="<?php echo admin_url('admin.php?page=pmpro-emailsettings');?>" title="<?php _e('Email Settings', 'paid-memberships-pro' );?>" class="<?php if($view == 'pmpro-emailsettings') { ?>current<?php } ?>"><?php _e('Email', 'paid-memberships-pro' );?></a>&nbsp;|&nbsp;</li>
 
 
 
 
269
  <?php } ?>
270
 
271
  <?php if(current_user_can('pmpro_advancedsettings')) { ?>
193
  ?>
194
  <script>
195
  jQuery(document).ready(function() {
196
+ jQuery.get('<?php echo admin_url( "/admin-ajax.php?action=pmpro_notifications" . $specific_notification ); ?>', function(data) {
197
  if(data && data != 'NULL')
198
  jQuery('#pmpro_notifications').html(data);
199
  });
211
  'pmpro-pagesettings',
212
  'pmpro-paymentsettings',
213
  'pmpro-emailsettings',
214
+ 'pmpro-emailtemplates',
215
  'pmpro-advancedsettings',
216
  'pmpro-addons',
217
  'pmpro-license'
235
  <?php } ?>
236
 
237
  <?php if(current_user_can('pmpro_membershiplevels')) { ?>
238
+ <a href="<?php echo admin_url('admin.php?page=pmpro-membershiplevels');?>" class="nav-tab<?php if( in_array( $view, array( 'pmpro-membershiplevels', 'pmpro-discountcodes', 'pmpro-pagesettings', 'pmpro-paymentsettings', 'pmpro-emailsettings', 'pmpro-emailtemplates', 'pmpro-advancedsettings' ) ) ) { ?> nav-tab-active<?php } ?>"><?php _e('Settings', 'paid-memberships-pro' );?></a>
239
  <?php } ?>
240
 
241
  <?php if(current_user_can('pmpro_addons')) { ?>
247
  <?php } ?>
248
  </nav>
249
 
250
+ <?php if( $view == 'pmpro-membershiplevels' || $view == 'pmpro-discountcodes' || $view == 'pmpro-pagesettings' || $view == 'pmpro-paymentsettings' || $view == 'pmpro-emailsettings' || $view == 'pmpro-emailtemplates' || $view == 'pmpro-advancedsettings' ) { ?>
251
  <ul class="subsubsub">
252
  <?php if(current_user_can('pmpro_membershiplevels')) { ?>
253
  <li><a href="<?php echo admin_url('admin.php?page=pmpro-membershiplevels');?>" title="<?php _e('Membership Levels', 'paid-memberships-pro' );?>" class="<?php if($view == 'pmpro-membershiplevels') { ?>current<?php } ?>"><?php _e('Levels', 'paid-memberships-pro' );?></a>&nbsp;|&nbsp;</li>
266
  <?php } ?>
267
 
268
  <?php if(current_user_can('pmpro_emailsettings')) { ?>
269
+ <li><a href="<?php echo admin_url('admin.php?page=pmpro-emailsettings');?>" title="<?php _e('Email Settings', 'paid-memberships-pro' );?>" class="<?php if($view == 'pmpro-emailsettings') { ?>current<?php } ?>"><?php _e('Email Settings', 'paid-memberships-pro' );?></a>&nbsp;|&nbsp;</li>
270
+ <?php } ?>
271
+
272
+ <?php if(current_user_can('pmpro_emailtemplates')) { ?>
273
+ <li><a href="<?php echo admin_url('admin.php?page=pmpro-emailtemplates');?>" title="<?php _e('Email Templates', 'paid-memberships-pro' );?>" class="<?php if($view == 'pmpro-emailtemplates') { ?>current<?php } ?>"><?php _e('Email Templates', 'paid-memberships-pro' );?></a>&nbsp;|&nbsp;</li>
274
  <?php } ?>
275
 
276
  <?php if(current_user_can('pmpro_advancedsettings')) { ?>
adminpages/advancedsettings.php CHANGED
@@ -38,6 +38,7 @@
38
 
39
  // Checkout settings.
40
  pmpro_setOption("tospage");
 
41
  pmpro_setOption("recaptcha");
42
  pmpro_setOption("recaptcha_version");
43
  pmpro_setOption("recaptcha_publickey");
@@ -83,6 +84,7 @@
83
 
84
  // Checkout settings.
85
  $tospage = pmpro_getOption("tospage");
 
86
  $recaptcha = pmpro_getOption("recaptcha");
87
  $recaptcha_version = pmpro_getOption("recaptcha_version");
88
  $recaptcha_publickey = pmpro_getOption("recaptcha_publickey");
@@ -236,6 +238,19 @@
236
  <p class="description"><?php _e('If yes, create a WordPress page containing your TOS agreement and assign it using the dropdown above.', 'paid-memberships-pro' );?></p>
237
  </td>
238
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  <tr>
240
  <th scope="row" valign="top">
241
  <label for="recaptcha"><?php _e('Use reCAPTCHA?', 'paid-memberships-pro' );?>:</label>
@@ -243,8 +258,8 @@
243
  <td>
244
  <select id="recaptcha" name="recaptcha" onchange="pmpro_updateRecaptchaTRs();">
245
  <option value="0" <?php if(!$recaptcha) { ?>selected="selected"<?php } ?>><?php _e('No', 'paid-memberships-pro' );?></option>
246
- <option value="1" <?php if($recaptcha == 1) { ?>selected="selected"<?php } ?>><?php _e('Yes - Free memberships only.', 'paid-memberships-pro' );?></option>
247
- <option value="2" <?php if($recaptcha == 2) { ?>selected="selected"<?php } ?>><?php _e('Yes - All memberships.', 'paid-memberships-pro' );?></option>
248
  </select>
249
  <p class="description"><?php _e('A free reCAPTCHA key is required.', 'paid-memberships-pro' );?> <a href="https://www.google.com/recaptcha/admin/create"><?php _e('Click here to signup for reCAPTCHA', 'paid-memberships-pro' );?></a>.</p>
250
  </td>
@@ -355,7 +370,16 @@ if ( function_exists( 'pmpro_displayAds' ) && pmpro_displayAds() ) {
355
  <label for="hideadslevels"><?php _e('Choose Levels to Hide Ads From', 'paid-memberships-pro' );?>:</label>
356
  </th>
357
  <td>
358
- <div class="checkbox_box" <?php if(count($levels) > 5) { ?>style="height: 100px; overflow: auto;"<?php } ?>>
 
 
 
 
 
 
 
 
 
359
  <?php
360
  $hideadslevels = pmpro_getOption("hideadslevels");
361
  if(!is_array($hideadslevels))
@@ -363,24 +387,18 @@ if ( function_exists( 'pmpro_displayAds' ) && pmpro_displayAds() ) {
363
 
364
  $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ";
365
  $levels = $wpdb->get_results($sqlQuery, OBJECT);
 
366
  foreach($levels as $level)
367
  {
368
  ?>
369
- <div class="clickable"><input type="checkbox" id="hideadslevels_<?php echo $level->id?>" name="hideadslevels[]" value="<?php echo $level->id?>" <?php if(in_array($level->id, $hideadslevels)) { ?>checked="checked"<?php } ?>> <?php echo $level->name?></div>
 
 
 
370
  <?php
371
  }
372
  ?>
373
  </div>
374
- <script>
375
- jQuery('.checkbox_box input').click(function(event) {
376
- event.stopPropagation()
377
- });
378
-
379
- jQuery('.checkbox_box div.clickable').click(function() {
380
- var checkbox = jQuery(this).find(':checkbox');
381
- checkbox.attr('checked', !checkbox.attr('checked'));
382
- });
383
- </script>
384
  </td>
385
  </tr>
386
  <?php if(is_multisite()) { ?>
@@ -469,7 +487,7 @@ if ( function_exists( 'pmpro_displayAds' ) && pmpro_displayAds() ) {
469
  ?>
470
  <tr>
471
  <th scope="row" valign="top">
472
- <label for="showexcerpts"><?php _e('Uninstall PMPro on deletion?', 'paid-memberships-pro' );?></label>
473
  </th>
474
  <td>
475
  <select id="uninstall" name="uninstall">
@@ -519,8 +537,7 @@ if ( function_exists( 'pmpro_displayAds' ) && pmpro_displayAds() ) {
519
  }
520
  pmpro_updateRecaptchaTRs();
521
  </script>
522
- </div> <!-- end pmpro_admin_section-other-settings -->
523
-
524
  <p class="submit">
525
  <input name="savesettings" type="submit" class="button button-primary" value="<?php _e('Save Settings', 'paid-memberships-pro' );?>" />
526
  </p>
38
 
39
  // Checkout settings.
40
  pmpro_setOption("tospage");
41
+ pmpro_setOption("spamprotection");
42
  pmpro_setOption("recaptcha");
43
  pmpro_setOption("recaptcha_version");
44
  pmpro_setOption("recaptcha_publickey");
84
 
85
  // Checkout settings.
86
  $tospage = pmpro_getOption("tospage");
87
+ $spamprotection = pmpro_getOption("spamprotection");
88
  $recaptcha = pmpro_getOption("recaptcha");
89
  $recaptcha_version = pmpro_getOption("recaptcha_version");
90
  $recaptcha_publickey = pmpro_getOption("recaptcha_publickey");
238
  <p class="description"><?php _e('If yes, create a WordPress page containing your TOS agreement and assign it using the dropdown above.', 'paid-memberships-pro' );?></p>
239
  </td>
240
  </tr>
241
+ <tr>
242
+ <th scope="row" valign="top">
243
+ <label for="spamprotection"><?php _e('Enable Spam Protection?', 'paid-memberships-pro' );?>:</label>
244
+ </th>
245
+ <td>
246
+ <select id="spamprotection" name="spamprotection">
247
+ <option value="0" <?php if(!$spamprotection) { ?>selected="selected"<?php } ?>><?php _e('No', 'paid-memberships-pro' );?></option>
248
+ <!-- For reference, removed the Yes - Free memberships only. option -->
249
+ <option value="2" <?php if( $spamprotection > 0 ) { ?>selected="selected"<?php } ?>><?php _e('Yes - Enable Spam Protection', 'paid-memberships-pro' );?></option>
250
+ </select>
251
+ <p class="description"><?php printf( __( 'Block IPs from checkout if there are more than %d failures within %d minutes.', 'paid-memberships-pro' ), PMPRO_SPAM_ACTION_NUM_LIMIT, round(PMPRO_SPAM_ACTION_TIME_LIMIT/60,2) );?></p>
252
+ </td>
253
+ </tr>
254
  <tr>
255
  <th scope="row" valign="top">
256
  <label for="recaptcha"><?php _e('Use reCAPTCHA?', 'paid-memberships-pro' );?>:</label>
258
  <td>
259
  <select id="recaptcha" name="recaptcha" onchange="pmpro_updateRecaptchaTRs();">
260
  <option value="0" <?php if(!$recaptcha) { ?>selected="selected"<?php } ?>><?php _e('No', 'paid-memberships-pro' );?></option>
261
+ <!-- For reference, removed the Yes - Free memberships only. option -->
262
+ <option value="2" <?php if( $recaptcha > 0 ) { ?>selected="selected"<?php } ?>><?php _e('Yes - All memberships.', 'paid-memberships-pro' );?></option>
263
  </select>
264
  <p class="description"><?php _e('A free reCAPTCHA key is required.', 'paid-memberships-pro' );?> <a href="https://www.google.com/recaptcha/admin/create"><?php _e('Click here to signup for reCAPTCHA', 'paid-memberships-pro' );?></a>.</p>
265
  </td>
370
  <label for="hideadslevels"><?php _e('Choose Levels to Hide Ads From', 'paid-memberships-pro' );?>:</label>
371
  </th>
372
  <td>
373
+ <?php
374
+ // Build the selectors for the checkbox list based on number of levels.
375
+ $classes = array();
376
+ $classes[] = "pmpro_checkbox_box";
377
+ if ( count( $levels ) > 5 ) {
378
+ $classes[] = "pmpro_scrollable";
379
+ }
380
+ $class = implode( ' ', array_unique( $classes ) );
381
+ ?>
382
+ <div class="<?php echo esc_attr( $class ); ?>">
383
  <?php
384
  $hideadslevels = pmpro_getOption("hideadslevels");
385
  if(!is_array($hideadslevels))
387
 
388
  $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ";
389
  $levels = $wpdb->get_results($sqlQuery, OBJECT);
390
+ $levels = pmpro_sort_levels_by_order( $levels );
391
  foreach($levels as $level)
392
  {
393
  ?>
394
+ <div class="pmpro_clickable">
395
+ <input type="checkbox" id="hideadslevels_<?php echo esc_attr( $level->id ); ?>" name="hideadslevels[]" value="<?php echo esc_attr( $level->id); ?>" <?php checked( in_array( $level->id, $hideadslevels ), true ); ?>>
396
+ <label for="hideadslevels_<?php echo esc_attr( $level->id ); ?>"><?php echo esc_html( $level->name ); ?></label>
397
+ </div>
398
  <?php
399
  }
400
  ?>
401
  </div>
 
 
 
 
 
 
 
 
 
 
402
  </td>
403
  </tr>
404
  <?php if(is_multisite()) { ?>
487
  ?>
488
  <tr>
489
  <th scope="row" valign="top">
490
+ <label for="uninstall"><?php _e('Uninstall PMPro on deletion?', 'paid-memberships-pro' );?></label>
491
  </th>
492
  <td>
493
  <select id="uninstall" name="uninstall">
537
  }
538
  pmpro_updateRecaptchaTRs();
539
  </script>
540
+ </div> <!-- end pmpro_admin_section-other-settings -->
 
541
  <p class="submit">
542
  <input name="savesettings" type="submit" class="button button-primary" value="<?php _e('Save Settings', 'paid-memberships-pro' );?>" />
543
  </p>
adminpages/dashboard.php CHANGED
@@ -143,6 +143,10 @@ function pmpro_dashboard_welcome_callback() { ?>
143
  <li><a href="<?php echo admin_url( 'admin.php?page=pmpro-emailsettings' );?>"><i class="dashicons dashicons-email"></i> <?php echo esc_attr_e( 'Confirm Email Settings', 'paid-memberships-pro' );?></a></li>
144
  <?php } ?>
145
 
 
 
 
 
146
  <?php if ( current_user_can( 'pmpro_advancedsettings' ) ) { ?>
147
  <li><a href="<?php echo admin_url( 'admin.php?page=pmpro-advancedsettings' );?>"><i class="dashicons dashicons-admin-settings"></i> <?php echo esc_attr_e( 'View Advanced Settings', 'paid-memberships-pro' ); ?></a></li>
148
  <?php } ?>
@@ -178,11 +182,11 @@ function pmpro_dashboard_welcome_callback() { ?>
178
  <?php } ?>
179
 
180
  <?php if ( ! pmpro_license_isValid() ) { ?>
181
- <p><?php esc_html_e( 'An annual support license is recommended for websites running Paid Memberships Pro.', 'paid-memberships-pro' ); ?><br /><a href="https://www.paidmembershipspro.com/pricing/?utm_source=plugin&utm_medium=pmpro-dashboard&utm_campaign=pricing&utm_content=upgrade" target="_blank"><?php esc_html_e( 'View Pricing &raquo;' , 'paid-memberships-pro' ); ?></a></p>
182
- <p><a href="https://www.paidmembershipspro.com/membership-checkout/?level=20&utm_source=plugin&utm_medium=pmpro-dashboard&utm_campaign=plus-checkout&utm_content=upgrade" target="_blank" class="button button-action button-hero"><?php esc_attr_e( 'Upgrade', 'paid-memberships-pro' ); ?></a>
183
  <?php } ?>
184
- <hr />
185
- <p><?php echo wp_kses_post( sprintf( __( 'Paid Memberships Pro and our add ons are distributed under the <a target="_blank" href="%s">GPLv2 license</a>. This means, among other things, that you may use the software on this site or any other site free of charge.', 'paid-memberships-pro' ), 'http://www.gnu.org/licenses/gpl-2.0.html' ) ); ?></p>
186
  </div> <!-- end pmpro-dashboard-welcome-column -->
187
  <div class="pmpro-dashboard-welcome-column">
188
  <h3><?php esc_html_e( 'Get Involved', 'paid-memberships-pro' ); ?></h3>
@@ -244,7 +248,7 @@ function pmpro_dashboard_report_recent_members_callback() {
244
  </strong>
245
  </td>
246
  <td><?php esc_attr_e( $auser->membership ); ?></td>
247
- <td><?php echo date_i18n( get_option( 'date_format' ), strtotime( $theuser->user_registered, current_time( 'timestamp' ) ) ); ?></td>
248
  <td>
249
  <?php
250
  if($auser->enddate)
@@ -295,7 +299,7 @@ function pmpro_dashboard_report_recent_orders_callback() {
295
  <?php
296
  if ( empty( $order_ids ) ) { ?>
297
  <tr>
298
- <td colspan="8"><p><?php _e( 'No orders found.', 'paid-memberships-pro' ); ?></p></td>
299
  </tr>
300
  <?php } else {
301
  foreach ( $order_ids as $order_id ) {
@@ -333,7 +337,7 @@ function pmpro_dashboard_report_recent_orders_callback() {
333
  <?php }
334
  ?>
335
  </td>
336
- <td><?php echo pmpro_formatPrice( $order->total ); ?></td>
337
  <td>
338
  <?php echo $order->gateway; ?>
339
  <?php if ( $order->gateway_environment == 'test' ) {
143
  <li><a href="<?php echo admin_url( 'admin.php?page=pmpro-emailsettings' );?>"><i class="dashicons dashicons-email"></i> <?php echo esc_attr_e( 'Confirm Email Settings', 'paid-memberships-pro' );?></a></li>
144
  <?php } ?>
145
 
146
+ <?php if ( current_user_can( 'pmpro_emailtemplates' ) ) { ?>
147
+ <li><a href="<?php echo admin_url( 'admin.php?page=pmpro-emailtemplates' );?>"><i class="dashicons dashicons-editor-spellcheck"></i> <?php echo esc_attr_e( 'Customize Email Templates', 'paid-memberships-pro' );?></a></li>
148
+ <?php } ?>
149
+
150
  <?php if ( current_user_can( 'pmpro_advancedsettings' ) ) { ?>
151
  <li><a href="<?php echo admin_url( 'admin.php?page=pmpro-advancedsettings' );?>"><i class="dashicons dashicons-admin-settings"></i> <?php echo esc_attr_e( 'View Advanced Settings', 'paid-memberships-pro' ); ?></a></li>
152
  <?php } ?>
182
  <?php } ?>
183
 
184
  <?php if ( ! pmpro_license_isValid() ) { ?>
185
+ <p><?php esc_html_e( 'An annual support license is recommended for websites running Paid Memberships Pro.', 'paid-memberships-pro' ); ?></p>
186
+ <p><a href="https://www.paidmembershipspro.com/pricing/?utm_source=plugin&utm_medium=pmpro-dashboard&utm_campaign=pricing&utm_content=upgrade" target="_blank" class="button button-primary button-hero"><?php esc_attr_e( 'View Plans and Pricing', 'paid-memberships-pro' ); ?></a>
187
  <?php } ?>
188
+ <hr />
189
+ <p><?php echo wp_kses_post( sprintf( __( 'Paid Memberships Pro and our Add Ons are distributed under the <a target="_blank" href="%s">GPLv2 license</a>. This means, among other things, that you may use the software on this site or any other site free of charge.', 'paid-memberships-pro' ), 'http://www.gnu.org/licenses/gpl-2.0.html' ) ); ?></p>
190
  </div> <!-- end pmpro-dashboard-welcome-column -->
191
  <div class="pmpro-dashboard-welcome-column">
192
  <h3><?php esc_html_e( 'Get Involved', 'paid-memberships-pro' ); ?></h3>
248
  </strong>
249
  </td>
250
  <td><?php esc_attr_e( $auser->membership ); ?></td>
251
+ <td><?php echo date_i18n( get_option( 'date_format' ), strtotime( get_date_from_gmt( $theuser->user_registered ), current_time( 'timestamp' ) ) ); ?></td>
252
  <td>
253
  <?php
254
  if($auser->enddate)
299
  <?php
300
  if ( empty( $order_ids ) ) { ?>
301
  <tr>
302
+ <td colspan="6"><p><?php _e( 'No orders found.', 'paid-memberships-pro' ); ?></p></td>
303
  </tr>
304
  <?php } else {
305
  foreach ( $order_ids as $order_id ) {
337
  <?php }
338
  ?>
339
  </td>
340
+ <td><?php echo pmpro_escape_price( pmpro_formatPrice( $order->total ) ); ?></td>
341
  <td>
342
  <?php echo $order->gateway; ?>
343
  <?php if ( $order->gateway_environment == 'test' ) {
adminpages/discountcodes.php CHANGED
@@ -6,7 +6,7 @@
6
  }
7
 
8
  //vars
9
- global $wpdb, $pmpro_currency_symbol, $pmpro_stripe_error, $pmpro_braintree_error, $pmpro_payflow_error, $pmpro_twocheckout_error;
10
 
11
  $now = current_time( 'timestamp' );
12
 
@@ -17,6 +17,8 @@
17
 
18
  if(isset($_REQUEST['copy']))
19
  $copy = intval($_REQUEST['copy']);
 
 
20
 
21
  if(isset($_REQUEST['delete']))
22
  $delete = intval($_REQUEST['delete']);
@@ -58,7 +60,7 @@
58
  $start = $end - $limit;
59
 
60
  //check nonce for saving codes
61
- if (!empty($_REQUEST['saveid']) && (empty($_REQUEST['pmpro_discountcodes_nonce']) || !check_admin_referer('save', 'pmpro_discountcodes_nonce'))) {
62
  $pmpro_msgt = 'error';
63
  $pmpro_msg = __("Are you sure you want to do that? Try again.", 'paid-memberships-pro' );
64
  $saveid = false;
@@ -306,7 +308,7 @@
306
  }
307
 
308
  //check nonce for deleting codes
309
- if (!empty($_REQUEST['delete']) && (empty($_REQUEST['pmpro_discountcodes_nonce']) || !check_admin_referer('delete', 'pmpro_discountcodes_nonce'))) {
310
  $pmpro_msgt = 'error';
311
  $pmpro_msg = __("Are you sure you want to do that? Try again.", 'paid-memberships-pro' );
312
  $delete = false;
@@ -353,10 +355,10 @@
353
  $pmpro_msgt = "error";
354
  }
355
  }
356
-
357
  if( ! empty( $pmpro_msg ) && ! empty( $expiration_warning_flag ) ) {
358
  $pmpro_msg .= ' <strong>' . sprintf( __( 'WARNING: A level was set with both a recurring billing amount and an expiration date. You only need to set one of these unless you really want this membership to expire after a specific time period. For more information, <a target="_blank" href="%s">see our post here</a>.', 'paid-memberships-pro' ), 'https://www.paidmembershipspro.com/important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels/?utm_source=plugin&utm_medium=pmpro-discountcodes&utm_campaign=blog&utm_content=important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels' ) . '</strong>';
359
-
360
  if( $pmpro_msgt == 'success' ) {
361
  $pmpro_msgt = 'warning';
362
  }
@@ -415,7 +417,7 @@
415
  $copy ),
416
  OBJECT
417
  );
418
-
419
  $temp_code = $code;
420
  }
421
 
@@ -427,7 +429,7 @@
427
  {
428
  $code = new stdClass();
429
  $code->code = pmpro_getDiscountCode();
430
-
431
  if( ! empty( $copy ) && $copy > 0 ) {
432
  $code->starts = $temp_code->starts;
433
  $code->expires = $temp_code->expires;
@@ -436,18 +438,18 @@
436
  }
437
  ?>
438
  <form action="" method="post">
439
- <input name="saveid" type="hidden" value="<?php echo $edit?>" />
440
  <?php wp_nonce_field('save', 'pmpro_discountcodes_nonce');?>
441
  <table class="form-table">
442
  <tbody>
443
  <tr>
444
  <th scope="row" valign="top"><label><?php _e('ID', 'paid-memberships-pro' );?>:</label></th>
445
- <td><p class="description"><?php if(!empty($code->id)) echo $code->id; else echo __("This will be generated when you save.", 'paid-memberships-pro' );?></p></td>
446
  </tr>
447
 
448
  <tr>
449
  <th scope="row" valign="top"><label for="code"><?php _e('Code', 'paid-memberships-pro' );?>:</label></th>
450
- <td><input name="code" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($code->code))?>" /></td>
451
  </tr>
452
 
453
  <?php
@@ -496,8 +498,8 @@
496
  }
497
  ?>
498
  </select>
499
- <input name="starts_day" type="text" size="2" value="<?php echo $selected_starts_day?>" />
500
- <input name="starts_year" type="text" size="4" value="<?php echo $selected_starts_year?>" />
501
  </td>
502
  </tr>
503
 
@@ -514,15 +516,15 @@
514
  }
515
  ?>
516
  </select>
517
- <input name="expires_day" type="text" size="2" value="<?php echo $selected_expires_day?>" />
518
- <input name="expires_year" type="text" size="4" value="<?php echo $selected_expires_year?>" />
519
  </td>
520
  </tr>
521
 
522
  <tr>
523
  <th scope="row" valign="top"><label for="uses"><?php _e('Uses', 'paid-memberships-pro' );?>:</label></th>
524
  <td>
525
- <input name="uses" type="text" size="10" value="<?php if(!empty($code->uses)) echo str_replace("\"", "&quot;", stripslashes($code->uses));?>" />
526
  <p class="description"><?php _e('Leave blank for unlimited uses.', 'paid-memberships-pro' );?></p>
527
  </td>
528
  </tr>
@@ -537,6 +539,7 @@
537
  <div class="pmpro_discount_levels">
538
  <?php
539
  $levels = $wpdb->get_results("SELECT * FROM $wpdb->pmpro_membership_levels");
 
540
  foreach($levels as $level)
541
  {
542
  //if this level is already managed for this discount code, use the code values
@@ -563,10 +566,10 @@
563
  $level_checked = false;
564
  ?>
565
  <div class="pmpro_discount_level <?php if ( ! pmpro_check_discount_code_level_for_gateway_compatibility( $level ) ) { ?>pmpro_error<?php } ?>">
566
- <input type="hidden" name="all_levels[]" value="<?php echo $level->id?>" />
567
- <input type="checkbox" id="levels_<?php echo $level->id;?>" name="levels[]" value="<?php echo $level->id?>" <?php if(!empty($level->checked)) { ?>checked="checked"<?php } ?> onclick="if(jQuery(this).is(':checked')) jQuery(this).next().next().show(); else jQuery(this).next().next().hide();" />
568
- <label for="levels_<?php echo $level->id;?>"><?php echo $level->name?></label>
569
- <div class="pmpro_discount_levels_pricing level_<?php echo $level->id?>" <?php if(empty($level->checked)) { ?>style="display: none;"<?php } ?>>
570
  <table class="form-table">
571
  <tbody>
572
  <tr>
@@ -587,7 +590,7 @@
587
 
588
  <tr>
589
  <th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'paid-memberships-pro' );?>:</label></th>
590
- <td><input class="recurring_checkbox" id="recurring_<?php echo $level->id;?>" name="recurring[]" type="checkbox" value="<?php echo $level->id?>" <?php if(pmpro_isLevelRecurring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).prop('checked')) { jQuery(this).parent().parent().siblings('.recurring_info').show(); if(!jQuery('#custom_trial_<?php echo $level->id?>').is(':checked')) jQuery(this).parent().parent().siblings('.trial_info').hide();} else jQuery(this).parent().parent().siblings('.recurring_info').hide();" /> <label for="recurring_<?php echo $level->id;?>"><?php _e('Check if this level has a recurring subscription payment.', 'paid-memberships-pro' );?></label></td>
591
  </tr>
592
 
593
  <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
@@ -603,7 +606,7 @@
603
  echo $pmpro_currency_symbol;
604
  ?>
605
  <?php _e('per', 'paid-memberships-pro' ); ?>
606
- <input name="cycle_number[]" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->cycle_number))?>" />
607
  <select name="cycle_period[]">
608
  <?php
609
  $cycles = array( __('Day(s)', 'paid-memberships-pro' ) => 'Day', __('Week(s)', 'paid-memberships-pro' ) => 'Week', __('Month(s)', 'paid-memberships-pro' ) => 'Month', __('Year(s)', 'paid-memberships-pro' ) => 'Year' );
@@ -617,14 +620,16 @@
617
  <p class="description"><?php _e('The amount to be billed one cycle after the initial payment.', 'paid-memberships-pro' );?></p>
618
  <?php if($gateway == "braintree") { ?>
619
  <strong <?php if(!empty($pmpro_braintree_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Braintree integration currently only supports billing periods of "Month" or "Year".', 'paid-memberships-pro' );?></strong>
620
- <?php } ?>
 
 
621
  </td>
622
  </tr>
623
 
624
  <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
625
  <th scope="row" valign="top"><label for="billing_limit"><?php _e('Billing Cycle Limit', 'paid-memberships-pro' );?>:</label></th>
626
  <td>
627
- <input name="billing_limit[]" type="text" size="20" value="<?php echo $level->billing_limit?>" />
628
  <p class="description">
629
  <?php _e('The <strong>total</strong> number of recurring billing cycles for this level, including the trial period (if applicable) but not including the initial payment. Set to zero if membership is indefinite.', 'paid-memberships-pro' );?>
630
  <?php if ( ( $gateway == "stripe" ) && ! function_exists( 'pmprosbl_plugin_row_meta' ) ) { ?>
@@ -647,7 +652,7 @@
647
  <tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
648
  <th scope="row" valign="top"><label><?php _e('Custom Trial', 'paid-memberships-pro' );?>:</label></th>
649
  <td>
650
- <input id="custom_trial_<?php echo $level->id?>" id="custom_trial_<?php echo $level->id;?>" name="custom_trial[]" type="checkbox" value="<?php echo $level->id?>" <?php if ( pmpro_isLevelTrial($level) ) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).prop('checked')) jQuery(this).parent().parent().siblings('.trial_info').show(); else jQuery(this).parent().parent().siblings('.trial_info').hide();" /> <label for="custom_trial_<?php echo $level->id;?>"><?php _e('Check to add a custom trial period.', 'paid-memberships-pro' );?></label>
651
  <?php if($gateway == "twocheckout") { ?>
652
  <p class="description"><strong <?php if(!empty($pmpro_twocheckout_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('2Checkout integration does not support custom trials. You can do one period trials by setting an initial payment different from the billing amount.', 'paid-memberships-pro' );?></strong></p>
653
  <?php } ?>
@@ -667,7 +672,7 @@
667
  echo $pmpro_currency_symbol;
668
  ?>
669
  <?php _e('for the first', 'paid-memberships-pro' );?>
670
- <input name="trial_limit[]" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->trial_limit))?>" />
671
  <?php _e('subscription payments', 'paid-memberships-pro' );?>.
672
  <?php if($gateway == "stripe") { ?>
673
  <p class="description"><strong <?php if(!empty($pmpro_stripe_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Stripe integration currently does not support trial amounts greater than $0.', 'paid-memberships-pro' );?></strong></p>
@@ -681,17 +686,19 @@
681
 
682
  <tr>
683
  <th scope="row" valign="top"><label><?php _e('Membership Expiration', 'paid-memberships-pro' );?>:</label></th>
684
- <td><input id="expiration_<?php echo $level->id;?>" name="expiration[]" type="checkbox" value="<?php echo $level->id?>" <?php if(pmpro_isLevelExpiring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).is(':checked')) { jQuery(this).parent().parent().siblings('.expiration_info').show(); } else { jQuery(this).parent().parent().siblings('.expiration_info').hide();}" /> <label for="expiration_<?php echo $level->id;?>"><?php _e('Check this to set when membership access expires.', 'paid-memberships-pro' );?></label></td>
685
  </tr>
686
 
687
  <tr class="expiration_info" <?php if(!pmpro_isLevelExpiring($level)) {?>style="display: none;"<?php } ?>>
688
  <th scope="row" valign="top"><label for="billing_amount"><?php _e('Expires In', 'paid-memberships-pro' );?>:</label></th>
689
  <td>
690
- <input id="expiration_number" name="expiration_number[]" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->expiration_number))?>" />
691
  <select id="expiration_period" name="expiration_period[]">
692
  <?php
693
- $cycles = array( __('Day(s)', 'paid-memberships-pro' ) => 'Day', __('Week(s)', 'paid-memberships-pro' ) => 'Week', __('Month(s)', 'paid-memberships-pro' ) => 'Month', __('Year(s)', 'paid-memberships-pro' ) => 'Year' );
 
694
  foreach ( $cycles as $name => $value ) {
 
695
  echo "<option value='$value'";
696
  if ( $level->expiration_period == $value ) echo " selected='selected'";
697
  echo ">$name</option>";
@@ -719,7 +726,7 @@
719
 
720
  <p class="submit topborder">
721
  <input name="save" type="submit" class="button button-primary" value="Save Code" />
722
- <input name="cancel" type="button" class="button" value="Cancel" onclick="location.href='<?php echo get_admin_url(NULL, '/admin.php?page=pmpro-discountcodes')?>';" />
723
  </p>
724
  </form>
725
  </div>
@@ -765,8 +772,8 @@
765
  <p class="search-box">
766
  <label class="screen-reader-text" for="post-search-input"><?php _e('Search Discount Codes', 'paid-memberships-pro' );?>:</label>
767
  <input type="hidden" name="page" value="pmpro-discountcodes" />
768
- <input id="post-search-input" type="text" value="<?php if(!empty($s)) echo $s;?>" name="s" size="30" />
769
- <input class="button" type="submit" value="<?php _e('Search', 'paid-memberships-pro' );?>" id="search-submit "/>
770
  </p>
771
  </form>
772
 
@@ -790,7 +797,7 @@
790
  <td colspan="6">
791
  <?php echo esc_attr_e( 'Code not found.', 'paid-memberships-pro' ); ?>
792
  </td>
793
- </tr>
794
  <?php } ?>
795
  <?php
796
  foreach($codes as $code) {
@@ -799,22 +806,109 @@
799
  <tr<?php if ( ! pmpro_check_discount_code_for_gateway_compatibility( $code->id ) ) { ?> class="pmpro_error"<?php } ?>>
800
  <td><?php echo $code->id?></td>
801
  <td class="has-row-actions">
802
- <a title="<?php echo sprintf( 'Edit Code: %s', $code->code ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $code->id ), admin_url('admin.php' ) ); ?>"><?php echo $code->code?></a>
803
  <div class="row-actions">
804
- <span class="edit">
805
- <a title="<?php _e( 'Edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $code->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Edit', 'paid-memberships-pro' ); ?></a>
806
- </span> |
807
- <span class="copy">
808
- <a title="<?php _e( 'Copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => -1, 'copy' => $code->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Copy', 'paid-memberships-pro' ); ?></a>
809
- </span> |
810
- <span class="delete">
811
- <a title="<?php _e( 'Delete', 'paid-memberships-pro' ); ?>" href="javascript:pmpro_askfirst('<?php echo str_replace("'", "\'", sprintf(__('Are you sure you want to delete the %s discount code? The subscriptions for existing users will not change, but new users will not be able to use this code anymore.', 'paid-memberships-pro' ), $code->code));?>', '<?php echo wp_nonce_url(add_query_arg( array( 'page' => 'pmpro-discountcodes', 'delete' => $code->id), admin_url( 'admin.php' ) ), 'delete', 'pmpro_discountcodes_nonce'); ?>'); void(0);"><?php _e('Delete', 'paid-memberships-pro' ); ?></a>
812
- </span>
813
- <?php if ( (int)$uses > 0 ) { ?>
814
- | <span class="orders">
815
- <a title="<?php _e(' View Orders', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'discount_code' => $code->id, 'filter' => 'with-discount-code' ), admin_url('admin.php' ) ); ?>"><?php _e( 'Orders', 'paid-memberships-pro' ); ?></a>
816
- </span>
817
- <?php } ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
  </div>
819
  </td>
820
  <td>
@@ -845,7 +939,11 @@
845
 
846
  $level_names = array();
847
  foreach( $levels as $level ) {
848
- $level_names[] = '<a title="' . pmpro_url( 'checkout', '?level=' . $level->id . '&discount_code=' . $code->code) . '" target="_blank" href="' . pmpro_url( 'checkout', '?level=' . $level->id . '&discount_code=' . $code->code) . '">' . $level->name . '</a>';
 
 
 
 
849
  }
850
  if( $level_names ) {
851
  echo implode( ', ', $level_names );
@@ -864,7 +962,7 @@
864
  </table>
865
 
866
  <?php
867
- $pagination_url = get_admin_url( null, "/admin.php?page=pmpro-discountcodes&s=" . $s );
868
  echo pmpro_getPaginationString( $pn, $totalrows, $limit, 1, $pagination_url, "&limit=$limit&pn=" );
869
  ?>
870
 
6
  }
7
 
8
  //vars
9
+ global $wpdb, $pmpro_currency_symbol, $pmpro_stripe_error, $pmpro_braintree_error, $pmpro_payflow_error, $pmpro_twocheckout_error, $pmpro_pages, $gateway;
10
 
11
  $now = current_time( 'timestamp' );
12
 
17
 
18
  if(isset($_REQUEST['copy']))
19
  $copy = intval($_REQUEST['copy']);
20
+ else
21
+ $copy = false;
22
 
23
  if(isset($_REQUEST['delete']))
24
  $delete = intval($_REQUEST['delete']);
60
  $start = $end - $limit;
61
 
62
  //check nonce for saving codes
63
+ if (!empty($saveid) && (empty($_REQUEST['pmpro_discountcodes_nonce']) || !check_admin_referer('save', 'pmpro_discountcodes_nonce'))) {
64
  $pmpro_msgt = 'error';
65
  $pmpro_msg = __("Are you sure you want to do that? Try again.", 'paid-memberships-pro' );
66
  $saveid = false;
308
  }
309
 
310
  //check nonce for deleting codes
311
+ if (!empty($delete) && (empty($_REQUEST['pmpro_discountcodes_nonce']) || !check_admin_referer('delete', 'pmpro_discountcodes_nonce'))) {
312
  $pmpro_msgt = 'error';
313
  $pmpro_msg = __("Are you sure you want to do that? Try again.", 'paid-memberships-pro' );
314
  $delete = false;
355
  $pmpro_msgt = "error";
356
  }
357
  }
358
+
359
  if( ! empty( $pmpro_msg ) && ! empty( $expiration_warning_flag ) ) {
360
  $pmpro_msg .= ' <strong>' . sprintf( __( 'WARNING: A level was set with both a recurring billing amount and an expiration date. You only need to set one of these unless you really want this membership to expire after a specific time period. For more information, <a target="_blank" href="%s">see our post here</a>.', 'paid-memberships-pro' ), 'https://www.paidmembershipspro.com/important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels/?utm_source=plugin&utm_medium=pmpro-discountcodes&utm_campaign=blog&utm_content=important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels' ) . '</strong>';
361
+
362
  if( $pmpro_msgt == 'success' ) {
363
  $pmpro_msgt = 'warning';
364
  }
417
  $copy ),
418
  OBJECT
419
  );
420
+
421
  $temp_code = $code;
422
  }
423
 
429
  {
430
  $code = new stdClass();
431
  $code->code = pmpro_getDiscountCode();
432
+
433
  if( ! empty( $copy ) && $copy > 0 ) {
434
  $code->starts = $temp_code->starts;
435
  $code->expires = $temp_code->expires;
438
  }
439
  ?>
440
  <form action="" method="post">
441
+ <input name="saveid" type="hidden" value="<?php echo esc_attr( $edit ); ?>" />
442
  <?php wp_nonce_field('save', 'pmpro_discountcodes_nonce');?>
443
  <table class="form-table">
444
  <tbody>
445
  <tr>
446
  <th scope="row" valign="top"><label><?php _e('ID', 'paid-memberships-pro' );?>:</label></th>
447
+ <td><p class="description"><?php if(!empty($code->id)) echo esc_html( $code->id ); else echo __("This will be generated when you save.", 'paid-memberships-pro' );?></p></td>
448
  </tr>
449
 
450
  <tr>
451
  <th scope="row" valign="top"><label for="code"><?php _e('Code', 'paid-memberships-pro' );?>:</label></th>
452
+ <td><input name="code" type="text" size="20" value="<?php echo esc_attr( $code->code ); ?>" /></td>
453
  </tr>
454
 
455
  <?php
498
  }
499
  ?>
500
  </select>
501
+ <input name="starts_day" type="text" size="2" value="<?php echo esc_attr( $selected_starts_day ); ?>" />
502
+ <input name="starts_year" type="text" size="4" value="<?php echo esc_attr( $selected_starts_year ); ?>" />
503
  </td>
504
  </tr>
505
 
516
  }
517
  ?>
518
  </select>
519
+ <input name="expires_day" type="text" size="2" value="<?php echo esc_attr( $selected_expires_day ); ?>" />
520
+ <input name="expires_year" type="text" size="4" value="<?php echo esc_attr( $selected_expires_year ); ?>" />
521
  </td>
522
  </tr>
523
 
524
  <tr>
525
  <th scope="row" valign="top"><label for="uses"><?php _e('Uses', 'paid-memberships-pro' );?>:</label></th>
526
  <td>
527
+ <input name="uses" type="text" size="10" value="<?php if ( ! empty( $code->uses ) ) echo esc_attr( $code->uses ); ?>" />
528
  <p class="description"><?php _e('Leave blank for unlimited uses.', 'paid-memberships-pro' );?></p>
529
  </td>
530
  </tr>
539
  <div class="pmpro_discount_levels">
540
  <?php
541
  $levels = $wpdb->get_results("SELECT * FROM $wpdb->pmpro_membership_levels");
542
+ $levels = pmpro_sort_levels_by_order( $levels );
543
  foreach($levels as $level)
544
  {
545
  //if this level is already managed for this discount code, use the code values
566
  $level_checked = false;
567
  ?>
568
  <div class="pmpro_discount_level <?php if ( ! pmpro_check_discount_code_level_for_gateway_compatibility( $level ) ) { ?>pmpro_error<?php } ?>">
569
+ <input type="hidden" name="all_levels[]" value="<?php echo esc_attr( $level->id ); ?>" />
570
+ <input type="checkbox" id="levels_<?php echo esc_attr( $level->id ); ?>" name="levels[]" value="<?php echo esc_attr( $level->id ); ?>" <?php if(!empty($level->checked)) { ?>checked="checked"<?php } ?> onclick="if(jQuery(this).is(':checked')) jQuery(this).next().next().show(); else jQuery(this).next().next().hide();" />
571
+ <label for="levels_<?php echo esc_attr( $level->id ); ?>"><?php echo $level->name?></label>
572
+ <div class="pmpro_discount_levels_pricing level_<?php echo esc_attr( $level->id ); ?>" <?php if(empty($level->checked)) { ?>style="display: none;"<?php } ?>>
573
  <table class="form-table">
574
  <tbody>
575
  <tr>
590
 
591
  <tr>
592
  <th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'paid-memberships-pro' );?>:</label></th>
593
+ <td><input class="recurring_checkbox" id="recurring_<?php echo esc_attr( $level->id );?>" name="recurring[]" type="checkbox" value="<?php echo esc_attr( $level->id ); ?>" <?php if(pmpro_isLevelRecurring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).prop('checked')) { jQuery(this).parent().parent().siblings('.recurring_info').show(); if(!jQuery('#custom_trial_<?php echo esc_attr( $level->id ); ?>').is(':checked')) jQuery(this).parent().parent().siblings('.trial_info').hide();} else jQuery(this).parent().parent().siblings('.recurring_info').hide();" /> <label for="recurring_<?php echo esc_attr( $level->id ); ?>"><?php _e('Check if this level has a recurring subscription payment.', 'paid-memberships-pro' );?></label></td>
594
  </tr>
595
 
596
  <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
606
  echo $pmpro_currency_symbol;
607
  ?>
608
  <?php _e('per', 'paid-memberships-pro' ); ?>
609
+ <input name="cycle_number[]" type="text" size="10" value="<?php echo esc_attr( $level->cycle_number ); ?>" />
610
  <select name="cycle_period[]">
611
  <?php
612
  $cycles = array( __('Day(s)', 'paid-memberships-pro' ) => 'Day', __('Week(s)', 'paid-memberships-pro' ) => 'Week', __('Month(s)', 'paid-memberships-pro' ) => 'Month', __('Year(s)', 'paid-memberships-pro' ) => 'Year' );
620
  <p class="description"><?php _e('The amount to be billed one cycle after the initial payment.', 'paid-memberships-pro' );?></p>
621
  <?php if($gateway == "braintree") { ?>
622
  <strong <?php if(!empty($pmpro_braintree_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Braintree integration currently only supports billing periods of "Month" or "Year".', 'paid-memberships-pro' );?></strong>
623
+ <?php } elseif($gateway == "stripe") { ?>
624
+ <p class="description"><strong <?php if(!empty($pmpro_stripe_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Stripe integration does not allow billing periods longer than 1 year.', 'paid-memberships-pro' );?></strong></p>
625
+ <?php }?>
626
  </td>
627
  </tr>
628
 
629
  <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
630
  <th scope="row" valign="top"><label for="billing_limit"><?php _e('Billing Cycle Limit', 'paid-memberships-pro' );?>:</label></th>
631
  <td>
632
+ <input name="billing_limit[]" type="text" size="20" value="<?php echo esc_attr( $level->billing_limit ); ?>" />
633
  <p class="description">
634
  <?php _e('The <strong>total</strong> number of recurring billing cycles for this level, including the trial period (if applicable) but not including the initial payment. Set to zero if membership is indefinite.', 'paid-memberships-pro' );?>
635
  <?php if ( ( $gateway == "stripe" ) && ! function_exists( 'pmprosbl_plugin_row_meta' ) ) { ?>
652
  <tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
653
  <th scope="row" valign="top"><label><?php _e('Custom Trial', 'paid-memberships-pro' );?>:</label></th>
654
  <td>
655
+ <input id="custom_trial_<?php echo esc_attr( $level->id ); ?>" id="custom_trial_<?php echo esc_attr( $level->id ); ?>" name="custom_trial[]" type="checkbox" value="<?php echo esc_attr( $level->id ); ?>" <?php if ( pmpro_isLevelTrial($level) ) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).prop('checked')) jQuery(this).parent().parent().siblings('.trial_info').show(); else jQuery(this).parent().parent().siblings('.trial_info').hide();" /> <label for="custom_trial_<?php echo esc_attr( $level->id );?>"><?php _e('Check to add a custom trial period.', 'paid-memberships-pro' );?></label>
656
  <?php if($gateway == "twocheckout") { ?>
657
  <p class="description"><strong <?php if(!empty($pmpro_twocheckout_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('2Checkout integration does not support custom trials. You can do one period trials by setting an initial payment different from the billing amount.', 'paid-memberships-pro' );?></strong></p>
658
  <?php } ?>
672
  echo $pmpro_currency_symbol;
673
  ?>
674
  <?php _e('for the first', 'paid-memberships-pro' );?>
675
+ <input name="trial_limit[]" type="text" size="10" value="<?php echo esc_attr( $level->trial_limit ); ?>" />
676
  <?php _e('subscription payments', 'paid-memberships-pro' );?>.
677
  <?php if($gateway == "stripe") { ?>
678
  <p class="description"><strong <?php if(!empty($pmpro_stripe_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Stripe integration currently does not support trial amounts greater than $0.', 'paid-memberships-pro' );?></strong></p>
686
 
687
  <tr>
688
  <th scope="row" valign="top"><label><?php _e('Membership Expiration', 'paid-memberships-pro' );?>:</label></th>
689
+ <td><input id="expiration_<?php echo esc_attr( $level->id ); ?>" name="expiration[]" type="checkbox" value="<?php echo esc_attr( $level->id ); ?>" <?php if(pmpro_isLevelExpiring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).is(':checked')) { jQuery(this).parent().parent().siblings('.expiration_info').show(); } else { jQuery(this).parent().parent().siblings('.expiration_info').hide();}" /> <label for="expiration_<?php echo esc_attr( $level->id ); ?>"><?php _e('Check this to set when membership access expires.', 'paid-memberships-pro' );?></label></td>
690
  </tr>
691
 
692
  <tr class="expiration_info" <?php if(!pmpro_isLevelExpiring($level)) {?>style="display: none;"<?php } ?>>
693
  <th scope="row" valign="top"><label for="billing_amount"><?php _e('Expires In', 'paid-memberships-pro' );?>:</label></th>
694
  <td>
695
+ <input id="expiration_number" name="expiration_number[]" type="text" size="10" value="<?php echo esc_attr( $level->expiration_number ); ?>" />
696
  <select id="expiration_period" name="expiration_period[]">
697
  <?php
698
+
699
+ $cycles = array( __('Hour(s)', 'paid-memberships-pro' ) => 'Hour', __('Day(s)', 'paid-memberships-pro' ) => 'Day', __('Week(s)', 'paid-memberships-pro' ) => 'Week', __('Month(s)', 'paid-memberships-pro' ) => 'Month', __('Year(s)', 'paid-memberships-pro' ) => 'Year' );
700
  foreach ( $cycles as $name => $value ) {
701
+
702
  echo "<option value='$value'";
703
  if ( $level->expiration_period == $value ) echo " selected='selected'";
704
  echo ">$name</option>";
726
 
727
  <p class="submit topborder">
728
  <input name="save" type="submit" class="button button-primary" value="Save Code" />
729
+ <input name="cancel" type="button" class="button" value="Cancel" onclick="location.href='<?php echo admin_url( '/admin.php?page=pmpro-discountcodes')?>';" />
730
  </p>
731
  </form>
732
  </div>
772
  <p class="search-box">
773
  <label class="screen-reader-text" for="post-search-input"><?php _e('Search Discount Codes', 'paid-memberships-pro' );?>:</label>
774
  <input type="hidden" name="page" value="pmpro-discountcodes" />
775
+ <input id="post-search-input" type="text" value="<?php echo esc_attr( wp_unslash( $s ) ); ?>" name="s" size="30" />
776
+ <input class="button" type="submit" value="<?php esc_attr_e('Search', 'paid-memberships-pro' );?>" id="search-submit "/>
777
  </p>
778
  </form>
779
 
797
  <td colspan="6">
798
  <?php echo esc_attr_e( 'Code not found.', 'paid-memberships-pro' ); ?>
799
  </td>
800
+ </tr>
801
  <?php } ?>
802
  <?php
803
  foreach($codes as $code) {
806
  <tr<?php if ( ! pmpro_check_discount_code_for_gateway_compatibility( $code->id ) ) { ?> class="pmpro_error"<?php } ?>>
807
  <td><?php echo $code->id?></td>
808
  <td class="has-row-actions">
809
+ <a title="<?php echo esc_attr( sprintf( __( 'Edit Code: %s', 'paid-memberships-pro' ), $code->code ) ); ?>" href="<?php echo esc_url( add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $code->id ), admin_url('admin.php' ) ) ); ?>"><?php echo $code->code?></a>
810
  <div class="row-actions">
811
+ <?php
812
+ $delete_text = esc_html(
813
+ sprintf(
814
+ // translators: %s is the Discount Code.
815
+ __( 'Are you sure you want to delete the %s discount code? The subscriptions for existing users will not change, but new users will not be able to use this code anymore.', 'paid-memberships-pro' ),
816
+ $code->code
817
+ )
818
+ );
819
+
820
+ $delete_nonce_url = wp_nonce_url(
821
+ add_query_arg(
822
+ [
823
+ 'page' => 'pmpro-discountcodes',
824
+ 'delete' => $code->id,
825
+ ],
826
+ admin_url( 'admin.php' )
827
+ ),
828
+ 'delete',
829
+ 'pmpro_discountcodes_nonce'
830
+ );
831
+
832
+ $actions = [
833
+ 'edit' => sprintf(
834
+ '<a title="%1$s" href="%2$s">%3$s</a>',
835
+ esc_attr__( 'Edit', 'paid-memberships-pro' ),
836
+ esc_url(
837
+ add_query_arg(
838
+ [
839
+ 'page' => 'pmpro-discountcodes',
840
+ 'edit' => $code->id,
841
+ ],
842
+ admin_url( 'admin.php' )
843
+ )
844
+ ),
845
+ esc_html__( 'Edit', 'paid-memberships-pro' )
846
+ ),
847
+ 'copy' => sprintf(
848
+ '<a title="%1$s" href="%2$s">%3$s</a>',
849
+ esc_attr__( 'Copy', 'paid-memberships-pro' ),
850
+ esc_url(
851
+ add_query_arg(
852
+ [
853
+ 'page' => 'pmpro-discountcodes',
854
+ 'edit' => - 1,
855
+ 'copy' => $code->id,
856
+ ],
857
+ admin_url( 'admin.php' )
858
+ )
859
+ ),
860
+ esc_html__( 'Copy', 'paid-memberships-pro' )
861
+ ),
862
+ 'delete' => sprintf(
863
+ '<a title="%1$s" href="%2$s">%3$s</a>',
864
+ esc_attr__( 'Delete', 'paid-memberships-pro' ),
865
+ 'javascript:pmpro_askfirst(\'' . esc_js( $delete_text ) . '\', \'' . esc_js( $delete_nonce_url ) . '\'); void(0);',
866
+ esc_html__( 'Delete', 'paid-memberships-pro' )
867
+ ),
868
+ ];
869
+
870
+ if ( 0 < (int) $uses ) {
871
+ $actions['orders'] = sprintf(
872
+ '<a title="%1$s" href="%2$s">%3$s</a>',
873
+ esc_attr__( 'View Orders', 'paid-memberships-pro' ),
874
+ esc_url(
875
+ add_query_arg(
876
+ [
877
+ 'page' => 'pmpro-orders',
878
+ 'discount-code' => $code->id,
879
+ 'filter' => 'with-discount-code',
880
+ ],
881
+ admin_url( 'admin.php' )
882
+ )
883
+ ),
884
+ esc_html__( 'Orders', 'paid-memberships-pro' )
885
+ );
886
+ }
887
+
888
+ /**
889
+ * Filter the extra actions for this discount code.
890
+ *
891
+ * @since 2.6.2
892
+ *
893
+ * @param array $actions The list of actions.
894
+ * @param object $code The discount code data.
895
+ */
896
+ $actions = apply_filters( 'pmpro_discountcodes_row_actions', $actions, $code );
897
+
898
+ $actions_html = [];
899
+
900
+ foreach ( $actions as $action => $link ) {
901
+ $actions_html[] = sprintf(
902
+ '<span class="%1$s">%2$s</span>',
903
+ esc_attr( $action ),
904
+ $link
905
+ );
906
+ }
907
+
908
+ if ( ! empty( $actions_html ) ) {
909
+ echo implode( ' | ', $actions_html );
910
+ }
911
+ ?>
912
  </div>
913
  </td>
914
  <td>
939
 
940
  $level_names = array();
941
  foreach( $levels as $level ) {
942
+ if ( ! empty( $pmpro_pages['checkout'] ) ) {
943
+ $level_names[] = '<a title="' . pmpro_url( 'checkout', '?level=' . $level->id . '&discount_code=' . $code->code) . '" target="_blank" href="' . pmpro_url( 'checkout', '?level=' . $level->id . '&discount_code=' . $code->code) . '">' . $level->name . '</a>';
944
+ } else {
945
+ $level_names[] = $level->name;
946
+ }
947
  }
948
  if( $level_names ) {
949
  echo implode( ', ', $level_names );
962
  </table>
963
 
964
  <?php
965
+ $pagination_url = admin_url( "/admin.php?page=pmpro-discountcodes&s=" . $s );
966
  echo pmpro_getPaginationString( $pn, $totalrows, $limit, 1, $pagination_url, "&limit=$limit&pn=" );
967
  ?>
968
 
adminpages/emailsettings.php CHANGED
@@ -9,6 +9,8 @@
9
 
10
  //get/set settings
11
  global $pmpro_pages;
 
 
12
 
13
  //check nonce for saving settings
14
  if (!empty($_REQUEST['savesettings']) && (empty($_REQUEST['pmpro_emailsettings_nonce']) || !check_admin_referer('savesettings', 'pmpro_emailsettings_nonce'))) {
@@ -116,20 +118,6 @@
116
  </table>
117
  <p class="submit"><input name="savesettings" type="submit" class="button-primary" value="<?php esc_attr_e( 'Save All Settings', 'paid-memberships-pro' ); ?>" /></p>
118
  <hr />
119
- <div class="pmpro_admin_section pmpro_admin_section-email-content">
120
- <h2><?php _e( 'Customizing Email Content', 'paid-memberships-pro' ); ?></h2>
121
- <p><?php
122
- $allowed_email_customizing_html = array (
123
- 'a' => array (
124
- 'href' => array(),
125
- 'target' => array(),
126
- 'title' => array(),
127
- ),
128
- );
129
- echo sprintf( wp_kses( __( 'There are several ways to modify the appearance of your Paid Memberships Pro emails. We recommend using the free <a href="%s" title="Paid Memberships Pro - Email Templates Admin Editor Add On" target="_blank">Email Templates Admin Editor Add On</a>, which allows you to modify the email header, footer, subject, and body content for all member and admin communications. <a title="Paid Memberships Pro - Member Communications" target="_blank" href="%s">Click here to learn more about Paid Memberships Pro emails</a>.', 'paid-memberships-pro' ), $allowed_email_customizing_html ), 'https://www.paidmembershipspro.com/add-ons/email-templates-admin-editor/?utm_source=plugin&utm_medium=pmpro-emailsettings&utm_campaign=add-ons&utm_content=email-templates-admin-editor', 'http://www.paidmembershipspro.com/documentation/member-communications/?utm_source=plugin&utm_medium=pmpro-emailsettings&utm_campaign=documentation&utm_content=member-communications' );
130
- ?></p>
131
- </div> <!-- end pmpro_admin_section-email-content -->
132
- <hr />
133
  <div class="pmpro_admin_section pmpro_admin_section-email-deliverability">
134
  <h2><?php _e( 'Email Deliverability', 'paid-memberships-pro' ); ?></h2>
135
 
@@ -158,7 +146,7 @@
158
  $sendwp_email_forwarding = function_exists( 'sendwp_forwarding_enabled' ) && sendwp_forwarding_enabled() ? true : false;
159
 
160
  // Messages for connected or not.
161
- $connected = __( 'Your site is connected to SendWP.', 'paid-memberships-pro' ) . " <a href='https://sendwp.com/account/' target='_blank' rel='nofollow'>" . __( 'View Your SendWP Account', 'paid-memberships-pro' ) . "</a>";
162
  $disconnected = ' ' . sprintf( __( 'Please enable email sending inside %s.', 'paid-memberships-pro' ), '<a href="' . admin_url('/tools.php?page=sendwp') . '">SendWP Settings</a>' );
163
  ?>
164
  <p class="description" id="pmpro-sendwp-description"><?php echo $sendwp_email_forwarding ? $connected : $disconnected; ?></p>
@@ -168,25 +156,7 @@
168
  <hr />
169
  <h2 class="title"><?php esc_html_e( 'Other Email Settings', 'paid-memberships-pro' ); ?></h2>
170
  <table class="form-table">
171
- <tbody>
172
- <tr>
173
- <th scope="row" valign="top">
174
- <label for="email_admin"><?php _e('Send the site admin emails', 'paid-memberships-pro' );?>:</label>
175
- </th>
176
- <td>
177
- <input type="checkbox" id="email_admin_checkout" name="email_admin_checkout" value="1" <?php if(!empty($email_admin_checkout)) { ?>checked="checked"<?php } ?> />
178
- <label for="email_admin_checkout"><?php _e('when a member checks out.', 'paid-memberships-pro' );?></label>
179
- <br />
180
- <input type="checkbox" id="email_admin_changes" name="email_admin_changes" value="1" <?php if(!empty($email_admin_changes)) { ?>checked="checked"<?php } ?> />
181
- <label for="email_admin_changes"><?php _e('when an admin changes a user\'s membership level through the dashboard.', 'paid-memberships-pro' );?></label>
182
- <br />
183
- <input type="checkbox" id="email_admin_cancels" name="email_admin_cancels" value="1" <?php if(!empty($email_admin_cancels)) { ?>checked="checked"<?php } ?> />
184
- <label for="email_admin_cancels"><?php _e('when a user cancels his or her account.', 'paid-memberships-pro' );?></label>
185
- <br />
186
- <input type="checkbox" id="email_admin_billing" name="email_admin_billing" value="1" <?php if(!empty($email_admin_billing)) { ?>checked="checked"<?php } ?> />
187
- <label for="email_admin_billing"><?php _e('when a user updates his or her billing information.', 'paid-memberships-pro' );?></label>
188
- </td>
189
- </tr>
190
  <tr>
191
  <th scope="row" valign="top">
192
  <label for="email_member_notification"><?php _e('Send members emails', 'paid-memberships-pro' );?>:</label>
@@ -198,13 +168,11 @@
198
  </td>
199
  </tr>
200
  </tbody>
201
- </table>
202
-
203
  <p class="submit">
204
  <input name="savesettings" type="submit" class="button-primary" value="<?php esc_attr_e( 'Save All Settings', 'paid-memberships-pro' ); ?>" />
205
  </p>
206
  </form>
207
 
208
  <?php
209
- require_once(dirname(__FILE__) . "/admin_footer.php");
210
- ?>
9
 
10
  //get/set settings
11
  global $pmpro_pages;
12
+
13
+ global $current_user;
14
 
15
  //check nonce for saving settings
16
  if (!empty($_REQUEST['savesettings']) && (empty($_REQUEST['pmpro_emailsettings_nonce']) || !check_admin_referer('savesettings', 'pmpro_emailsettings_nonce'))) {
118
  </table>
119
  <p class="submit"><input name="savesettings" type="submit" class="button-primary" value="<?php esc_attr_e( 'Save All Settings', 'paid-memberships-pro' ); ?>" /></p>
120
  <hr />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  <div class="pmpro_admin_section pmpro_admin_section-email-deliverability">
122
  <h2><?php _e( 'Email Deliverability', 'paid-memberships-pro' ); ?></h2>
123
 
146
  $sendwp_email_forwarding = function_exists( 'sendwp_forwarding_enabled' ) && sendwp_forwarding_enabled() ? true : false;
147
 
148
  // Messages for connected or not.
149
+ $connected = __( 'Your site is connected to SendWP.', 'paid-memberships-pro' ) . " <a href='https://app.sendwp.com/dashboard/' target='_blank' rel='nofollow'>" . __( 'View Your SendWP Account', 'paid-memberships-pro' ) . "</a>";
150
  $disconnected = ' ' . sprintf( __( 'Please enable email sending inside %s.', 'paid-memberships-pro' ), '<a href="' . admin_url('/tools.php?page=sendwp') . '">SendWP Settings</a>' );
151
  ?>
152
  <p class="description" id="pmpro-sendwp-description"><?php echo $sendwp_email_forwarding ? $connected : $disconnected; ?></p>
156
  <hr />
157
  <h2 class="title"><?php esc_html_e( 'Other Email Settings', 'paid-memberships-pro' ); ?></h2>
158
  <table class="form-table">
159
+ <tbody>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  <tr>
161
  <th scope="row" valign="top">
162
  <label for="email_member_notification"><?php _e('Send members emails', 'paid-memberships-pro' );?>:</label>
168
  </td>
169
  </tr>
170
  </tbody>
171
+ </table>
 
172
  <p class="submit">
173
  <input name="savesettings" type="submit" class="button-primary" value="<?php esc_attr_e( 'Save All Settings', 'paid-memberships-pro' ); ?>" />
174
  </p>
175
  </form>
176
 
177
  <?php
178
+ require_once(dirname(__FILE__) . "/admin_footer.php");
 
adminpages/emailtemplates.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_emailsettings")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", 'paid-memberships-pro' ));
6
+ }
7
+
8
+ global $wpdb, $msg, $msgt;
9
+
10
+ //get/set settings
11
+ global $pmpro_pages;
12
+
13
+ global $pmpro_email_templates_defaults, $current_user;
14
+
15
+ require_once(dirname(__FILE__) . "/admin_header.php");
16
+ ?>
17
+ <form action="" method="post" enctype="multipart/form-data">
18
+ <?php wp_nonce_field('savesettings', 'pmpro_emailsettings_nonce');?>
19
+
20
+ <h1 class="wp-heading-inline"><?php esc_html_e( 'Email Templates', 'paid-memberships-pro' ); ?></h1>
21
+ <hr class="wp-header-end">
22
+
23
+ <p><?php esc_html_e( 'Select an email template from the dropdown below to customize the subject and body of emails sent through your membership site. You can also disable a specific email or send a test version through this admin page.', 'paid-memberships-pro' ); ?> <a href="https://www.paidmembershipspro.com/documentation/member-communications/list-of-pmpro-email-templates/" target="_blank"><?php esc_html_e( 'Click here for a description of each email sent to your members and admins at different stages of the member experience.', 'paid-memberships-pro'); ?></a></p>
24
+
25
+ <div class="pmpro_admin_section pmpro_admin_section-email-templates-content">
26
+
27
+ <table class="form-table">
28
+ <tr class="status hide-while-loading" style="display:none;">
29
+ <th scope="row" valign="top"></th>
30
+ <td>
31
+ <div id="message" class="status_message_wrapper">
32
+ <p class="status_message"></p>
33
+ </div>
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <th scope="row" valign="top">
38
+ <label for="pmpro_email_template_switcher"><?php esc_html_e( 'Email Template', 'paid-memberships-pro' ); ?></label>
39
+ </th>
40
+ <td>
41
+ <select name="pmpro_email_template_switcher" id="pmpro_email_template_switcher">
42
+ <option value="" selected="selected"><?php echo '--- ' . esc_html__( 'Select a Template to Edit', 'paid-memberships-pro' ) . ' ---'; ?></option>
43
+
44
+ <?php foreach ( $pmpro_email_templates_defaults as $key => $template ): ?>
45
+ <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $template['description'] ); ?></option>
46
+
47
+ <?php endforeach; ?>
48
+ </select>
49
+ <img src="<?php echo esc_url( admin_url( 'images/wpspin_light.gif' ) ); ?>" id="pmproet-spinner" style="display:none;"/>
50
+
51
+ <p id="pmpro_email_template_help_text" class="description"></p>
52
+ </td>
53
+ </tr>
54
+ <tr class="hide-while-loading">
55
+ <th scope="row" valign="top"></th>
56
+ <td>
57
+ <label><input id="pmpro_email_template_disable" name="pmpro_email_template_disable" type="checkbox" /><span
58
+ id="disable_label"><?php esc_html_e( 'Disable this email?', 'paid-memberships-pro' ); ?></span></label>
59
+
60
+
61
+ <p id="disable_description" class="description"><?php esc_html_e( 'Emails with this template will not be sent.', 'paid-memberships-pro' ); ?></p>
62
+
63
+ </td>
64
+ </tr>
65
+ <tr class="hide-while-loading">
66
+ <th scope="row" valign="top"><label for="pmpro_email_template_subject"><?php esc_html_e( 'Subject', 'paid-memberships-pro' ); ?></label></th>
67
+
68
+ <td>
69
+ <input id="pmpro_email_template_subject" name="pmpro_email_template_subject" type="text" size="100"/>
70
+ </td>
71
+ </tr>
72
+ <tr class="hide-while-loading">
73
+ <th scope="row" valign="top"><label for="pmpro_email_template_body"><?php esc_html_e( 'Body', 'paid-memberships-pro' ); ?></label></th>
74
+
75
+ <td>
76
+ <div id="template_editor_container">
77
+ <textarea rows="10" cols="80" name="pmpro_email_template_body" id="pmpro_email_template_body"></textarea>
78
+ </div>
79
+ </td>
80
+ </tr>
81
+ <tr class="hide-while-loading">
82
+ <th scope="row" valign="top"></th>
83
+ <td>
84
+ <?php esc_html_e( 'Send a test email to ', 'paid-memberships-pro' ); ?>
85
+ <input id="test_email_address" name="test_email_address" type="text"
86
+ value="<?php echo esc_attr( $current_user->user_email ); ?>"/>
87
+ <input id="send_test_email" class="button" name="send_test_email" value="<?php esc_attr_e( 'Save Template and Send Email', 'paid-memberships-pro' ); ?>"
88
+
89
+ type="button"/>
90
+
91
+ <p class="description">
92
+ <?php esc_html_e( 'Your current membership will be used for any membership level data.', 'paid-memberships-pro' ); ?>
93
+ </p>
94
+ </td>
95
+ </tr>
96
+ <tr class="controls hide-while-loading">
97
+ <th scope="row" valign="top"></th>
98
+ <td>
99
+ <p class="submit">
100
+ <input id="pmpro_submit_template_data" name="pmpro_save_template" type="button" class="button-primary"
101
+ value="<?php esc_attr_e( 'Save Template', 'paid-memberships-pro' ); ?>"/>
102
+
103
+ <input id="pmpro_reset_template_data" name="pmpro_reset_template" type="button" class="button"
104
+ value="<?php esc_attr_e( 'Reset Template', 'paid-memberships-pro' ); ?>"/>
105
+
106
+ </p>
107
+ </td>
108
+ </tr>
109
+ </table>
110
+
111
+ <hr />
112
+
113
+ <div class="pmpro-email-templates-variable-reference">
114
+ <h1><?php esc_html_e( 'Variable Reference', 'paid-memberships-pro' ); ?></h1>
115
+
116
+ <p><?php esc_html_e( 'Use the placeholder variables below to customize your member and admin emails with specific user or membership data.', 'paid-memberships-pro' ); ?></p>
117
+ <table class="form-table">
118
+ <tbody>
119
+ <tr>
120
+ <th scope="row"><?php esc_html_e('General Settings / Membership Info', 'paid-memberships-pro'); ?></th>
121
+ <td>
122
+ <table class="widefat striped">
123
+ <tbody>
124
+ <?php
125
+ $email_variables = [
126
+ '!!name!!' => __( 'Display Name (Profile/Edit User > Display name publicly as)', 'paid-memberships-pro' ),
127
+ '!!user_login!!' => __( 'Username', 'paid-memberships-pro' ),
128
+ '!!sitename!!' => __( 'Site Title', 'paid-memberships-pro' ),
129
+ '!!siteemail!!' => __( 'Site Email Address (General Settings > Email OR Memberships > Settings > Email Settings)', 'paid-memberships-pro' ),
130
+ '!!membership_id!!' => __( 'Membership Level ID', 'paid-memberships-pro' ),
131
+ '!!membership_level_name!!' => __( 'Membership Level Name', 'paid-memberships-pro' ),
132
+ '!!membership_change!!' => __( 'Membership Level Change', 'paid-memberships-pro' ),
133
+ '!!membership_expiration!!' => __( 'Membership Level Expiration', 'paid-memberships-pro' ),
134
+ '!!startdate!!' => __( 'Membership Start Date', 'paid-memberships-pro' ),
135
+ '!!enddate!!' => __( 'Membership End Date', 'paid-memberships-pro' ),
136
+ '!!display_name!!' => __( 'Display Name (Profile/Edit User > Display name publicly as)', 'paid-memberships-pro' ),
137
+ '!!user_email!!' => __( 'User Email', 'paid-memberships-pro' ),
138
+ '!!login_url!!' => __( 'Login URL', 'paid-memberships-pro' ),
139
+ '!!levels_url!!' => __( 'Membership Levels Page URL', 'paid-memberships-pro' ),
140
+ ];
141
+
142
+ foreach ( $email_variables as $email_variable => $description ) {
143
+ ?>
144
+ <tr>
145
+ <td><?php echo esc_html( $email_variable ); ?></td>
146
+ <td><?php echo esc_html( $description ); ?></td>
147
+ </tr>
148
+ <?php
149
+ }
150
+ ?>
151
+ </tbody>
152
+ </table>
153
+ </td>
154
+ </tr>
155
+ <tr>
156
+ <th scope="row"><?php esc_html_e( 'Billing Information', 'paid-memberships-pro' ); ?></th>
157
+ <td>
158
+ <table class="widefat striped">
159
+ <tbody>
160
+ <?php
161
+ $email_variables = [
162
+ '!!billing_address!!' => __( 'Billing Info Complete Address', 'paid-memberships-pro' ),
163
+ '!!billing_name!!' => __( 'Billing Info Name', 'paid-memberships-pro' ),
164
+ '!!billing_street!!' => __( 'Billing Info Street Address', 'paid-memberships-pro' ),
165
+ '!!billing_city!!' => __( 'Billing Info City', 'paid-memberships-pro' ),
166
+ '!!billing_state!!' => __( 'Billing Info State', 'paid-memberships-pro' ),
167
+ '!!billing_zip!!' => __( 'Billing Info ZIP Code', 'paid-memberships-pro' ),
168
+ '!!billing_country!!' => __( 'Billing Info Country', 'paid-memberships-pro' ),
169
+ '!!billing_phone!!' => __( 'Billing Info Phone #', 'paid-memberships-pro' ),
170
+ '!!cardtype!!' => __( 'Credit Card Type', 'paid-memberships-pro' ),
171
+ '!!accountnumber!!' => __( 'Credit Card Number (last 4 digits)', 'paid-memberships-pro' ),
172
+ '!!expirationmonth!!' => __( 'Credit Card Expiration Month (mm format)', 'paid-memberships-pro' ),
173
+ '!!expirationyear!!' => __( 'Credit Card Expiration Year (yyyy format)', 'paid-memberships-pro' ),
174
+ '!!membership_cost!!' => __( 'Membership Level Cost Text', 'paid-memberships-pro' ),
175
+ '!!instructions!!' => __( 'Payment Instructions (used in Checkout - Email Template)', 'paid-memberships-pro' ),
176
+ '!!invoice_id!!' => __( 'Invoice ID', 'paid-memberships-pro' ),
177
+ '!!invoice_total!!' => __( 'Invoice Total', 'paid-memberships-pro' ),
178
+ '!!invoice_date!!' => __( 'Invoice Date', 'paid-memberships-pro' ),
179
+ '!!invoice_url!!' => __( 'Invoice Page URL', 'paid-memberships-pro' ),
180
+ '!!discount_code!!' => __( 'Discount Code Applied', 'paid-memberships-pro' ),
181
+ '!!membership_level_confirmation_message!!' => __( 'Custom Level Confirmation Message', 'paid-memberships-pro' ),
182
+
183
+ ];
184
+
185
+ foreach ( $email_variables as $email_variable => $description ) {
186
+ ?>
187
+ <tr>
188
+ <td><?php echo esc_html( $email_variable ); ?></td>
189
+ <td><?php echo esc_html( $description ); ?></td>
190
+ </tr>
191
+ <?php
192
+ }
193
+ ?>
194
+ </tbody>
195
+ </table>
196
+ </td>
197
+ </tr>
198
+ </tbody>
199
+ </table>
200
+ </div> <!-- end pmpro-email-templates-variable-reference -->
201
+
202
+ <?php wp_nonce_field( 'pmproet', 'security' ); ?>
203
+
204
+ </div> <!-- end pmpro_admin_section-email-templates-content -->
205
+ </form>
206
+ <?php
207
+ require_once(dirname(__FILE__) . "/admin_footer.php");
adminpages/functions.php CHANGED
@@ -51,10 +51,20 @@ function pmpro_checkLevelForStripeCompatibility($level = NULL)
51
  if(is_numeric($level))
52
  $level = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = %d LIMIT 1" , $level ) );
53
 
54
- //check this level
55
  if ( ( $level->billing_limit > 0 ) && ! function_exists( 'pmprosbl_plugin_row_meta' ) ) {
56
  return false;
57
  }
 
 
 
 
 
 
 
 
 
 
58
  }
59
  }
60
 
@@ -231,7 +241,19 @@ function pmpro_check_discount_code_level_for_gateway_compatibility( $discount_co
231
 
232
  // Check this discount code level for gateway compatibility
233
  if ( $gateway == 'stripe' ) {
234
- if ( ( $discount_code_level->billing_limit > 0 ) && ! function_exists( 'pmprosbl_plugin_row_meta' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
235
  global $pmpro_stripe_error;
236
  $pmpro_stripe_error = true;
237
  return false;
@@ -249,7 +271,7 @@ function pmpro_check_discount_code_level_for_gateway_compatibility( $discount_co
249
  $pmpro_braintree_error = true;
250
  return false;
251
  }
252
- } if ( $gateway == 'twocheckout' ) {
253
  if ( $discount_code_level->trial_amount > $discount_code_level->billing_amount ) {
254
  global $pmpro_twocheckout_error;
255
  $pmpro_twocheckout_error = true;
@@ -367,7 +389,7 @@ function pmpro_add_email_order_modal() {
367
  $email = new PMProEmail();
368
  $user = get_user_by( 'email', sanitize_email( $_REQUEST['email'] ) );
369
  $order = new MemberOrder( $_REQUEST['order'] );
370
- if ( $email->sendBillableInvoiceEmail( $user, $order ) ) { ?>
371
  <div class="notice notice-success is-dismissible">
372
  <p><?php _e( 'Invoice emailed successfully.', 'paid-memberships-pro' ); ?></p>
373
  </div>
@@ -410,4 +432,3 @@ function pmpro_add_email_order_modal() {
410
  </div>
411
  <?php
412
  }
413
-
51
  if(is_numeric($level))
52
  $level = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = %d LIMIT 1" , $level ) );
53
 
54
+ // Check if this level uses billing limits.
55
  if ( ( $level->billing_limit > 0 ) && ! function_exists( 'pmprosbl_plugin_row_meta' ) ) {
56
  return false;
57
  }
58
+
59
+ // Check if this level has a billing period longer than 1 year.
60
+ if (
61
+ ( $level->cycle_period === 'Year' && $level->cycle_number > 1 ) ||
62
+ ( $level->cycle_period === 'Month' && $level->cycle_number > 12 ) ||
63
+ ( $level->cycle_period === 'Week' && $level->cycle_number > 52 ) ||
64
+ ( $level->cycle_period === 'Day' && $level->cycle_number > 365 )
65
+ ) {
66
+ return false;
67
+ }
68
  }
69
  }
70
 
241
 
242
  // Check this discount code level for gateway compatibility
243
  if ( $gateway == 'stripe' ) {
244
+ // Check if this code level has a billing limit.
245
+ if ( ( intval( $discount_code_level->billing_limit ) > 0 ) && ! function_exists( 'pmprosbl_plugin_row_meta' ) ) {
246
+ global $pmpro_stripe_error;
247
+ $pmpro_stripe_error = true;
248
+ return false;
249
+ }
250
+ // Check if this code level has a billing period longer than 1 year.
251
+ if (
252
+ ( $discount_code_level->cycle_period === 'Year' && intval( $discount_code_level->cycle_number ) > 1 ) ||
253
+ ( $discount_code_level->cycle_period === 'Month' && intval( $discount_code_level->cycle_number ) > 12 ) ||
254
+ ( $discount_code_level->cycle_period === 'Week' && intval( $discount_code_level->cycle_number ) > 52 ) ||
255
+ ( $discount_code_level->cycle_period === 'Day' && intval( $discount_code_level->cycle_number ) > 365 )
256
+ ) {
257
  global $pmpro_stripe_error;
258
  $pmpro_stripe_error = true;
259
  return false;
271
  $pmpro_braintree_error = true;
272
  return false;
273
  }
274
+ } elseif ( $gateway == 'twocheckout' ) {
275
  if ( $discount_code_level->trial_amount > $discount_code_level->billing_amount ) {
276
  global $pmpro_twocheckout_error;
277
  $pmpro_twocheckout_error = true;
389
  $email = new PMProEmail();
390
  $user = get_user_by( 'email', sanitize_email( $_REQUEST['email'] ) );
391
  $order = new MemberOrder( $_REQUEST['order'] );
392
+ if ( ! empty( $user ) && ! empty( $order ) && $email->sendBillableInvoiceEmail( $user, $order ) ) { ?>
393
  <div class="notice notice-success is-dismissible">
394
  <p><?php _e( 'Invoice emailed successfully.', 'paid-memberships-pro' ); ?></p>
395
  </div>
432
  </div>
433
  <?php
434
  }
 
adminpages/license.php CHANGED
@@ -7,35 +7,25 @@ if ( ! function_exists( 'current_user_can' ) || ( ! current_user_can( 'manage_op
7
  //updating license?
8
  if ( ! empty( $_REQUEST['pmpro-verify-submit'] ) ) {
9
  $key = preg_replace("/[^a-zA-Z0-9]/", "", $_REQUEST['pmpro-license-key']);
10
-
11
- //erase the old key
12
- delete_option('pmpro_license_key');
13
 
14
- //check key
15
- $valid = pmpro_license_isValid($key, NULL, true);
 
16
 
17
- if ( $valid ) { ?>
18
- <div id="message" class="updated fade">
19
- <p><?php _e( 'Your license key has been validated.', 'paid-memberships-pro' ); ?></p>
20
- </div>
21
- <?php } else {
22
- global $pmpro_license_error;
23
- if ( ! empty( $pmpro_license_error ) ) { ?>
24
- <div id="message" class="error">
25
- <p><?php echo $pmpro_license_error; ?></p>
26
- </div>
27
- <?php }
28
- }
29
-
30
- //update key
31
  update_option( 'pmpro_license_key', $key, 'no' );
32
- }
33
 
34
- //get saved license
35
- $key = get_option( 'pmpro_license_key', '' );
36
- $pmpro_license_check = get_option( 'pmpro_license_check', array( 'license' => false, 'enddate' => 0 ) );
 
 
 
 
 
37
 
38
- //html for license settings page
39
  if ( defined( 'PMPRO_DIR' ) ) {
40
  require_once( PMPRO_DIR . '/adminpages/admin_header.php' );
41
  } ?>
@@ -43,12 +33,14 @@ if ( defined( 'PMPRO_DIR' ) ) {
43
  <h2><?php _e('Paid Memberships Pro Support License', 'paid-memberships-pro' );?></h2>
44
 
45
  <div class="about-text">
46
- <?php if(!pmpro_license_isValid() && empty($key)) { ?>
 
 
47
  <p class="pmpro_message pmpro_error"><strong><?php _e('Enter your support license key.</strong> Your license key can be found in your membership email receipt or in your <a href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fmembership-account%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dmembership-account%26utm_content%3Dno-key" target="_blank">Membership Account</a>.', 'paid-memberships-pro' );?></p>
48
- <?php } elseif(!pmpro_license_isValid()) { ?>
49
  <p class="pmpro_message pmpro_error"><strong><?php _e('Your license is invalid or expired.', 'paid-memberships-pro' );?></strong> <?php _e('Visit the PMPro <a href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fmembership-account%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dmembership-account%26utm_content%3Dkey-not-valid" target="_blank">Membership Account</a> page to confirm that your account is active and to find your license key.', 'paid-memberships-pro' );?></p>
50
  <?php } else { ?>
51
- <p class="pmpro_message pmpro_success"><?php printf(__('<strong>Thank you!</strong> A valid <strong>%s</strong> license key has been used to activate your support license on this site.', 'paid-memberships-pro' ), ucwords($pmpro_license_check['license']));?></p>
52
  <?php } ?>
53
 
54
  <form action="" method="post">
@@ -56,7 +48,7 @@ if ( defined( 'PMPRO_DIR' ) ) {
56
  <tbody>
57
  <tr id="pmpro-settings-key-box">
58
  <td>
59
- <input type="password" name="pmpro-license-key" id="pmpro-license-key" value="<?php echo esc_attr($key);?>" placeholder="<?php _e('Enter license key here...', 'paid-memberships-pro' );?>" size="40" />
60
  <?php wp_nonce_field( 'pmpro-key-nonce', 'pmpro-key-nonce' ); ?>
61
  <?php submit_button( __( 'Validate Key', 'paid-memberships-pro' ), 'primary', 'pmpro-verify-submit', false ); ?>
62
  </td>
@@ -67,8 +59,7 @@ if ( defined( 'PMPRO_DIR' ) ) {
67
 
68
  <p>
69
  <?php if ( ! pmpro_license_isValid() ) { ?>
70
- <a class="button button-primary button-hero" href="https://www.paidmembershipspro.com/membership-checkout/?level=20&utm_source=plugin&utm_medium=pmpro-license&utm_campaign=plus-checkout&utm_content=buy-plus" target="_blank"><?php echo esc_html( 'Buy Plus License', 'paid-memberships-pro' ); ?></a>
71
- <a class="button button-hero" href="https://www.paidmembershipspro.com/pricing/?utm_source=plugin&utm_medium=pmpro-license&utm_campaign=pricing&utm_content=view-license-options" target="_blank"><?php echo esc_html( 'View Support License Options', 'paid-memberships-pro' ); ?></a>
72
  <?php } else { ?>
73
  <a class="button button-primary button-hero" href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fmembership-account%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dmembership-account%26utm_content%3Dview-account" target="_blank"><?php echo esc_html( 'Manage My Account', 'paid-memberships-pro' ); ?></a>
74
  <a class="button button-hero" href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fnew-topic%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dsupport%26utm_content%3Dnew-support-ticket" target="_blank"><?php echo esc_html( 'Open Support Ticket', 'paid-memberships-pro' ); ?></a>
@@ -96,7 +87,7 @@ if ( defined( 'PMPRO_DIR' ) ) {
96
  ?>
97
 
98
  <?php
99
- echo '<p>' . wp_kses( __( '<strong>Paid Memberships Pro offers plans for automatic updates of Add Ons and premium support.</strong> These plans include a Plus license key which we recommend for all public websites running Paid Memberships Pro. A Plus license key allows you to automatically install new Add Ons and update active Add Ons when a new security, bug fix, or feature enhancement is released.' ), $allowed_pmpro_license_strings_html ) . '</p>';
100
  ?>
101
 
102
  <?php
7
  //updating license?
8
  if ( ! empty( $_REQUEST['pmpro-verify-submit'] ) ) {
9
  $key = preg_replace("/[^a-zA-Z0-9]/", "", $_REQUEST['pmpro-license-key']);
 
 
 
10
 
11
+ // Check key.
12
+ $pmpro_license_check = pmpro_license_check_key( $key );
13
+ $r = pmpro_license_isValid( $key );
14
 
15
+ // Update key.
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  update_option( 'pmpro_license_key', $key, 'no' );
17
+ }
18
 
19
+ // Get values from options if not updating.
20
+ if ( empty( $key ) ) {
21
+ $key = get_option( 'pmpro_license_key', '' );
22
+ }
23
+
24
+ if ( empty( $pmpro_license_check ) ) {
25
+ $pmpro_license_check = get_option( 'pmpro_license_check', array( 'license' => false, 'enddate' => 0 ) );
26
+ }
27
 
28
+ // HTML for license settings page.
29
  if ( defined( 'PMPRO_DIR' ) ) {
30
  require_once( PMPRO_DIR . '/adminpages/admin_header.php' );
31
  } ?>
33
  <h2><?php _e('Paid Memberships Pro Support License', 'paid-memberships-pro' );?></h2>
34
 
35
  <div class="about-text">
36
+ <?php if ( is_wp_error( $pmpro_license_check ) ) { ?>
37
+ <p class="pmpro_message pmpro_error"><strong><?php echo esc_html( sprintf( __( 'There was an issue validating your license key: %s', 'paid-memberships-pro' ), $pmpro_license_check->get_error_message() ) );?></strong> <?php _e('Visit the PMPro <a href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fmembership-account%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dmembership-account%26utm_content%3Dkey-not-valid" target="_blank">Membership Account</a> page to confirm that your account is active and to find your license key.', 'paid-memberships-pro' );?></p>
38
+ <?php } elseif( ! pmpro_license_isValid() && empty( $key ) ) { ?>
39
  <p class="pmpro_message pmpro_error"><strong><?php _e('Enter your support license key.</strong> Your license key can be found in your membership email receipt or in your <a href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fmembership-account%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dmembership-account%26utm_content%3Dno-key" target="_blank">Membership Account</a>.', 'paid-memberships-pro' );?></p>
40
+ <?php } elseif( ! pmpro_license_isValid() ) { ?>
41
  <p class="pmpro_message pmpro_error"><strong><?php _e('Your license is invalid or expired.', 'paid-memberships-pro' );?></strong> <?php _e('Visit the PMPro <a href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fmembership-account%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dmembership-account%26utm_content%3Dkey-not-valid" target="_blank">Membership Account</a> page to confirm that your account is active and to find your license key.', 'paid-memberships-pro' );?></p>
42
  <?php } else { ?>
43
+ <p class="pmpro_message pmpro_success"><?php printf(__('<strong>Thank you!</strong> A valid <strong>%s</strong> license key has been used to activate your support license on this site.', 'paid-memberships-pro' ), ucwords( $pmpro_license_check['license'] ) );?></p>
44
  <?php } ?>
45
 
46
  <form action="" method="post">
48
  <tbody>
49
  <tr id="pmpro-settings-key-box">
50
  <td>
51
+ <input type="text" name="pmpro-license-key" id="pmpro-license-key" value="<?php echo esc_attr($key);?>" placeholder="<?php _e('Enter license key here...', 'paid-memberships-pro' );?>" size="40" />
52
  <?php wp_nonce_field( 'pmpro-key-nonce', 'pmpro-key-nonce' ); ?>
53
  <?php submit_button( __( 'Validate Key', 'paid-memberships-pro' ), 'primary', 'pmpro-verify-submit', false ); ?>
54
  </td>
59
 
60
  <p>
61
  <?php if ( ! pmpro_license_isValid() ) { ?>
62
+ <a class="button button-primary button-hero" href="https://www.paidmembershipspro.com/pricing/?utm_source=plugin&utm_medium=pmpro-license&utm_campaign=pricing&utm_content=view-plans-pricing" target="_blank"><?php echo esc_html( 'View Plans and Pricing', 'paid-memberships-pro' ); ?></a>
 
63
  <?php } else { ?>
64
  <a class="button button-primary button-hero" href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fmembership-account%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dmembership-account%26utm_content%3Dview-account" target="_blank"><?php echo esc_html( 'Manage My Account', 'paid-memberships-pro' ); ?></a>
65
  <a class="button button-hero" href="https://www.paidmembershipspro.com/login/?redirect_to=%2Fnew-topic%2F%3Futm_source%3Dplugin%26utm_medium%3Dpmpro-license%26utm_campaign%3Dsupport%26utm_content%3Dnew-support-ticket" target="_blank"><?php echo esc_html( 'Open Support Ticket', 'paid-memberships-pro' ); ?></a>
87
  ?>
88
 
89
  <?php
90
+ echo '<p>' . wp_kses( __( '<strong>Paid Memberships Pro offers plans for automatic updates of Add Ons and premium support.</strong> These plans include a license key which we recommend for all public websites running Paid Memberships Pro. A license key allows you to automatically install Add Ons included in your plan and update active Add Ons included in your plan when a new security, bug fix, or feature enhancement is released.' ), $allowed_pmpro_license_strings_html ) . '</p>';
91
  ?>
92
 
93
  <?php
adminpages/membershiplevels.php CHANGED
@@ -5,7 +5,7 @@
5
  die(__("You do not have permissions to perform this action.", 'paid-memberships-pro' ));
6
  }
7
 
8
- global $wpdb, $msg, $msgt, $pmpro_currency_symbol, $allowedposttags;
9
 
10
  //some vars
11
  $gateway = pmpro_getOption("gateway");
@@ -134,7 +134,7 @@
134
  '%d', //allow_signups
135
  )
136
  );
137
-
138
  if($saveid < 1) {
139
  //added a level
140
  $saveid = $wpdb->insert_id;
@@ -164,10 +164,10 @@
164
  $msgt = __("Error updating membership level.", 'paid-memberships-pro' );
165
  }
166
  }
167
-
168
  if( ! empty( $msgt ) && $ml_recurring && $ml_expiration ) {
169
  $msgt .= ' <strong class="red">' . sprintf( __( 'WARNING: A level was set with both a recurring billing amount and an expiration date. You only need to set one of these unless you really want this membership to expire after a specific time period. For more information, <a target="_blank" href="%s">see our post here</a>.', 'paid-memberships-pro' ), 'https://www.paidmembershipspro.com/important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels/?utm_source=plugin&utm_medium=pmpro-membershiplevels&utm_campaign=blog&utm_content=important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels' ) . '</strong>';
170
-
171
  // turn success to errors
172
  if( $msg > 0 ) {
173
  $msg = 0 - $msg;
@@ -178,7 +178,7 @@
178
  if ( isset( $ml_confirmation_in_email ) ) {
179
  update_pmpro_membership_level_meta( $saveid, 'confirmation_in_email', $ml_confirmation_in_email );
180
  }
181
-
182
  do_action("pmpro_save_membership_level", $saveid);
183
  }
184
  elseif($action == "delete_membership_level")
@@ -269,7 +269,7 @@
269
  ?>
270
  </h1>
271
  <hr class="wp-header-end">
272
-
273
  <div>
274
  <?php
275
  // get the level...
@@ -328,7 +328,7 @@
328
  ) );
329
  if(empty($level->categories))
330
  $level->categories = array();
331
-
332
  // grab the meta for the given level...
333
  if ( ! empty( $temp_id ) ) {
334
  $confirmation_in_email = get_pmpro_membership_level_meta( $temp_id, 'confirmation_in_email', true );
@@ -446,7 +446,9 @@
446
  <?php _e('The amount to be billed one cycle after the initial payment.', 'paid-memberships-pro' );?>
447
  <?php if($gateway == "braintree") { ?>
448
  <strong <?php if(!empty($pmpro_braintree_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Braintree integration currently only supports billing periods of "Month" or "Year".', 'paid-memberships-pro' );?></strong>
449
- <?php } ?>
 
 
450
  </p>
451
  <?php if($gateway == "braintree" && $edit < 0) { ?>
452
  <p class="pmpro_message"><strong><?php _e('Note', 'paid-memberships-pro' );?>:</strong> <?php _e('After saving this level, make note of the ID and create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to <em>pmpro_#</em>, where # is the level ID.', 'paid-memberships-pro' );?></p>
@@ -531,7 +533,20 @@
531
 
532
  </tbody>
533
  </table>
 
 
 
 
 
 
 
 
 
 
 
 
534
  <hr />
 
535
  <h2 class="title"><?php esc_html_e( 'Other Settings', 'paid-memberships-pro' ); ?></h2>
536
  <table class="form-table">
537
  <tbody>
@@ -556,22 +571,20 @@
556
  echo '<tr><th>&nbsp;</th><td><p class="description">' . sprintf( wp_kses( __( 'Optional: Allow more customizable expiration dates using the <a href="%s" title="Paid Memberships Pro - Set Expiration Date Add On" target="_blank">Set Expiration Date Add On</a>.', 'paid-memberships-pro' ), $allowed_sed_html ), 'https://www.paidmembershipspro.com/add-ons/pmpro-expiration-date/?utm_source=plugin&utm_medium=pmpro-membershiplevels&utm_campaign=add-ons&utm_content=pmpro-expiration-date' ) . '</p></td></tr>';
557
  } ?>
558
 
559
- <tr class="expiration_info" <?php if(!pmpro_isLevelExpiring($level)) {?>style="display: none;"<?php } ?>>
560
  <th scope="row" valign="top"><label for="billing_amount"><?php _e('Expires In', 'paid-memberships-pro' );?>:</label></th>
561
  <td>
562
  <input id="expiration_number" name="expiration_number" type="text" value="<?php echo esc_attr($level->expiration_number);?>" class="small-text" />
563
  <select id="expiration_period" name="expiration_period">
564
  <?php
565
- $cycles = array( __('Day(s)', 'paid-memberships-pro' ) => 'Day', __('Week(s)', 'paid-memberships-pro' ) => 'Week', __('Month(s)', 'paid-memberships-pro' ) => 'Month', __('Year(s)', 'paid-memberships-pro' ) => 'Year' );
566
  foreach ( $cycles as $name => $value ) {
567
- echo "<option value='$value'";
568
- if ( $level->expiration_period == $value ) echo " selected='selected'";
569
- echo ">$name</option>";
570
  }
571
  ?>
572
  </select>
573
  <p class="description"><?php _e('Set the duration of membership access. Note that the any future payments (recurring subscription, if any) will be cancelled when the membership expires.', 'paid-memberships-pro' );?></p>
574
-
575
  <div id="pmpro_expiration_warning" style="display: none;" class="notice error inline">
576
  <p><?php printf( __( 'WARNING: This level is set with both a recurring billing amount and an expiration date. You only need to set one of these unless you really want this membership to expire after a certain number of payments. For more information, <a target="_blank" href="%s">see our post here</a>.', 'paid-memberships-pro' ), 'https://www.paidmembershipspro.com/important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels/?utm_source=plugin&utm_medium=pmpro-membershiplevels&utm_campaign=blog&utm_content=important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels' ); ?></p>
577
  </div>
@@ -584,9 +597,9 @@
584
  jQuery('#pmpro_expiration_warning').hide();
585
  }
586
  }
587
-
588
  pmpro_expirationWarningCheck();
589
-
590
  jQuery('#recurring,#expiration').change(function() { pmpro_expirationWarningCheck(); });
591
  });
592
  </script>
@@ -595,7 +608,16 @@
595
  </tbody>
596
  </table>
597
 
598
- <?php do_action("pmpro_membership_level_after_other_settings"); ?>
 
 
 
 
 
 
 
 
 
599
 
600
  <hr />
601
 
@@ -636,9 +658,21 @@
636
  </tr>
637
  </tbody>
638
  </table>
 
 
 
 
 
 
 
 
 
 
 
 
639
  <p class="submit topborder">
640
  <input name="save" type="submit" class="button button-primary" value="<?php _e('Save Level', 'paid-memberships-pro' ); ?>" />
641
- <input name="cancel" type="button" class="button" value="<?php _e('Cancel', 'paid-memberships-pro' ); ?>" onclick="location.href='<?php echo add_query_arg( 'page', 'pmpro-membershiplevels' , get_admin_url(NULL, '/admin.php') ); ?>';" />
642
  </p>
643
  </form>
644
  </div>
@@ -756,7 +790,7 @@
756
  </p>
757
  </form>
758
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Membership Levels', 'paid-memberships-pro' ); ?></h1>
759
- <a href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => -1 ), get_admin_url(null, 'admin.php' ) ); ?>" class="page-title-action"><?php esc_html_e( 'Add New Level', 'paid-memberships-pro' ); ?></a>
760
  <hr class="wp-header-end">
761
 
762
  <?php if(empty($_REQUEST['s']) && count($reordered_levels) > 1) { ?>
@@ -784,7 +818,7 @@
784
  <td colspan="5">
785
  <?php echo esc_attr_e( 'No Membership Levels Found', 'paid-memberships-pro' ); ?>
786
  </td>
787
- </tr>
788
  <?php } ?>
789
  <?php
790
  $count = 0;
@@ -796,9 +830,90 @@
796
  <td class="level_name has-row-actions">
797
  <span class="level-name"><a href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => $level->id ), admin_url( 'admin.php' ) ); ?>"><?php esc_attr_e( $level->name ); ?></a></span>
798
  <div class="row-actions">
799
- <span class="edit"><a title="<?php _e('Edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => $level->id ), admin_url('admin.php' ) ); ?>"><?php _e('Edit', 'paid-memberships-pro' ); ?></a></span> |
800
- <span class="copy"><a title="<?php _e('Copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => -1, 'copy' => $level->id ), admin_url( 'admin.php' ) ); ?>"><?php _e('Copy', 'paid-memberships-pro' ); ?></a></span> |
801
- <span class="delete"><a title="<?php _e('Delete', 'paid-memberships-pro' ); ?>" href="javascript:pmpro_askfirst('<?php echo str_replace("'", "\'", sprintf(__("Are you sure you want to delete membership level %s? All subscriptions will be cancelled.", 'paid-memberships-pro' ), $level->name));?>', '<?php echo wp_nonce_url(add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'action' => 'delete_membership_level', 'deleteid' => $level->id ), admin_url( 'admin.php' ) ), 'delete_membership_level', 'pmpro_membershiplevels_nonce'); ?>'); void(0);"><?php _e('Delete', 'paid-memberships-pro' ); ?></a></span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
802
  </div>
803
  </td>
804
  <td>
@@ -815,7 +930,17 @@
815
  <?php _e('After', 'paid-memberships-pro' );?> <?php echo $level->expiration_number?> <?php echo sornot($level->expiration_period,$level->expiration_number)?>
816
  <?php } ?>
817
  </td>
818
- <td><?php if($level->allow_signups) { ?><a target="_blank" href="<?php echo add_query_arg( 'level', $level->id, pmpro_url("checkout") );?>"><?php _e('Yes', 'paid-memberships-pro' );?></a><?php } else { ?><?php _e('No', 'paid-memberships-pro' );?><?php } ?></td>
 
 
 
 
 
 
 
 
 
 
819
  <?php do_action( 'pmpro_membership_levels_table_extra_cols_body', $level ); ?>
820
  </tr>
821
  <?php
5
  die(__("You do not have permissions to perform this action.", 'paid-memberships-pro' ));
6
  }
7
 
8
+ global $wpdb, $msg, $msgt, $pmpro_currency_symbol, $allowedposttags, $pmpro_pages;
9
 
10
  //some vars
11
  $gateway = pmpro_getOption("gateway");
134
  '%d', //allow_signups
135
  )
136
  );
137
+
138
  if($saveid < 1) {
139
  //added a level
140
  $saveid = $wpdb->insert_id;
164
  $msgt = __("Error updating membership level.", 'paid-memberships-pro' );
165
  }
166
  }
167
+
168
  if( ! empty( $msgt ) && $ml_recurring && $ml_expiration ) {
169
  $msgt .= ' <strong class="red">' . sprintf( __( 'WARNING: A level was set with both a recurring billing amount and an expiration date. You only need to set one of these unless you really want this membership to expire after a specific time period. For more information, <a target="_blank" href="%s">see our post here</a>.', 'paid-memberships-pro' ), 'https://www.paidmembershipspro.com/important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels/?utm_source=plugin&utm_medium=pmpro-membershiplevels&utm_campaign=blog&utm_content=important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels' ) . '</strong>';
170
+
171
  // turn success to errors
172
  if( $msg > 0 ) {
173
  $msg = 0 - $msg;
178
  if ( isset( $ml_confirmation_in_email ) ) {
179
  update_pmpro_membership_level_meta( $saveid, 'confirmation_in_email', $ml_confirmation_in_email );
180
  }
181
+
182
  do_action("pmpro_save_membership_level", $saveid);
183
  }
184
  elseif($action == "delete_membership_level")
269
  ?>
270
  </h1>
271
  <hr class="wp-header-end">
272
+
273
  <div>
274
  <?php
275
  // get the level...
328
  ) );
329
  if(empty($level->categories))
330
  $level->categories = array();
331
+
332
  // grab the meta for the given level...
333
  if ( ! empty( $temp_id ) ) {
334
  $confirmation_in_email = get_pmpro_membership_level_meta( $temp_id, 'confirmation_in_email', true );
446
  <?php _e('The amount to be billed one cycle after the initial payment.', 'paid-memberships-pro' );?>
447
  <?php if($gateway == "braintree") { ?>
448
  <strong <?php if(!empty($pmpro_braintree_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Braintree integration currently only supports billing periods of "Month" or "Year".', 'paid-memberships-pro' );?></strong>
449
+ <?php } elseif($gateway == "stripe") { ?>
450
+ <p class="description"><strong <?php if(!empty($pmpro_stripe_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Stripe integration does not allow billing periods longer than 1 year.', 'paid-memberships-pro' );?></strong></p>
451
+ <?php }?>
452
  </p>
453
  <?php if($gateway == "braintree" && $edit < 0) { ?>
454
  <p class="pmpro_message"><strong><?php _e('Note', 'paid-memberships-pro' );?>:</strong> <?php _e('After saving this level, make note of the ID and create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to <em>pmpro_#</em>, where # is the level ID.', 'paid-memberships-pro' );?></p>
533
 
534
  </tbody>
535
  </table>
536
+
537
+ <?php
538
+ /**
539
+ * Allow adding form fields after the Billing Details Settings section.
540
+ *
541
+ * @since 2.5.10
542
+ *
543
+ * @param object $level The Membership Level object.
544
+ */
545
+ do_action( 'pmpro_membership_level_after_billing_details_settings', $level );
546
+ ?>
547
+
548
  <hr />
549
+
550
  <h2 class="title"><?php esc_html_e( 'Other Settings', 'paid-memberships-pro' ); ?></h2>
551
  <table class="form-table">
552
  <tbody>
571
  echo '<tr><th>&nbsp;</th><td><p class="description">' . sprintf( wp_kses( __( 'Optional: Allow more customizable expiration dates using the <a href="%s" title="Paid Memberships Pro - Set Expiration Date Add On" target="_blank">Set Expiration Date Add On</a>.', 'paid-memberships-pro' ), $allowed_sed_html ), 'https://www.paidmembershipspro.com/add-ons/pmpro-expiration-date/?utm_source=plugin&utm_medium=pmpro-membershiplevels&utm_campaign=add-ons&utm_content=pmpro-expiration-date' ) . '</p></td></tr>';
572
  } ?>
573
 
574
+ <tr class="expiration_info" <?php if(!pmpro_isLevelExpiring($level)) {?>style="display: none;"<?php } ?>>
575
  <th scope="row" valign="top"><label for="billing_amount"><?php _e('Expires In', 'paid-memberships-pro' );?>:</label></th>
576
  <td>
577
  <input id="expiration_number" name="expiration_number" type="text" value="<?php echo esc_attr($level->expiration_number);?>" class="small-text" />
578
  <select id="expiration_period" name="expiration_period">
579
  <?php
580
+ $cycles = array( __('Hour(s)', 'paid-memberships-pro' ) => 'Hour', __('Day(s)', 'paid-memberships-pro' ) => 'Day', __('Week(s)', 'paid-memberships-pro' ) => 'Week', __('Month(s)', 'paid-memberships-pro' ) => 'Month', __('Year(s)', 'paid-memberships-pro' ) => 'Year' );
581
  foreach ( $cycles as $name => $value ) {
582
+ echo "<option value='$value' ".selected( $level->expiration_period, $value, true ).">$name</option>";
 
 
583
  }
584
  ?>
585
  </select>
586
  <p class="description"><?php _e('Set the duration of membership access. Note that the any future payments (recurring subscription, if any) will be cancelled when the membership expires.', 'paid-memberships-pro' );?></p>
587
+
588
  <div id="pmpro_expiration_warning" style="display: none;" class="notice error inline">
589
  <p><?php printf( __( 'WARNING: This level is set with both a recurring billing amount and an expiration date. You only need to set one of these unless you really want this membership to expire after a certain number of payments. For more information, <a target="_blank" href="%s">see our post here</a>.', 'paid-memberships-pro' ), 'https://www.paidmembershipspro.com/important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels/?utm_source=plugin&utm_medium=pmpro-membershiplevels&utm_campaign=blog&utm_content=important-notes-on-recurring-billing-and-expiration-dates-for-membership-levels' ); ?></p>
590
  </div>
597
  jQuery('#pmpro_expiration_warning').hide();
598
  }
599
  }
600
+
601
  pmpro_expirationWarningCheck();
602
+
603
  jQuery('#recurring,#expiration').change(function() { pmpro_expirationWarningCheck(); });
604
  });
605
  </script>
608
  </tbody>
609
  </table>
610
 
611
+ <?php
612
+ /**
613
+ * Allow adding form fields after the Other Settings section.
614
+ *
615
+ * @since 2.5.10
616
+ *
617
+ * @param object $level The Membership Level object.
618
+ */
619
+ do_action( 'pmpro_membership_level_after_other_settings', $level );
620
+ ?>
621
 
622
  <hr />
623
 
658
  </tr>
659
  </tbody>
660
  </table>
661
+
662
+ <?php
663
+ /**
664
+ * Allow adding form fields after the Content Settings section.
665
+ *
666
+ * @since 2.5.10
667
+ *
668
+ * @param object $level The Membership Level object.
669
+ */
670
+ do_action( 'pmpro_membership_level_after_content_settings', $level );
671
+ ?>
672
+
673
  <p class="submit topborder">
674
  <input name="save" type="submit" class="button button-primary" value="<?php _e('Save Level', 'paid-memberships-pro' ); ?>" />
675
+ <input name="cancel" type="button" class="button" value="<?php _e('Cancel', 'paid-memberships-pro' ); ?>" onclick="location.href='<?php echo add_query_arg( 'page', 'pmpro-membershiplevels' , admin_url( '/admin.php') ); ?>';" />
676
  </p>
677
  </form>
678
  </div>
790
  </p>
791
  </form>
792
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Membership Levels', 'paid-memberships-pro' ); ?></h1>
793
+ <a href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => -1 ), admin_url( 'admin.php' ) ); ?>" class="page-title-action"><?php esc_html_e( 'Add New Level', 'paid-memberships-pro' ); ?></a>
794
  <hr class="wp-header-end">
795
 
796
  <?php if(empty($_REQUEST['s']) && count($reordered_levels) > 1) { ?>
818
  <td colspan="5">
819
  <?php echo esc_attr_e( 'No Membership Levels Found', 'paid-memberships-pro' ); ?>
820
  </td>
821
+ </tr>
822
  <?php } ?>
823
  <?php
824
  $count = 0;
830
  <td class="level_name has-row-actions">
831
  <span class="level-name"><a href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => $level->id ), admin_url( 'admin.php' ) ); ?>"><?php esc_attr_e( $level->name ); ?></a></span>
832
  <div class="row-actions">
833
+ <?php
834
+ $delete_text = esc_html(
835
+ sprintf(
836
+ // translators: %s is the Level Name.
837
+ __( 'Are you sure you want to delete membership level %s? All subscriptions will be cancelled.', 'paid-memberships-pro' ),
838
+ $level->name
839
+ )
840
+ );
841
+
842
+ $delete_nonce_url = wp_nonce_url(
843
+ add_query_arg(
844
+ [
845
+ 'page' => 'pmpro-membershiplevels',
846
+ 'action' => 'delete_membership_level',
847
+ 'deleteid' => $level->id,
848
+ ],
849
+ admin_url( 'admin.php' )
850
+ ),
851
+ 'delete_membership_level',
852
+ 'pmpro_membershiplevels_nonce'
853
+ );
854
+
855
+ $actions = [
856
+ 'edit' => sprintf(
857
+ '<a title="%1$s" href="%2$s">%3$s</a>',
858
+ esc_attr__( 'Edit', 'paid-memberships-pro' ),
859
+ esc_url(
860
+ add_query_arg(
861
+ [
862
+ 'page' => 'pmpro-membershiplevels',
863
+ 'edit' => $level->id,
864
+ ],
865
+ admin_url( 'admin.php' )
866
+ )
867
+ ),
868
+ esc_html__( 'Edit', 'paid-memberships-pro' )
869
+ ),
870
+ 'copy' => sprintf(
871
+ '<a title="%1$s" href="%2$s">%3$s</a>',
872
+ esc_attr__( 'Copy', 'paid-memberships-pro' ),
873
+ esc_url(
874
+ add_query_arg(
875
+ [
876
+ 'page' => 'pmpro-membershiplevels',
877
+ 'edit' => - 1,
878
+ 'copy' => $level->id,
879
+ ],
880
+ admin_url( 'admin.php' )
881
+ )
882
+ ),
883
+ esc_html__( 'Copy', 'paid-memberships-pro' )
884
+ ),
885
+ 'delete' => sprintf(
886
+ '<a title="%1$s" href="%2$s">%3$s</a>',
887
+ esc_attr__( 'Delete', 'paid-memberships-pro' ),
888
+ 'javascript:pmpro_askfirst(\'' . esc_js( $delete_text ) . '\', \'' . esc_js( $delete_nonce_url ) . '\'); void(0);',
889
+ esc_html__( 'Delete', 'paid-memberships-pro' )
890
+ ),
891
+ ];
892
+
893
+ /**
894
+ * Filter the extra actions for this level.
895
+ *
896
+ * @since 2.6.2
897
+ *
898
+ * @param array $actions The list of actions.
899
+ * @param object $level The membership level data.
900
+ */
901
+ $actions = apply_filters( 'pmpro_membershiplevels_row_actions', $actions, $level );
902
+
903
+ $actions_html = [];
904
+
905
+ foreach ( $actions as $action => $link ) {
906
+ $actions_html[] = sprintf(
907
+ '<span class="%1$s">%2$s</span>',
908
+ esc_attr( $action ),
909
+ $link
910
+ );
911
+ }
912
+
913
+ if ( ! empty( $actions_html ) ) {
914
+ echo implode( ' | ', $actions_html );
915
+ }
916
+ ?>
917
  </div>
918
  </td>
919
  <td>
930
  <?php _e('After', 'paid-memberships-pro' );?> <?php echo $level->expiration_number?> <?php echo sornot($level->expiration_period,$level->expiration_number)?>
931
  <?php } ?>
932
  </td>
933
+ <td><?php
934
+ if($level->allow_signups) {
935
+ if ( ! empty( $pmpro_pages['checkout'] ) ) {
936
+ ?><a target="_blank" href="<?php echo add_query_arg( 'level', $level->id, pmpro_url("checkout") );?>"><?php _e('Yes', 'paid-memberships-pro' );?></a><?php
937
+ } else {
938
+ _e('Yes', 'paid-memberships-pro' );
939
+ }
940
+ } else {
941
+ _e('No', 'paid-memberships-pro' );
942
+ }
943
+ ?></td>
944
  <?php do_action( 'pmpro_membership_levels_table_extra_cols_body', $level ); ?>
945
  </tr>
946
  <?php
adminpages/memberslist.php CHANGED
@@ -23,7 +23,7 @@ if ( isset( $_REQUEST['l'] ) ) {
23
 
24
  <?php do_action( 'pmpro_memberslist_before_table' ); ?>
25
  <form id="member-list-form" method="get">
26
- <input type="hidden" name="page" value="<?php echo $_REQUEST['page']; ?>" />
27
  <?php
28
  $user_list_table->search_box( __( 'Search Members', 'paid-memberships-pro' ), 'paid-memberships-pro' );
29
  $user_list_table->display();
23
 
24
  <?php do_action( 'pmpro_memberslist_before_table' ); ?>
25
  <form id="member-list-form" method="get">
26
+ <input type="hidden" name="page" value="pmpro-memberslist" />
27
  <?php
28
  $user_list_table->search_box( __( 'Search Members', 'paid-memberships-pro' ), 'paid-memberships-pro' );
29
  $user_list_table->display();
adminpages/orders-csv.php CHANGED
@@ -330,6 +330,14 @@ $default_columns = array(
330
  array( "discount_code", "code" )
331
  );
332
 
 
 
 
 
 
 
 
 
333
  $default_columns = apply_filters( "pmpro_order_list_csv_default_columns", $default_columns );
334
 
335
  $csv_file_header_array = apply_filters( "pmpro_order_list_csv_export_header_array", $csv_file_header_array );
330
  array( "discount_code", "code" )
331
  );
332
 
333
+ // Hiding couponamount by default.
334
+ $coupons = apply_filters( 'pmpro_orders_show_coupon_amounts', false );
335
+ if ( empty( $coupons ) ) {
336
+ $csv_file_header_array = array_diff( $csv_file_header_array, array( 'couponamount' ) );
337
+ $couponamount_array_key = array_keys( $default_columns, array( 'order', 'couponamount' ) );
338
+ unset( $default_columns[ $couponamount_array_key[0] ] );
339
+ }
340
+
341
  $default_columns = apply_filters( "pmpro_order_list_csv_default_columns", $default_columns );
342
 
343
  $csv_file_header_array = apply_filters( "pmpro_order_list_csv_export_header_array", $csv_file_header_array );
adminpages/orders.php CHANGED
@@ -21,8 +21,8 @@ if ( isset( $_REQUEST['l'] ) ) {
21
  $l = false;
22
  }
23
 
24
- if ( isset( $_REQUEST['discount_code'] ) ) {
25
- $discount_code = intval( $_REQUEST['discount_code'] );
26
  } else {
27
  $discount_code = false;
28
  }
@@ -156,8 +156,14 @@ $condition = apply_filters( 'pmpro_admin_orders_query_condition', $condition, $f
156
 
157
  // deleting?
158
  if ( ! empty( $_REQUEST['delete'] ) ) {
 
 
 
 
 
 
159
  $dorder = new MemberOrder( intval( $_REQUEST['delete'] ) );
160
- if ( $dorder->deleteMe() ) {
161
  $pmpro_msg = __( 'Order deleted successfully.', 'paid-memberships-pro' );
162
  $pmpro_msgt = 'success';
163
  } else {
@@ -230,9 +236,15 @@ if ( ! empty( $_REQUEST['save'] ) ) {
230
  if ( ! in_array( 'tax', $read_only_fields ) && isset( $_POST['tax'] ) ) {
231
  $order->tax = sanitize_text_field( $_POST['tax'] );
232
  }
233
- if ( ! in_array( 'couponamount', $read_only_fields ) && isset( $_POST['couponamount'] ) ) {
234
- $order->couponamount = sanitize_text_field( $_POST['couponamount'] );
 
 
 
 
 
235
  }
 
236
  if ( ! in_array( 'total', $read_only_fields ) && isset( $_POST['total'] ) ) {
237
  $order->total = sanitize_text_field( $_POST['total'] );
238
  }
@@ -271,6 +283,14 @@ if ( ! empty( $_REQUEST['save'] ) ) {
271
  global $allowedposttags;
272
  $order->notes = wp_kses( wp_unslash( $_REQUEST['notes'] ), $allowedposttags );
273
  }
 
 
 
 
 
 
 
 
274
 
275
  // affiliate stuff
276
  $affiliates = apply_filters( 'pmpro_orders_show_affiliate_ids', false );
@@ -290,22 +310,15 @@ if ( ! empty( $_REQUEST['save'] ) ) {
290
  }
291
 
292
  // save
293
- if ( $order->saveOrder() !== false && $nonceokay ) {
294
  $order_id = $order->id;
295
-
296
- // handle timestamp
297
- if ( $order->updateTimestamp( intval( $_POST['ts_year'] ), intval( $_POST['ts_month'] ), intval( $_POST['ts_day'] ), intval( $_POST['ts_hour'] ) . ':' . intval( $_POST['ts_minute'] ) . ':00' ) !== false ) {
298
- $pmpro_msg = __( 'Order saved successfully.', 'paid-memberships-pro' );
299
- $pmpro_msgt = 'success';
300
- } else {
301
- $pmpro_msg = __( 'Error updating order timestamp.', 'paid-memberships-pro' );
302
- $pmpro_msgt = 'error';
303
- }
304
  } else {
305
  $pmpro_msg = __( 'Error saving order.', 'paid-memberships-pro' );
306
  $pmpro_msgt = 'error';
307
  }
308
-
309
  // also update the discount code if needed
310
  if( isset( $_REQUEST['discount_code_id'] ) ) {
311
  $order->updateDiscountCode( intval( $_REQUEST['discount_code_id'] ) );
@@ -419,7 +432,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
419
  echo esc_html( $order->code );
420
  } else { ?>
421
  <input id="code" name="code" type="text" value="<?php echo esc_attr( $order->code ); ?>" class="regular-text" />
422
- <?php
423
  }
424
  ?>
425
  <?php if ( $order_id < 0 ) { ?>
@@ -435,7 +448,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
435
  echo esc_html( $order->user_id );
436
  } else { ?>
437
  <input id="user_id" name="user_id" type="text" value="<?php echo esc_attr( $order->user_id ); ?>" class="regular-text" />
438
- <?php
439
  }
440
  ?>
441
  </td>
@@ -448,7 +461,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
448
  echo esc_html( $order->membership_id );
449
  } else { ?>
450
  <input id="membership_id" name="membership_id" type="text" value="<?php echo esc_attr( $order->membership_id ); ?>" class="regular-text" />
451
- <?php
452
  }
453
  ?>
454
  </td>
@@ -468,8 +481,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
468
  </td>
469
  </tr>
470
  <tr>
471
- <th scope="row" valign="top"><label for="billing_street"><?php esc_html_e( 'Billing Street', 'paid-memberships-pro' ); ?>
472
- :</label></th>
473
  <td>
474
  <?php
475
  if ( in_array( 'billing_street', $read_only_fields ) && $order_id > 0 ) {
@@ -494,8 +506,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
494
  <?php } ?>
495
  </tr>
496
  <tr>
497
- <th scope="row" valign="top"><label for="billing_state"><?php esc_html_e( 'Billing State', 'paid-memberships-pro' ); ?>
498
- :</label></th>
499
  <td>
500
  <?php
501
  if ( in_array( 'billing_state', $read_only_fields ) && $order_id > 0 ) {
@@ -507,8 +518,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
507
  <?php } ?>
508
  </tr>
509
  <tr>
510
- <th scope="row" valign="top"><label for="billing_zip"><?php esc_html_e( 'Billing Postal Code', 'paid-memberships-pro' ); ?>
511
- :</label></th>
512
  <td>
513
  <?php
514
  if ( in_array( 'billing_zip', $read_only_fields ) && $order_id > 0 ) {
@@ -520,8 +530,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
520
  <?php } ?>
521
  </tr>
522
  <tr>
523
- <th scope="row" valign="top"><label for="billing_country"><?php esc_html_e( 'Billing Country', 'paid-memberships-pro' ); ?>
524
- :</label></th>
525
  <td>
526
  <?php
527
  if ( in_array( 'billing_country', $read_only_fields ) && $order_id > 0 ) {
@@ -534,8 +543,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
534
  </td>
535
  </tr>
536
  <tr>
537
- <th scope="row" valign="top"><label for="billing_phone"><?php esc_html_e( 'Billing Phone', 'paid-memberships-pro' ); ?>
538
- :</label></th>
539
  <td>
540
  <?php
541
  if ( in_array( 'billing_phone', $read_only_fields ) && $order_id > 0 ) {
@@ -611,19 +619,28 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
611
  <?php } ?>
612
  </td>
613
  </tr>
614
- <tr>
615
- <th scope="row" valign="top"><label for="couponamount"><?php esc_html_e( 'Coupon Amount', 'paid-memberships-pro' ); ?>:</label>
616
- </th>
617
- <td>
 
 
 
 
618
  <?php
619
- if ( in_array( 'couponamount', $read_only_fields ) && $order_id > 0 ) {
620
- echo esc_html( $order->couponamount );
621
- } else {
 
 
 
 
622
  ?>
623
- <input id="couponamount" name="couponamount" type="text" size="10" value="<?php echo esc_attr( $order->couponamount ); ?>"/>
624
- <?php } ?>
625
- </td>
626
- </tr>
 
627
  <tr>
628
  <th scope="row" valign="top"><label for="total"><?php esc_html_e( 'Total', 'paid-memberships-pro' ); ?>:</label></th>
629
  <td>
@@ -635,7 +652,6 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
635
  <input id="total" name="total" type="text" size="10"
636
  value="<?php echo esc_attr( $order->total ); ?>"/>
637
  <?php } ?>
638
- <p class="description"><?php esc_html_e( 'Should be subtotal + tax - couponamount.', 'paid-memberships-pro' ); ?></p>
639
  </td>
640
  </tr>
641
 
@@ -655,7 +671,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
655
  </td>
656
  </tr>
657
  <tr>
658
- <th scope="row" valign="top"><label for="cardtype"><?php esc_html_e( 'Card Type', 'paid-memberships-pro' ); ?></label></th>
659
  <td>
660
  <?php
661
  if ( in_array( 'cardtype', $read_only_fields ) && $order_id > 0 ) {
@@ -669,8 +685,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
669
  </td>
670
  </tr>
671
  <tr>
672
- <th scope="row" valign="top"><label for="accountnumber"><?php esc_html_e( 'Account Number', 'paid-memberships-pro' ); ?>
673
- :</label></th>
674
  <td>
675
  <?php
676
  if ( in_array( 'accountnumber', $read_only_fields ) && $order_id > 0 ) {
@@ -708,8 +723,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
708
  </td>
709
  </tr>
710
  <tr>
711
- <th scope="row" valign="top"><label for="expirationyear"><?php esc_html_e( 'Expiration Year', 'paid-memberships-pro' ); ?>
712
- :</label></th>
713
  <td>
714
  <input id="expirationyear" name="expirationyear" type="text" size="10"
715
  value="<?php echo esc_attr( $order->expirationyear ); ?>"/>
@@ -733,8 +747,8 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
733
  value="<?php echo esc_attr( $status ); ?>" <?php selected( $order->status, $status ); ?>><?php echo esc_html( $status ); ?></option>
734
  <?php } ?>
735
  </select>
736
- <?php
737
- }
738
  ?>
739
  </td>
740
  </tr>
@@ -795,8 +809,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
795
  </tr>
796
  <tr>
797
  <th scope="row" valign="top"><label
798
- for="subscription_transaction_id"><?php esc_html_e( 'Subscription Transaction ID', 'paid-memberships-pro' ); ?>
799
- :</label></th>
800
  <td>
801
  <?php
802
  if ( in_array( 'subscription_transaction_id', $read_only_fields ) && $order_id > 0 ) {
@@ -823,7 +836,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
823
  } else {
824
  $timestamp = current_time( 'timestamp' );
825
  }
826
-
827
  $year = date( 'Y', $timestamp );
828
  $month = date( 'n', $timestamp );
829
  $day = date( 'j', $timestamp );
@@ -908,7 +921,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
908
  <th scope="row" valign="top"><label for="notes"><?php esc_html_e( 'Notes', 'paid-memberships-pro' ); ?>:</label></th>
909
  <td>
910
  <?php
911
- if ( in_array( 'notes', $read_only_fields ) && $order_id > 0 ) {
912
  echo wp_kses_post( $order->notes );
913
  } else {
914
  ?>
@@ -922,6 +935,17 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
922
  </tbody>
923
  </table>
924
 
 
 
 
 
 
 
 
 
 
 
 
925
  <p class="submit topborder">
926
  <input name="order" type="hidden" value="
927
  <?php
@@ -934,7 +958,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
934
  "/>
935
  <input name="save" type="submit" class="button-primary" value="<?php esc_attr_e( 'Save Order', 'paid-memberships-pro' ); ?>"/>
936
  <input name="cancel" type="button" class="cancel button-secondary" value="<?php esc_attr_e( 'Cancel', 'paid-memberships-pro' ); ?>"
937
- onclick="location.href='<?php echo esc_url( get_admin_url( null, '/admin.php?page=pmpro-orders' ) ); ?>';"/>
938
  </p>
939
 
940
  </form>
@@ -944,7 +968,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
944
  <form id="posts-filter" method="get" action="">
945
 
946
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Orders', 'paid-memberships-pro' ); ?></h1>
947
- <a href="<?php echo esc_url( add_query_arg( array( 'page' => 'pmpro-orders', 'order' => -1 ), get_admin_url(null, 'admin.php' ) ) ); ?>" class="page-title-action"><?php esc_html_e( 'Add New Order', 'paid-memberships-pro' ); ?></a>
948
 
949
  <?php
950
  // build the export URL
@@ -966,7 +990,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
966
  $export_url = add_query_arg( $url_params, $export_url );
967
  ?>
968
  <a target="_blank" href="<?php echo esc_url( $export_url ); ?>" class="page-title-action"><?php esc_html_e( 'Export to CSV', 'paid-memberships-pro' ); ?></a>
969
-
970
  <hr class="wp-header-end">
971
 
972
 
@@ -998,9 +1022,9 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
998
  value="with-discount-code" <?php selected( $filter, 'with-discount-code' ); ?>><?php esc_html_e( 'With a Discount Code', 'paid-memberships-pro' ); ?></option>
999
  <option
1000
  value="within-a-status" <?php selected( $filter, 'within-a-status' ); ?>><?php esc_html_e( 'Within a Status', 'paid-memberships-pro' ); ?></option>
1001
- <option
1002
  value="only-paid" <?php selected( $filter, 'only-paid' ); ?>><?php esc_html_e( 'Only Paid Orders', 'paid-memberships-pro' ); ?></option>
1003
- <option
1004
  value="only-free" <?php selected( $filter, 'only-free' ); ?>><?php esc_html_e( 'Only Free Orders', 'paid-memberships-pro' ); ?></option>
1005
 
1006
  <?php $custom_filters = apply_filters( 'pmpro_admin_orders_filters', array() ); ?>
@@ -1054,7 +1078,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1054
 
1055
  <?php
1056
  // Note: only orders belonging to current levels can be filtered. There is no option for orders belonging to deleted levels
1057
- $levels = pmpro_getAllLevels( true, true );
1058
  ?>
1059
  <select id="l" name="l">
1060
  <?php foreach ( $levels as $level ) { ?>
@@ -1063,13 +1087,13 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1063
  <?php } ?>
1064
 
1065
  </select>
1066
-
1067
  <?php
1068
  $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS * FROM $wpdb->pmpro_discount_codes ";
1069
  $sqlQuery .= "ORDER BY id DESC ";
1070
  $codes = $wpdb->get_results($sqlQuery, OBJECT);
1071
  if ( ! empty( $codes ) ) { ?>
1072
- <select id="discount_code" name="discount_code">
1073
  <?php foreach ( $codes as $code ) { ?>
1074
  <option
1075
  value="<?php echo esc_attr( $code->id ); ?>" <?php selected( $discount_code, $code->id ); ?>><?php echo esc_html( $code->code ); ?></option>
@@ -1111,7 +1135,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1111
  jQuery('#predefined-date').hide();
1112
  jQuery('#status').hide();
1113
  jQuery('#l').hide();
1114
- jQuery('#discount_code').hide();
1115
  jQuery('#from').hide();
1116
  jQuery('#to').hide();
1117
  jQuery('#submit').show();
@@ -1127,7 +1151,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1127
  jQuery('#predefined-date').hide();
1128
  jQuery('#status').hide();
1129
  jQuery('#l').hide();
1130
- jQuery('#discount_code').hide();
1131
  jQuery('#submit').show();
1132
  jQuery('#from').show();
1133
  jQuery('#to').show();
@@ -1143,7 +1167,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1143
  jQuery('#predefined-date').show();
1144
  jQuery('#status').hide();
1145
  jQuery('#l').hide();
1146
- jQuery('#discount_code').hide();
1147
  jQuery('#submit').show();
1148
  jQuery('#from').hide();
1149
  jQuery('#to').hide();
@@ -1159,7 +1183,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1159
  jQuery('#predefined-date').hide();
1160
  jQuery('#status').hide();
1161
  jQuery('#l').show();
1162
- jQuery('#discount_code').hide();
1163
  jQuery('#submit').show();
1164
  jQuery('#from').hide();
1165
  jQuery('#to').hide();
@@ -1175,7 +1199,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1175
  jQuery('#predefined-date').hide();
1176
  jQuery('#status').hide();
1177
  jQuery('#l').hide();
1178
- jQuery('#discount_code').show();
1179
  jQuery('#submit').show();
1180
  jQuery('#from').hide();
1181
  jQuery('#to').hide();
@@ -1191,7 +1215,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1191
  jQuery('#predefined-date').hide();
1192
  jQuery('#status').show();
1193
  jQuery('#l').hide();
1194
- jQuery('#discount_code').hide();
1195
  jQuery('#submit').show();
1196
  jQuery('#from').hide();
1197
  jQuery('#to').hide();
@@ -1207,7 +1231,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1207
  jQuery('#predefined-date').hide();
1208
  jQuery('#status').hide();
1209
  jQuery('#l').hide();
1210
- jQuery('#discount_code').hide();
1211
  jQuery('#submit').show();
1212
  jQuery('#from').hide();
1213
  jQuery('#to').hide();
@@ -1223,7 +1247,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1223
  <p class="search-box">
1224
  <label class="hidden" for="post-search-input"><?php esc_html_e( 'Search Orders', 'paid-memberships-pro' ); ?>:</label>
1225
  <input type="hidden" name="page" value="pmpro-orders"/>
1226
- <input id="post-search-input" type="text" value="<?php echo esc_attr( $s ); ?>" name="s"/>
1227
  <input class="button" type="submit" value="<?php esc_attr_e( 'Search Orders', 'paid-memberships-pro' ); ?>"/>
1228
  </p>
1229
 
@@ -1235,7 +1259,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1235
  if ( $join_with_usermeta ) {
1236
  $sqlQuery .= "LEFT JOIN $wpdb->usermeta um ON o.user_id = um.user_id ";
1237
  }
1238
-
1239
  if ( $filter === 'with-discount-code' ) {
1240
  $sqlQuery .= "LEFT JOIN $wpdb->pmpro_discount_codes_uses dc ON o.id = dc.order_id ";
1241
  }
@@ -1281,18 +1305,18 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1281
  $sqlQuery .= 'GROUP BY o.id ORDER BY o.id DESC, o.timestamp DESC ';
1282
  } else {
1283
  $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS o.id FROM $wpdb->pmpro_membership_orders o ";
1284
-
1285
  if ( $filter === 'with-discount-code' ) {
1286
  $sqlQuery .= "LEFT JOIN $wpdb->pmpro_discount_codes_uses dc ON o.id = dc.order_id ";
1287
  }
1288
-
1289
  $sqlQuery .= "WHERE " . $condition . ' ORDER BY o.id DESC, o.timestamp DESC ';
1290
  }
1291
 
1292
  $sqlQuery .= "LIMIT $start, $limit";
1293
 
1294
  $order_ids = $wpdb->get_col( $sqlQuery );
1295
-
1296
  $totalrows = $wpdb->get_var( 'SELECT FOUND_ROWS() as found_rows' );
1297
 
1298
  if ( $order_ids ) {
@@ -1306,7 +1330,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1306
  <tr class="thead">
1307
  <th><?php esc_html_e( 'ID', 'paid-memberships-pro' ); ?></th>
1308
  <th><?php esc_html_e( 'Code', 'paid-memberships-pro' ); ?></th>
1309
- <th><?php esc_html_e( 'Username', 'paid-memberships-pro' ); ?></th>
1310
  <?php do_action( 'pmpro_orders_extra_cols_header', $order_ids ); ?>
1311
  <th><?php esc_html_e( 'Level', 'paid-memberships-pro' ); ?></th>
1312
  <th><?php esc_html_e( 'Total', 'paid-memberships-pro' ); ?></th>
@@ -1336,36 +1360,108 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1336
  <a href="admin.php?page=pmpro-orders&order=<?php echo esc_attr( $order->id ); ?>"><?php echo esc_html( $order->code ); ?></a>
1337
  <br />
1338
  <div class="row-actions">
1339
- <span class="edit">
1340
- <a title="<?php esc_attr_e( 'Edit', 'paid-memberships-pro' ); ?>" href="<?php echo esc_url( add_query_arg( array( 'page' => 'pmpro-orders', 'order' => $order->id ), admin_url('admin.php' ) ) ); ?>"><?php esc_html_e( 'Edit', 'paid-memberships-pro' ); ?></a>
1341
- </span> |
1342
- <span class="copy">
1343
- <a title="<?php esc_attr_e( 'Copy', 'paid-memberships-pro' ); ?>" href="<?php echo esc_url( add_query_arg( array( 'page' => 'pmpro-orders', 'order' => '-1', 'copy' => $order->id ), admin_url('admin.php' ) ) ); ?>"><?php esc_html_e( 'Copy', 'paid-memberships-pro' ); ?></a>
1344
- </span> |
1345
- <span class="delete">
1346
- <a href="javascript:pmpro_askfirst('<?php echo esc_attr
1347
- ( sprintf( __( 'Deleting orders is permanent and can affect active users. Are you sure you want to delete order %s?', 'paid-memberships-pro' ), str_replace( "'", '', $order->code ) ) ); ?>', 'admin.php?page=pmpro-orders&delete=<?php echo $order->id; ?>'); void(0);"><?php esc_html_e( 'Delete', 'paid-memberships-pro' ); ?></a>
1348
- </span> |
1349
- <span class="print">
1350
- <a target="_blank" title="<?php esc_attr_e( 'Print', 'paid-memberships-pro' ); ?>" href="<?php echo esc_url( add_query_arg( array( 'action' => 'pmpro_orders_print_view', 'order' => $order->id ), admin_url('admin-ajax.php' ) ) ); ?>"><?php esc_html_e( 'Print', 'paid-memberships-pro' ); ?></a>
1351
- </span> |
1352
- <span class="email">
1353
- <a href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link"
1354
- data-order="<?php echo esc_attr( $order->id ); ?>"><?php esc_html_e( 'Email', 'paid-memberships-pro' ); ?></a>
1355
- </span>
1356
  <?php
1357
- // Set up the hover actions for this user
1358
- $actions = apply_filters( 'pmpro_orders_user_row_actions', array(), $order->user, $order );
1359
- $action_count = count( $actions );
1360
- $i = 0;
1361
- if ( $action_count ) {
1362
- $out = ' | ';
1363
- foreach ( $actions as $action => $link ) {
1364
- ++ $i;
1365
- ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
1366
- $out .= "<span class='" . esc_attr( $action ) . "'>" . $link . $sep . "</span>";
1367
- }
1368
- echo $out;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1369
  }
1370
  ?>
1371
  </div>
@@ -1373,12 +1469,13 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1373
  <td class="username column-username">
1374
  <?php $order->getUser(); ?>
1375
  <?php if ( ! empty( $order->user ) ) { ?>
1376
- <a href="user-edit.php?user_id=<?php echo esc_attr( $order->user->ID ); ?>"><?php echo esc_html( $order->user->user_login ); ?></a>
 
1377
  <?php } elseif ( $order->user_id > 0 ) { ?>
1378
  [<?php esc_html_e( 'deleted', 'paid-memberships-pro' ); ?>]
1379
  <?php } else { ?>
1380
  [<?php esc_html_e( 'none', 'paid-memberships-pro' ); ?>]
1381
- <?php } ?>
1382
  </td>
1383
  <?php do_action( 'pmpro_orders_extra_cols_body', $order ); ?>
1384
  <td>
@@ -1393,7 +1490,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1393
  <?php }
1394
  ?>
1395
  </td>
1396
- <td><?php echo esc_html( pmpro_formatPrice( $order->total ) ); ?></td>
1397
  <td>
1398
  <?php
1399
  if ( ! empty( $order->payment_type ) ) {
@@ -1409,7 +1506,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1409
  <?php if ( ! empty( $order->billing->street ) ) { ?>
1410
  <?php echo esc_html( $order->billing->street ); ?><br/>
1411
  <?php if ( $order->billing->city && $order->billing->state ) { ?>
1412
- <?php echo esc_html( $order->billing->city ); ?>, <?php echo esc_html( $order->billing->state ); ?><?php echo esc_html( $order->billing->zip ); ?>
1413
  <?php
1414
  if ( ! empty( $order->billing->country ) ) {
1415
  echo esc_html( $order->billing->country ); }
@@ -1431,7 +1528,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1431
  ?>
1432
  </td>
1433
  <td>
1434
- <?php esc_html_e( 'Payment', 'paid-memberships-pro' ); ?>:
1435
  <?php
1436
  if ( ! empty( $order->payment_transaction_id ) ) {
1437
  echo esc_html( $order->payment_transaction_id );
@@ -1440,8 +1537,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1440
  }
1441
  ?>
1442
  <br/>
1443
- <?php esc_html_e( 'Subscription', 'paid-memberships-pro' ); ?>
1444
- :
1445
  <?php
1446
  if ( ! empty( $order->subscription_transaction_id ) ) {
1447
  echo esc_html( $order->subscription_transaction_id );
@@ -1460,7 +1556,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1460
  <a title="<?php esc_attr_e('edit', 'paid-memberships-pro' ); ?>" href="<?php echo esc_url( add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $order->discount_code->id ), admin_url('admin.php' ) ) ); ?>">
1461
  <?php echo esc_html( $order->discount_code->code ); ?>
1462
  </a>
1463
- <?php } ?>
1464
  </td>
1465
  </tr>
1466
  <?php
@@ -1479,7 +1575,7 @@ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
1479
  </form>
1480
  <?php
1481
  // add normal args
1482
- $pagination_url = esc_url( add_query_arg( $url_params, get_admin_url( null, '/admin.php?page=pmpro-orders' ) ) );
1483
  echo pmpro_getPaginationString( $pn, $totalrows, $limit, 1, $pagination_url, "&limit=$limit&pn=" );
1484
  ?>
1485
 
21
  $l = false;
22
  }
23
 
24
+ if ( isset( $_REQUEST['discount-code'] ) ) {
25
+ $discount_code = intval( $_REQUEST['discount-code'] );
26
  } else {
27
  $discount_code = false;
28
  }
156
 
157
  // deleting?
158
  if ( ! empty( $_REQUEST['delete'] ) ) {
159
+ // Check nonce for deleting.
160
+ $nonceokay = true;
161
+ if ( empty( $_REQUEST['pmpro_orders_nonce'] ) || ! check_admin_referer( 'delete_order', 'pmpro_orders_nonce' ) ) {
162
+ $nonceokay = false;
163
+ }
164
+
165
  $dorder = new MemberOrder( intval( $_REQUEST['delete'] ) );
166
+ if ( $nonceokay && $dorder->deleteMe() ) {
167
  $pmpro_msg = __( 'Order deleted successfully.', 'paid-memberships-pro' );
168
  $pmpro_msgt = 'success';
169
  } else {
236
  if ( ! in_array( 'tax', $read_only_fields ) && isset( $_POST['tax'] ) ) {
237
  $order->tax = sanitize_text_field( $_POST['tax'] );
238
  }
239
+
240
+ // Hiding couponamount by default.
241
+ $coupons = apply_filters( 'pmpro_orders_show_coupon_amounts', false );
242
+ if ( ! empty( $coupons ) ) {
243
+ if ( ! in_array( 'couponamount', $read_only_fields ) && isset( $_POST['couponamount'] ) ) {
244
+ $order->couponamount = sanitize_text_field( $_POST['couponamount'] );
245
+ }
246
  }
247
+
248
  if ( ! in_array( 'total', $read_only_fields ) && isset( $_POST['total'] ) ) {
249
  $order->total = sanitize_text_field( $_POST['total'] );
250
  }
283
  global $allowedposttags;
284
  $order->notes = wp_kses( wp_unslash( $_REQUEST['notes'] ), $allowedposttags );
285
  }
286
+ if ( ! in_array( 'timestamp', $read_only_fields ) && isset( $_POST['ts_year'] ) && isset( $_POST['ts_month'] ) && isset( $_POST['ts_day'] ) && isset( $_POST['ts_hour'] ) && isset( $_POST['ts_minute'] ) ) {
287
+ $year = intval( $_POST['ts_year'] );
288
+ $month = intval( $_POST['ts_month'] );
289
+ $day = intval( $_POST['ts_day'] );
290
+ $hour = intval( $_POST['ts_hour'] );
291
+ $minute = intval( $_POST['ts_minute'] );
292
+ $order->timestamp = $date = get_gmt_from_date( $year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $minute . ':00' , 'U' );;
293
+ }
294
 
295
  // affiliate stuff
296
  $affiliates = apply_filters( 'pmpro_orders_show_affiliate_ids', false );
310
  }
311
 
312
  // save
313
+ if ( $nonceokay && false !== $order->saveOrder() ) {
314
  $order_id = $order->id;
315
+ $pmpro_msg = __( 'Order saved successfully.', 'paid-memberships-pro' );
316
+ $pmpro_msgt = 'success';
 
 
 
 
 
 
 
317
  } else {
318
  $pmpro_msg = __( 'Error saving order.', 'paid-memberships-pro' );
319
  $pmpro_msgt = 'error';
320
  }
321
+
322
  // also update the discount code if needed
323
  if( isset( $_REQUEST['discount_code_id'] ) ) {
324
  $order->updateDiscountCode( intval( $_REQUEST['discount_code_id'] ) );
432
  echo esc_html( $order->code );
433
  } else { ?>
434
  <input id="code" name="code" type="text" value="<?php echo esc_attr( $order->code ); ?>" class="regular-text" />
435
+ <?php
436
  }
437
  ?>
438
  <?php if ( $order_id < 0 ) { ?>
448
  echo esc_html( $order->user_id );
449
  } else { ?>
450
  <input id="user_id" name="user_id" type="text" value="<?php echo esc_attr( $order->user_id ); ?>" class="regular-text" />
451
+ <?php
452
  }
453
  ?>
454
  </td>
461
  echo esc_html( $order->membership_id );
462
  } else { ?>
463
  <input id="membership_id" name="membership_id" type="text" value="<?php echo esc_attr( $order->membership_id ); ?>" class="regular-text" />
464
+ <?php
465
  }
466
  ?>
467
  </td>
481
  </td>
482
  </tr>
483
  <tr>
484
+ <th scope="row" valign="top"><label for="billing_street"><?php esc_html_e( 'Billing Street', 'paid-memberships-pro' ); ?>:</label></th>
 
485
  <td>
486
  <?php
487
  if ( in_array( 'billing_street', $read_only_fields ) && $order_id > 0 ) {
506
  <?php } ?>
507
  </tr>
508
  <tr>
509
+ <th scope="row" valign="top"><label for="billing_state"><?php esc_html_e( 'Billing State', 'paid-memberships-pro' ); ?>:</label></th>
 
510
  <td>
511
  <?php
512
  if ( in_array( 'billing_state', $read_only_fields ) && $order_id > 0 ) {
518
  <?php } ?>
519
  </tr>
520
  <tr>
521
+ <th scope="row" valign="top"><label for="billing_zip"><?php esc_html_e( 'Billing Postal Code', 'paid-memberships-pro' ); ?>:</label></th>
 
522
  <td>
523
  <?php
524
  if ( in_array( 'billing_zip', $read_only_fields ) && $order_id > 0 ) {
530
  <?php } ?>
531
  </tr>
532
  <tr>
533
+ <th scope="row" valign="top"><label for="billing_country"><?php esc_html_e( 'Billing Country', 'paid-memberships-pro' ); ?>:</label></th>
 
534
  <td>
535
  <?php
536
  if ( in_array( 'billing_country', $read_only_fields ) && $order_id > 0 ) {
543
  </td>
544
  </tr>
545
  <tr>
546
+ <th scope="row" valign="top"><label for="billing_phone"><?php esc_html_e( 'Billing Phone', 'paid-memberships-pro' ); ?>:</label></th>
 
547
  <td>
548
  <?php
549
  if ( in_array( 'billing_phone', $read_only_fields ) && $order_id > 0 ) {
619
  <?php } ?>
620
  </td>
621
  </tr>
622
+ <?php
623
+ // Hiding couponamount by default.
624
+ $coupons = apply_filters( 'pmpro_orders_show_coupon_amounts', false );
625
+ if ( ! empty( $coupons ) ) { ?>
626
+ <tr>
627
+ <th scope="row" valign="top"><label for="couponamount"><?php esc_html_e( 'Coupon Amount', 'paid-memberships-pro' ); ?>:</label>
628
+ </th>
629
+ <td>
630
  <?php
631
+ if ( in_array( 'couponamount', $read_only_fields ) && $order_id > 0 ) {
632
+ echo $order->couponamount;
633
+ } else {
634
+ ?>
635
+ <input id="couponamount" name="couponamount" type="text" size="10" value="<?php echo esc_attr( $order->couponamount ); ?>"/>
636
+ <?php
637
+ }
638
  ?>
639
+ </td>
640
+ </tr>
641
+ <?php
642
+ }
643
+ ?>
644
  <tr>
645
  <th scope="row" valign="top"><label for="total"><?php esc_html_e( 'Total', 'paid-memberships-pro' ); ?>:</label></th>
646
  <td>
652
  <input id="total" name="total" type="text" size="10"
653
  value="<?php echo esc_attr( $order->total ); ?>"/>
654
  <?php } ?>
 
655
  </td>
656
  </tr>
657
 
671
  </td>
672
  </tr>
673
  <tr>
674
+ <th scope="row" valign="top"><label for="cardtype"><?php esc_html_e( 'Card Type', 'paid-memberships-pro' ); ?>:</label></th>
675
  <td>
676
  <?php
677
  if ( in_array( 'cardtype', $read_only_fields ) && $order_id > 0 ) {
685
  </td>
686
  </tr>
687
  <tr>
688
+ <th scope="row" valign="top"><label for="accountnumber"><?php esc_html_e( 'Account Number', 'paid-memberships-pro' ); ?>:</label></th>
 
689
  <td>
690
  <?php
691
  if ( in_array( 'accountnumber', $read_only_fields ) && $order_id > 0 ) {
723
  </td>
724
  </tr>
725
  <tr>
726
+ <th scope="row" valign="top"><label for="expirationyear"><?php esc_html_e( 'Expiration Year', 'paid-memberships-pro' ); ?>:</label></th>
 
727
  <td>
728
  <input id="expirationyear" name="expirationyear" type="text" size="10"
729
  value="<?php echo esc_attr( $order->expirationyear ); ?>"/>
747
  value="<?php echo esc_attr( $status ); ?>" <?php selected( $order->status, $status ); ?>><?php echo esc_html( $status ); ?></option>
748
  <?php } ?>
749
  </select>
750
+ <?php
751
+ }
752
  ?>
753
  </td>
754
  </tr>
809
  </tr>
810
  <tr>
811
  <th scope="row" valign="top"><label
812
+ for="subscription_transaction_id"><?php esc_html_e( 'Subscription Transaction ID', 'paid-memberships-pro' ); ?>:</label></th>
 
813
  <td>
814
  <?php
815
  if ( in_array( 'subscription_transaction_id', $read_only_fields ) && $order_id > 0 ) {
836
  } else {
837
  $timestamp = current_time( 'timestamp' );
838
  }
839
+
840
  $year = date( 'Y', $timestamp );
841
  $month = date( 'n', $timestamp );
842
  $day = date( 'j', $timestamp );
921
  <th scope="row" valign="top"><label for="notes"><?php esc_html_e( 'Notes', 'paid-memberships-pro' ); ?>:</label></th>
922
  <td>
923
  <?php
924
+ if ( in_array( 'notes', $read_only_fields ) && $order_id > 0 ) {
925
  echo wp_kses_post( $order->notes );
926
  } else {
927
  ?>
935
  </tbody>
936
  </table>
937
 
938
+ <?php
939
+ /**
940
+ * Allow adding other content after the Order Settings table.
941
+ *
942
+ * @since 2.5.10
943
+ *
944
+ * @param MemberOrder $order Member order object.
945
+ */
946
+ do_action( 'pmpro_after_order_settings_table', $order );
947
+ ?>
948
+
949
  <p class="submit topborder">
950
  <input name="order" type="hidden" value="
951
  <?php
958
  "/>
959
  <input name="save" type="submit" class="button-primary" value="<?php esc_attr_e( 'Save Order', 'paid-memberships-pro' ); ?>"/>
960
  <input name="cancel" type="button" class="cancel button-secondary" value="<?php esc_attr_e( 'Cancel', 'paid-memberships-pro' ); ?>"
961
+ onclick="location.href='<?php echo esc_url( admin_url( '/admin.php?page=pmpro-orders' ) ); ?>';"/>
962
  </p>
963
 
964
  </form>
968
  <form id="posts-filter" method="get" action="">
969
 
970
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Orders', 'paid-memberships-pro' ); ?></h1>
971
+ <a href="<?php echo esc_url( add_query_arg( array( 'page' => 'pmpro-orders', 'order' => -1 ), admin_url( 'admin.php' ) ) ); ?>" class="page-title-action"><?php esc_html_e( 'Add New Order', 'paid-memberships-pro' ); ?></a>
972
 
973
  <?php
974
  // build the export URL
990
  $export_url = add_query_arg( $url_params, $export_url );
991
  ?>
992
  <a target="_blank" href="<?php echo esc_url( $export_url ); ?>" class="page-title-action"><?php esc_html_e( 'Export to CSV', 'paid-memberships-pro' ); ?></a>
993
+
994
  <hr class="wp-header-end">
995
 
996
 
1022
  value="with-discount-code" <?php selected( $filter, 'with-discount-code' ); ?>><?php esc_html_e( 'With a Discount Code', 'paid-memberships-pro' ); ?></option>
1023
  <option
1024
  value="within-a-status" <?php selected( $filter, 'within-a-status' ); ?>><?php esc_html_e( 'Within a Status', 'paid-memberships-pro' ); ?></option>
1025
+ <option
1026
  value="only-paid" <?php selected( $filter, 'only-paid' ); ?>><?php esc_html_e( 'Only Paid Orders', 'paid-memberships-pro' ); ?></option>
1027
+ <option
1028
  value="only-free" <?php selected( $filter, 'only-free' ); ?>><?php esc_html_e( 'Only Free Orders', 'paid-memberships-pro' ); ?></option>
1029
 
1030
  <?php $custom_filters = apply_filters( 'pmpro_admin_orders_filters', array() ); ?>
1078
 
1079
  <?php
1080
  // Note: only orders belonging to current levels can be filtered. There is no option for orders belonging to deleted levels
1081
+ $levels = pmpro_sort_levels_by_order( pmpro_getAllLevels( true, true ) );
1082
  ?>
1083
  <select id="l" name="l">
1084
  <?php foreach ( $levels as $level ) { ?>
1087
  <?php } ?>
1088
 
1089
  </select>
1090
+
1091
  <?php
1092
  $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS * FROM $wpdb->pmpro_discount_codes ";
1093
  $sqlQuery .= "ORDER BY id DESC ";
1094
  $codes = $wpdb->get_results($sqlQuery, OBJECT);
1095
  if ( ! empty( $codes ) ) { ?>
1096
+ <select id="discount-code" name="discount-code">
1097
  <?php foreach ( $codes as $code ) { ?>
1098
  <option
1099
  value="<?php echo esc_attr( $code->id ); ?>" <?php selected( $discount_code, $code->id ); ?>><?php echo esc_html( $code->code ); ?></option>
1135
  jQuery('#predefined-date').hide();
1136
  jQuery('#status').hide();
1137
  jQuery('#l').hide();
1138
+ jQuery('#discount-code').hide();
1139
  jQuery('#from').hide();
1140
  jQuery('#to').hide();
1141
  jQuery('#submit').show();
1151
  jQuery('#predefined-date').hide();
1152
  jQuery('#status').hide();
1153
  jQuery('#l').hide();
1154
+ jQuery('#discount-code').hide();
1155
  jQuery('#submit').show();
1156
  jQuery('#from').show();
1157
  jQuery('#to').show();
1167
  jQuery('#predefined-date').show();
1168
  jQuery('#status').hide();
1169
  jQuery('#l').hide();
1170
+ jQuery('#discount-code').hide();
1171
  jQuery('#submit').show();
1172
  jQuery('#from').hide();
1173
  jQuery('#to').hide();
1183
  jQuery('#predefined-date').hide();
1184
  jQuery('#status').hide();
1185
  jQuery('#l').show();
1186
+ jQuery('#discount-code').hide();
1187
  jQuery('#submit').show();
1188
  jQuery('#from').hide();
1189
  jQuery('#to').hide();
1199
  jQuery('#predefined-date').hide();
1200
  jQuery('#status').hide();
1201
  jQuery('#l').hide();
1202
+ jQuery('#discount-code').show();
1203
  jQuery('#submit').show();
1204
  jQuery('#from').hide();
1205
  jQuery('#to').hide();
1215
  jQuery('#predefined-date').hide();
1216
  jQuery('#status').show();
1217
  jQuery('#l').hide();
1218
+ jQuery('#discount-code').hide();
1219
  jQuery('#submit').show();
1220
  jQuery('#from').hide();
1221
  jQuery('#to').hide();
1231
  jQuery('#predefined-date').hide();
1232
  jQuery('#status').hide();
1233
  jQuery('#l').hide();
1234
+ jQuery('#discount-code').hide();
1235
  jQuery('#submit').show();
1236
  jQuery('#from').hide();
1237
  jQuery('#to').hide();
1247
  <p class="search-box">
1248
  <label class="hidden" for="post-search-input"><?php esc_html_e( 'Search Orders', 'paid-memberships-pro' ); ?>:</label>
1249
  <input type="hidden" name="page" value="pmpro-orders"/>
1250
+ <input id="post-search-input" type="text" value="<?php echo esc_attr( wp_unslash( $s ) ); ?>" name="s"/>
1251
  <input class="button" type="submit" value="<?php esc_attr_e( 'Search Orders', 'paid-memberships-pro' ); ?>"/>
1252
  </p>
1253
 
1259
  if ( $join_with_usermeta ) {
1260
  $sqlQuery .= "LEFT JOIN $wpdb->usermeta um ON o.user_id = um.user_id ";
1261
  }
1262
+
1263
  if ( $filter === 'with-discount-code' ) {
1264
  $sqlQuery .= "LEFT JOIN $wpdb->pmpro_discount_codes_uses dc ON o.id = dc.order_id ";
1265
  }
1305
  $sqlQuery .= 'GROUP BY o.id ORDER BY o.id DESC, o.timestamp DESC ';
1306
  } else {
1307
  $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS o.id FROM $wpdb->pmpro_membership_orders o ";
1308
+
1309
  if ( $filter === 'with-discount-code' ) {
1310
  $sqlQuery .= "LEFT JOIN $wpdb->pmpro_discount_codes_uses dc ON o.id = dc.order_id ";
1311
  }
1312
+
1313
  $sqlQuery .= "WHERE " . $condition . ' ORDER BY o.id DESC, o.timestamp DESC ';
1314
  }
1315
 
1316
  $sqlQuery .= "LIMIT $start, $limit";
1317
 
1318
  $order_ids = $wpdb->get_col( $sqlQuery );
1319
+
1320
  $totalrows = $wpdb->get_var( 'SELECT FOUND_ROWS() as found_rows' );
1321
 
1322
  if ( $order_ids ) {
1330
  <tr class="thead">
1331
  <th><?php esc_html_e( 'ID', 'paid-memberships-pro' ); ?></th>
1332
  <th><?php esc_html_e( 'Code', 'paid-memberships-pro' ); ?></th>
1333
+ <th><?php esc_html_e( 'User', 'paid-memberships-pro' ); ?></th>
1334
  <?php do_action( 'pmpro_orders_extra_cols_header', $order_ids ); ?>
1335
  <th><?php esc_html_e( 'Level', 'paid-memberships-pro' ); ?></th>
1336
  <th><?php esc_html_e( 'Total', 'paid-memberships-pro' ); ?></th>
1360
  <a href="admin.php?page=pmpro-orders&order=<?php echo esc_attr( $order->id ); ?>"><?php echo esc_html( $order->code ); ?></a>
1361
  <br />
1362
  <div class="row-actions">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1363
  <?php
1364
+ $delete_text = esc_html(
1365
+ sprintf(
1366
+ // translators: %s is the Order Code.
1367
+ __( 'Deleting orders is permanent and can affect active users. Are you sure you want to delete order %s?', 'paid-memberships-pro' ),
1368
+ str_replace( "'", '', $order->code )
1369
+ )
1370
+ );
1371
+
1372
+ $delete_nonce_url = wp_nonce_url(
1373
+ add_query_arg(
1374
+ [
1375
+ 'page' => 'pmpro-orders',
1376
+ 'action' => 'delete_order',
1377
+ 'delete' => $order->id,
1378
+ ],
1379
+ admin_url( 'admin.php' )
1380
+ ),
1381
+ 'delete_order',
1382
+ 'pmpro_orders_nonce'
1383
+ );
1384
+
1385
+ $actions = [
1386
+ 'edit' => sprintf(
1387
+ '<a title="%1$s" href="%2$s">%3$s</a>',
1388
+ esc_attr__( 'Edit', 'paid-memberships-pro' ),
1389
+ esc_url(
1390
+ add_query_arg(
1391
+ [
1392
+ 'page' => 'pmpro-orders',
1393
+ 'order' => $order->id,
1394
+ ],
1395
+ admin_url( 'admin.php' )
1396
+ )
1397
+ ),
1398
+ esc_html__( 'Edit', 'paid-memberships-pro' )
1399
+ ),
1400
+ 'copy' => sprintf(
1401
+ '<a title="%1$s" href="%2$s">%3$s</a>',
1402
+ esc_attr__( 'Copy', 'paid-memberships-pro' ),
1403
+ esc_url(
1404
+ add_query_arg(
1405
+ [
1406
+ 'page' => 'pmpro-orders',
1407
+ 'order' => - 1,
1408
+ 'copy' => $order->id,
1409
+ ],
1410
+ admin_url( 'admin.php' )
1411
+ )
1412
+ ),
1413
+ esc_html__( 'Copy', 'paid-memberships-pro' )
1414
+ ),
1415
+ 'delete' => sprintf(
1416
+ '<a title="%1$s" href="%2$s">%3$s</a>',
1417
+ esc_attr__( 'Delete', 'paid-memberships-pro' ),
1418
+ 'javascript:pmpro_askfirst(\'' . esc_js( $delete_text ) . '\', \'' . esc_js( $delete_nonce_url ) . '\'); void(0);',
1419
+ esc_html__( 'Delete', 'paid-memberships-pro' )
1420
+ ),
1421
+ 'print' => sprintf(
1422
+ '<a title="%1$s" href="%2$s" target="_blank" rel="noopener noreferrer">%3$s</a>',
1423
+ esc_attr__( 'Print', 'paid-memberships-pro' ),
1424
+ esc_url(
1425
+ add_query_arg(
1426
+ [
1427
+ 'action' => 'pmpro_orders_print_view',
1428
+ 'order' => $order->id,
1429
+ ],
1430
+ admin_url( 'admin-ajax.php' )
1431
+ )
1432
+ ),
1433
+ esc_html__( 'Print', 'paid-memberships-pro' )
1434
+ ),
1435
+ 'email' => sprintf(
1436
+ '<a title="%1$s" href="%2$s" data-order="%3$s" class="thickbox email_link">%4$s</a>',
1437
+ esc_attr__( 'Email', 'paid-memberships-pro' ),
1438
+ '#TB_inline?width=600&height=200&inlineId=email_invoice',
1439
+ esc_attr( $order->id ),
1440
+ esc_html__( 'Email', 'paid-memberships-pro' )
1441
+ ),
1442
+ ];
1443
+
1444
+ /**
1445
+ * Filter the extra actions for this user on this order.
1446
+ *
1447
+ * @param array $actions The list of actions.
1448
+ * @param object $user The user data.
1449
+ * @param MemberOrder $order The current order.
1450
+ */
1451
+ $actions = apply_filters( 'pmpro_orders_user_row_actions', $actions, $order->user, $order );
1452
+
1453
+ $actions_html = [];
1454
+
1455
+ foreach ( $actions as $action => $link ) {
1456
+ $actions_html[] = sprintf(
1457
+ '<span class="%1$s">%2$s</span>',
1458
+ esc_attr( $action ),
1459
+ $link
1460
+ );
1461
+ }
1462
+
1463
+ if ( ! empty( $actions_html ) ) {
1464
+ echo implode( ' | ', $actions_html );
1465
  }
1466
  ?>
1467
  </div>
1469
  <td class="username column-username">
1470
  <?php $order->getUser(); ?>
1471
  <?php if ( ! empty( $order->user ) ) { ?>
1472
+ <a href="user-edit.php?user_id=<?php echo esc_attr( $order->user->ID ); ?>"><?php echo esc_html( $order->user->user_login ); ?></a><br />
1473
+ <?php echo esc_html( $order->user->user_email ); ?>
1474
  <?php } elseif ( $order->user_id > 0 ) { ?>
1475
  [<?php esc_html_e( 'deleted', 'paid-memberships-pro' ); ?>]
1476
  <?php } else { ?>
1477
  [<?php esc_html_e( 'none', 'paid-memberships-pro' ); ?>]
1478
+ <?php } ?>
1479
  </td>
1480
  <?php do_action( 'pmpro_orders_extra_cols_body', $order ); ?>
1481
  <td>
1490
  <?php }
1491
  ?>
1492
  </td>
1493
+ <td><?php echo pmpro_escape_price( pmpro_formatPrice( $order->total ) ); ?></td>
1494
  <td>
1495
  <?php
1496
  if ( ! empty( $order->payment_type ) ) {
1506
  <?php if ( ! empty( $order->billing->street ) ) { ?>
1507
  <?php echo esc_html( $order->billing->street ); ?><br/>
1508
  <?php if ( $order->billing->city && $order->billing->state ) { ?>
1509
+ <?php echo esc_html( $order->billing->city ); ?>, <?php echo esc_html( $order->billing->state ); ?> <?php echo esc_html( $order->billing->zip ); ?>
1510
  <?php
1511
  if ( ! empty( $order->billing->country ) ) {
1512
  echo esc_html( $order->billing->country ); }
1528
  ?>
1529
  </td>
1530
  <td>
1531
+ <?php esc_html_e( 'Payment', 'paid-memberships-pro' ); ?>:
1532
  <?php
1533
  if ( ! empty( $order->payment_transaction_id ) ) {
1534
  echo esc_html( $order->payment_transaction_id );
1537
  }
1538
  ?>
1539
  <br/>
1540
+ <?php esc_html_e( 'Subscription', 'paid-memberships-pro' ); ?>:
 
1541
  <?php
1542
  if ( ! empty( $order->subscription_transaction_id ) ) {
1543
  echo esc_html( $order->subscription_transaction_id );
1556
  <a title="<?php esc_attr_e('edit', 'paid-memberships-pro' ); ?>" href="<?php echo esc_url( add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $order->discount_code->id ), admin_url('admin.php' ) ) ); ?>">
1557
  <?php echo esc_html( $order->discount_code->code ); ?>
1558
  </a>
1559
+ <?php } ?>
1560
  </td>
1561
  </tr>
1562
  <?php
1575
  </form>
1576
  <?php
1577
  // add normal args
1578
+ $pagination_url = esc_url( add_query_arg( $url_params, admin_url( '/admin.php?page=pmpro-orders' ) ) );
1579
  echo pmpro_getPaginationString( $pn, $totalrows, $limit, 1, $pagination_url, "&limit=$limit&pn=" );
1580
  ?>
1581
 
adminpages/pagesettings.php CHANGED
@@ -21,7 +21,22 @@ global $pmpro_pages;
21
  * @since 1.8.5
22
  */
23
  $extra_pages = apply_filters('pmpro_extra_page_settings', array());
24
- $post_types = apply_filters('pmpro_admin_pagesetting_post_type_array', array( 'page' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  //check nonce for saving settings
27
  if (!empty($_REQUEST['savesettings']) && (empty($_REQUEST['pmpro_pagesettings_nonce']) || !check_admin_referer('savesettings', 'pmpro_pagesettings_nonce'))) {
@@ -187,7 +202,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
187
  </th>
188
  <td>
189
  <?php
190
- wp_dropdown_pages(array("name" => "account_page_id", "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro' ) . " --", "selected" => $pmpro_pages['account']));
 
 
 
 
 
 
 
191
  ?>
192
  <?php if (!empty($pmpro_pages['account'])) { ?>
193
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['account']; ?>&action=edit"
@@ -204,7 +226,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
204
  </th>
205
  <td>
206
  <?php
207
- wp_dropdown_pages(array("name" => "billing_page_id", "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro' ) . " --", "selected" => $pmpro_pages['billing']));
 
 
 
 
 
 
 
208
  ?>
209
  <?php if (!empty($pmpro_pages['billing'])) { ?>
210
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['billing'] ?>&action=edit"
@@ -221,7 +250,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
221
  </th>
222
  <td>
223
  <?php
224
- wp_dropdown_pages(array("name" => "cancel_page_id", "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro') . " --", "selected" => $pmpro_pages['cancel'], "post_types" => $post_types ) );
 
 
 
 
 
 
 
225
  ?>
226
  <?php if (!empty($pmpro_pages['cancel'])) { ?>
227
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['cancel'] ?>&action=edit"
@@ -239,7 +275,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
239
  </th>
240
  <td>
241
  <?php
242
- wp_dropdown_pages(array("name" => "checkout_page_id", "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro') . " --", "selected" => $pmpro_pages['checkout'], "post_types" => $post_types ));
 
 
 
 
 
 
 
243
  ?>
244
  <?php if (!empty($pmpro_pages['checkout'])) { ?>
245
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['checkout'] ?>&action=edit"
@@ -257,7 +300,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
257
  </th>
258
  <td>
259
  <?php
260
- wp_dropdown_pages(array("name" => "confirmation_page_id", "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro') . " --", "selected" => $pmpro_pages['confirmation'], "post_types" => $post_types));
 
 
 
 
 
 
 
261
  ?>
262
  <?php if (!empty($pmpro_pages['confirmation'])) { ?>
263
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['confirmation'] ?>&action=edit"
@@ -275,7 +325,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
275
  </th>
276
  <td>
277
  <?php
278
- wp_dropdown_pages(array("name" => "invoice_page_id", "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro') . " --", "selected" => $pmpro_pages['invoice'], "post_types" => $post_types));
 
 
 
 
 
 
 
279
  ?>
280
  <?php if (!empty($pmpro_pages['invoice'])) { ?>
281
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['invoice'] ?>&action=edit"
@@ -293,7 +350,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
293
  </th>
294
  <td>
295
  <?php
296
- wp_dropdown_pages(array("name" => "levels_page_id", "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro') . " --", "selected" => $pmpro_pages['levels'], "post_types" => $post_types));
 
 
 
 
 
 
 
297
  ?>
298
  <?php if (!empty($pmpro_pages['levels'])) { ?>
299
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['levels'] ?>&action=edit"
@@ -326,7 +390,8 @@ require_once(dirname(__FILE__) . "/admin_header.php");
326
  array(
327
  'name' => 'login_page_id',
328
  'show_option_none' => '-- ' . __('Use WordPress Default', 'paid-memberships-pro') . ' --',
329
- 'selected' => $pmpro_pages['login'], 'post_types' => $post_types
 
330
  )
331
  );
332
  ?>
@@ -354,7 +419,8 @@ require_once(dirname(__FILE__) . "/admin_header.php");
354
  array(
355
  'name' => 'member_profile_edit_page_id',
356
  'show_option_none' => '-- ' . __('Use WordPress Default', 'paid-memberships-pro') . ' --',
357
- 'selected' => $pmpro_pages['member_profile_edit'], 'post_types' => $post_types
 
358
  )
359
  );
360
  ?>
@@ -409,11 +475,14 @@ require_once(dirname(__FILE__) . "/admin_header.php");
409
  <label for="<?php echo $name; ?>_page_id"><?php echo $label; ?></label>
410
  </th>
411
  <td>
412
- <?php wp_dropdown_pages(array(
413
- "name" => $name . '_page_id',
414
- "show_option_none" => "-- " . __('Choose One', 'paid-memberships-pro' ) . " --",
415
- "selected" => $pmpro_pages[$name],
416
- ));
 
 
 
417
  if(!empty($pmpro_pages[$name])) {
418
  ?>
419
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages[$name] ?>&action=edit"
21
  * @since 1.8.5
22
  */
23
  $extra_pages = apply_filters('pmpro_extra_page_settings', array());
24
+
25
+ /**
26
+ * @deprecated replaced with pmpro_admin_pagesetting_post_type since 2.7.0
27
+ */
28
+ $post_types = apply_filters( 'pmpro_admin_pagesetting_post_type_array', array( 'page' ) );
29
+
30
+ // For backward compatibility we extract the first element from the array
31
+ $post_type = is_array( $post_types ) ? $post_types[ array_key_first( $post_types ) ] : $post_types;
32
+
33
+ /**
34
+ * Set post type to use for PMPro pages in the page settings dropdown.
35
+ *
36
+ * @since 2.7.0
37
+ * @param string $post_type Accepts existing hierarchical post type
38
+ */
39
+ $post_type = apply_filters( 'pmpro_admin_pagesetting_post_type', $post_type );
40
 
41
  //check nonce for saving settings
42
  if (!empty($_REQUEST['savesettings']) && (empty($_REQUEST['pmpro_pagesettings_nonce']) || !check_admin_referer('savesettings', 'pmpro_pagesettings_nonce'))) {
202
  </th>
203
  <td>
204
  <?php
205
+ wp_dropdown_pages(
206
+ array(
207
+ 'name' => 'account_page_id',
208
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
209
+ 'selected' => $pmpro_pages['account'],
210
+ 'post_type' => $post_type,
211
+ )
212
+ );
213
  ?>
214
  <?php if (!empty($pmpro_pages['account'])) { ?>
215
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['account']; ?>&action=edit"
226
  </th>
227
  <td>
228
  <?php
229
+ wp_dropdown_pages(
230
+ array(
231
+ 'name' => 'billing_page_id',
232
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
233
+ 'selected' => $pmpro_pages['billing'],
234
+ 'post_type' => $post_type,
235
+ )
236
+ );
237
  ?>
238
  <?php if (!empty($pmpro_pages['billing'])) { ?>
239
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['billing'] ?>&action=edit"
250
  </th>
251
  <td>
252
  <?php
253
+ wp_dropdown_pages(
254
+ array(
255
+ 'name' => 'cancel_page_id',
256
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
257
+ 'selected' => $pmpro_pages['cancel'],
258
+ 'post_type' => $post_type,
259
+ )
260
+ );
261
  ?>
262
  <?php if (!empty($pmpro_pages['cancel'])) { ?>
263
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['cancel'] ?>&action=edit"
275
  </th>
276
  <td>
277
  <?php
278
+ wp_dropdown_pages(
279
+ array(
280
+ 'name' => 'checkout_page_id',
281
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
282
+ 'selected' => $pmpro_pages['checkout'],
283
+ 'post_type' => $post_type,
284
+ )
285
+ );
286
  ?>
287
  <?php if (!empty($pmpro_pages['checkout'])) { ?>
288
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['checkout'] ?>&action=edit"
300
  </th>
301
  <td>
302
  <?php
303
+ wp_dropdown_pages(
304
+ array(
305
+ 'name' => 'confirmation_page_id',
306
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
307
+ 'selected' => $pmpro_pages['confirmation'],
308
+ 'post_type' => $post_type,
309
+ )
310
+ );
311
  ?>
312
  <?php if (!empty($pmpro_pages['confirmation'])) { ?>
313
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['confirmation'] ?>&action=edit"
325
  </th>
326
  <td>
327
  <?php
328
+ wp_dropdown_pages(
329
+ array(
330
+ 'name' => 'invoice_page_id',
331
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
332
+ 'selected' => $pmpro_pages['invoice'],
333
+ 'post_type' => $post_type,
334
+ )
335
+ );
336
  ?>
337
  <?php if (!empty($pmpro_pages['invoice'])) { ?>
338
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['invoice'] ?>&action=edit"
350
  </th>
351
  <td>
352
  <?php
353
+ wp_dropdown_pages(
354
+ array(
355
+ 'name' => 'levels_page_id',
356
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
357
+ 'selected' => $pmpro_pages['levels'],
358
+ 'post_type' => $post_type,
359
+ )
360
+ );
361
  ?>
362
  <?php if (!empty($pmpro_pages['levels'])) { ?>
363
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['levels'] ?>&action=edit"
390
  array(
391
  'name' => 'login_page_id',
392
  'show_option_none' => '-- ' . __('Use WordPress Default', 'paid-memberships-pro') . ' --',
393
+ 'selected' => $pmpro_pages['login'],
394
+ 'post_type' => $post_type,
395
  )
396
  );
397
  ?>
419
  array(
420
  'name' => 'member_profile_edit_page_id',
421
  'show_option_none' => '-- ' . __('Use WordPress Default', 'paid-memberships-pro') . ' --',
422
+ 'selected' => $pmpro_pages['member_profile_edit'],
423
+ 'post_type' => $post_type,
424
  )
425
  );
426
  ?>
475
  <label for="<?php echo $name; ?>_page_id"><?php echo $label; ?></label>
476
  </th>
477
  <td>
478
+ <?php wp_dropdown_pages(
479
+ array(
480
+ 'name' => $name . '_page_id',
481
+ 'show_option_none' => '-- ' . __( 'Choose One', 'paid-memberships-pro' ) . ' --',
482
+ 'selected' => $pmpro_pages[ $name ],
483
+ 'post_type' => $post_type,
484
+ )
485
+ );
486
  if(!empty($pmpro_pages[$name])) {
487
  ?>
488
  <a target="_blank" href="post.php?post=<?php echo $pmpro_pages[$name] ?>&action=edit"
adminpages/paymentsettings.php CHANGED
@@ -112,7 +112,7 @@
112
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Payment Gateway', 'paid-memberships-pro' );?> &amp; <?php esc_html_e( 'SSL Settings', 'paid-memberships-pro' ); ?></h1>
113
  <hr class="wp-header-end">
114
 
115
- <p><?php _e('Learn more about <a title="Paid Memberships Pro - SSL Settings" target="_blank" href="https://www.paidmembershipspro.com/documentation/initial-plugin-setup/ssl/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=documentation&utm_content=ssl&utm_term=link1">SSL</a> or <a title="Paid Memberships Pro - Payment Gateway Settings" target="_blank" href="https://www.paidmembershipspro.com/documentation/initial-plugin-setup/step-3-payment-gateway-security/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=documentation&utm_content=step-3-payment-gateway-security">Payment Gateway Settings</a>.', 'paid-memberships-pro' ); ?></p>
116
 
117
  <table class="form-table">
118
  <tbody>
@@ -127,7 +127,7 @@
127
  <label for="gateway"><?php _e('Payment Gateway', 'paid-memberships-pro' );?>:</label>
128
  </th>
129
  <td>
130
- <select id="gateway" name="gateway" onchange="pmpro_changeGateway(jQuery(this).val());">
131
  <?php
132
  $pmpro_gateways = pmpro_gateways();
133
  foreach($pmpro_gateways as $pmpro_gateway_name => $pmpro_gateway_label)
@@ -148,16 +148,20 @@
148
  <label for="gateway_environment"><?php _e('Gateway Environment', 'paid-memberships-pro' );?>:</label>
149
  </th>
150
  <td>
151
- <select name="gateway_environment">
152
  <option value="sandbox" <?php selected( $gateway_environment, "sandbox" ); ?>><?php _e('Sandbox/Testing', 'paid-memberships-pro' );?></option>
153
  <option value="live" <?php selected( $gateway_environment, "live" ); ?>><?php _e('Live/Production', 'paid-memberships-pro' );?></option>
154
  </select>
155
  <script>
156
- function pmpro_changeGateway(gateway)
157
  {
 
 
 
158
  //hide all gateway options
159
  jQuery('tr.gateway').hide();
160
  jQuery('tr.gateway_'+gateway).show();
 
161
 
162
  //hide sub settings and toggle them on based on triggers
163
  jQuery('tr.pmpro_toggle_target').hide();
@@ -173,7 +177,10 @@
173
  jQuery('#pmpro-default-gateway-message').hide();
174
  }
175
  }
176
- pmpro_changeGateway(jQuery('#gateway').val());
 
 
 
177
  </script>
178
  </td>
179
  </tr>
112
  <h1 class="wp-heading-inline"><?php esc_html_e( 'Payment Gateway', 'paid-memberships-pro' );?> &amp; <?php esc_html_e( 'SSL Settings', 'paid-memberships-pro' ); ?></h1>
113
  <hr class="wp-header-end">
114
 
115
+ <p><?php _e('Learn more about <a title="Paid Memberships Pro - Payment Gateway Settings" target="_blank" href="https://www.paidmembershipspro.com/documentation/admin/payment-ssl-settings/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=documentation&utm_content=payment-gateway-settings">Payment Gateway Settings</a> and <a title="Paid Memberships Pro - SSL Settings" target="_blank" href="https://www.paidmembershipspro.com/documentation/initial-plugin-setup/ssl/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=documentation&utm_content=ssl&utm_term=link1">SSL</a>.', 'paid-memberships-pro' ); ?></p>
116
 
117
  <table class="form-table">
118
  <tbody>
127
  <label for="gateway"><?php _e('Payment Gateway', 'paid-memberships-pro' );?>:</label>
128
  </th>
129
  <td>
130
+ <select id="gateway" name="gateway">
131
  <?php
132
  $pmpro_gateways = pmpro_gateways();
133
  foreach($pmpro_gateways as $pmpro_gateway_name => $pmpro_gateway_label)
148
  <label for="gateway_environment"><?php _e('Gateway Environment', 'paid-memberships-pro' );?>:</label>
149
  </th>
150
  <td>
151
+ <select id="gateway_environment" name="gateway_environment">
152
  <option value="sandbox" <?php selected( $gateway_environment, "sandbox" ); ?>><?php _e('Sandbox/Testing', 'paid-memberships-pro' );?></option>
153
  <option value="live" <?php selected( $gateway_environment, "live" ); ?>><?php _e('Live/Production', 'paid-memberships-pro' );?></option>
154
  </select>
155
  <script>
156
+ function pmpro_changeGateway()
157
  {
158
+ const gateway = jQuery('#gateway').val();
159
+ const gateway_environment = jQuery('#gateway_environment').val();
160
+
161
  //hide all gateway options
162
  jQuery('tr.gateway').hide();
163
  jQuery('tr.gateway_'+gateway).show();
164
+ jQuery('tr.gateway_'+gateway+'_'+gateway_environment).show();
165
 
166
  //hide sub settings and toggle them on based on triggers
167
  jQuery('tr.pmpro_toggle_target').hide();
177
  jQuery('#pmpro-default-gateway-message').hide();
178
  }
179
  }
180
+ pmpro_changeGateway();
181
+
182
+ // Handle change events.
183
+ jQuery('#gateway, #gateway_environment').on('change', pmpro_changeGateway);
184
  </script>
185
  </td>
186
  </tr>
adminpages/reports/login.php CHANGED
@@ -105,6 +105,7 @@ function pmpro_report_login_page()
105
  <option value="all" <?php if($l == "all") { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'paid-memberships-pro')?></option>
106
  <?php
107
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
 
108
  foreach($levels as $level)
109
  {
110
  ?>
@@ -227,7 +228,8 @@ function pmpro_report_login_page()
227
  <?php echo $theuser->display_name;?>
228
  </td>
229
  <td><?php echo $auser->membership?></td>
230
- <td><?php echo date_i18n("m/d/Y", strtotime($theuser->user_registered, current_time("timestamp")))?></td>
 
231
  <td>
232
  <?php
233
  if($auser->enddate)
@@ -268,7 +270,7 @@ function pmpro_report_login_page()
268
  </form>
269
 
270
  <?php
271
- echo pmpro_getPaginationString($pn, $totalrows, $limit, 1, get_admin_url(NULL, "/admin.php?page=pmpro-reports&report=login&s=" . urlencode($s)), "&l=$l&limit=$limit&pn=");
272
  ?>
273
  <?php
274
  }
@@ -410,12 +412,15 @@ function pmpro_report_track_values($type, $user_id = NULL) {
410
  return false;
411
 
412
  //check for cookie for visits
413
- if($type == "visits" && !empty($_COOKIE['pmpro_visit']))
414
  return false;
 
415
 
416
  //set cookie for visits
417
- if($type == "visits" && empty($_COOKIE['pmpro_visit']))
418
- setcookie("pmpro_visit", "1", NULL, COOKIEPATH, COOKIE_DOMAIN, false);
 
 
419
 
420
  //some vars for below
421
  $now = current_time('timestamp');
105
  <option value="all" <?php if($l == "all") { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'paid-memberships-pro')?></option>
106
  <?php
107
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
108
+ $levels = pmpro_sort_levels_by_order( $levels );
109
  foreach($levels as $level)
110
  {
111
  ?>
228
  <?php echo $theuser->display_name;?>
229
  </td>
230
  <td><?php echo $auser->membership?></td>
231
+ <td><?php echo date_i18n( 'm/d/Y', strtotime( get_date_from_gmt( $theuser->user_registered ), current_time( 'timestamp' ) ) ); ?></td>
232
+
233
  <td>
234
  <?php
235
  if($auser->enddate)
270
  </form>
271
 
272
  <?php
273
+ echo pmpro_getPaginationString($pn, $totalrows, $limit, 1, admin_url( "/admin.php?page=pmpro-reports&report=login&s=" . urlencode($s)), "&l=$l&limit=$limit&pn=");
274
  ?>
275
  <?php
276
  }
412
  return false;
413
 
414
  //check for cookie for visits
415
+ if( $type === 'visits' && !empty( $_COOKIE['pmpro_visit'] ) ) {
416
  return false;
417
+ }
418
 
419
  //set cookie for visits
420
+ if( $type === 'visits' && empty( $_COOKIE['pmpro_visit'] ) ) {
421
+ // The secure parameter is set to is_ssl(), true if HTTPS.
422
+ setcookie( 'pmpro_visit', '1', null, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
423
+ }
424
 
425
  //some vars for below
426
  $now = current_time('timestamp');
adminpages/reports/memberships.php CHANGED
@@ -31,25 +31,7 @@ function pmpro_report_memberships_widget() {
31
  global $wpdb;
32
 
33
  //get levels to show stats on first 3
34
- $pmpro_levels = pmpro_getAllLevels(true, true);
35
-
36
- $pmpro_level_order = pmpro_getOption('level_order');
37
-
38
- if(!empty($pmpro_level_order))
39
- {
40
- $order = explode(',',$pmpro_level_order);
41
-
42
- //reorder array
43
- $reordered_levels = array();
44
- foreach($order as $level_id) {
45
- foreach($pmpro_levels as $key=>$level) {
46
- if($level_id == $level->id)
47
- $reordered_levels[$key] = $pmpro_levels[$key];
48
- }
49
- }
50
-
51
- $pmpro_levels = $reordered_levels;
52
- }
53
 
54
  $pmpro_levels = apply_filters( 'pmpro_report_levels', $pmpro_levels );
55
  ?>
@@ -375,6 +357,7 @@ function pmpro_report_memberships_page()
375
  <option value="free-levels" <?php if(isset($_REQUEST['level']) && $_REQUEST['level'] == "free-levels"){?> selected="selected" <?php }?>><?php _e( 'All Free Levels', 'paid-memberships-pro' ); ?></option>
376
  <?php
377
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
 
378
  foreach($levels as $level)
379
  {
380
  ?>
31
  global $wpdb;
32
 
33
  //get levels to show stats on first 3
34
+ $pmpro_levels = pmpro_sort_levels_by_order( pmpro_getAllLevels(true, true) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  $pmpro_levels = apply_filters( 'pmpro_report_levels', $pmpro_levels );
37
  ?>
357
  <option value="free-levels" <?php if(isset($_REQUEST['level']) && $_REQUEST['level'] == "free-levels"){?> selected="selected" <?php }?>><?php _e( 'All Free Levels', 'paid-memberships-pro' ); ?></option>
358
  <?php
359
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
360
+ $levels = pmpro_sort_levels_by_order( $levels );
361
  foreach($levels as $level)
362
  {
363
  ?>
adminpages/reports/sales.php CHANGED
@@ -69,7 +69,7 @@ function pmpro_report_sales_widget() {
69
  <?php } ?>
70
  </th>
71
  <td><?php echo esc_html( number_format_i18n( pmpro_getSales( $report_type ) ) ); ?></td>
72
- <td><?php echo esc_html(pmpro_formatPrice( pmpro_getRevenue( $report_type ) ) ); ?></td>
73
  </tr>
74
  <?php
75
  //sale prices stats
@@ -82,9 +82,9 @@ function pmpro_report_sales_widget() {
82
  }
83
  ?>
84
  <tr class="pmpro_report_tr_sub" style="display: none;">
85
- <th scope="row">- <?php echo esc_html( pmpro_formatPrice( $price ) );?></th>
86
  <td><?php echo esc_html( number_format_i18n( $quantity ) ); ?></td>
87
- <td><?php echo esc_html( pmpro_formatPrice( $price * $quantity ) ); ?></td>
88
  </tr>
89
  <?php
90
  }
@@ -152,7 +152,7 @@ function pmpro_report_sales_page()
152
  if($period == "daily")
153
  {
154
  $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
155
- $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-31';
156
  $date_function = 'DAY';
157
  $currently_in_period = ( intval( date( 'Y' ) ) == $year && intval( date( 'n' ) ) == $month );
158
  }
@@ -173,17 +173,22 @@ function pmpro_report_sales_page()
173
  //testing or live data
174
  $gateway_environment = pmpro_getOption("gateway_environment");
175
 
 
 
 
 
 
176
  //get data
177
- $sqlQuery = "SELECT $date_function(o.timestamp) as date, $type_function(o.total) as value FROM $wpdb->pmpro_membership_orders o ";
178
 
179
  if ( ! empty( $discount_code ) ) {
180
  $sqlQuery .= "LEFT JOIN $wpdb->pmpro_discount_codes_uses dc ON o.id = dc.order_id ";
181
  }
182
 
183
- $sqlQuery .= "WHERE o.total > 0 AND o.timestamp >= '" . esc_sql( $startdate ) . "' AND o.status NOT IN('refunded', 'review', 'token', 'error') AND o.gateway_environment = '" . esc_sql( $gateway_environment ) . "' ";
184
 
185
  if(!empty($enddate))
186
- $sqlQuery .= "AND o.timestamp <= '" . esc_sql( $enddate ) . "' ";
187
 
188
  if(!empty($l))
189
  $sqlQuery .= "AND o.membership_id IN(" . esc_sql( $l ) . ") ";
@@ -311,6 +316,7 @@ function pmpro_report_sales_page()
311
  <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'paid-memberships-pro' );?></option>
312
  <?php
313
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
 
314
  foreach($levels as $level)
315
  {
316
  ?>
@@ -473,10 +479,13 @@ function pmpro_getSales($period, $levels = NULL)
473
  elseif($period == "this year")
474
  $startdate = date_i18n("Y", current_time('timestamp')) . "-01-01";
475
  else
476
- $startdate = "";
477
 
478
  $gateway_environment = pmpro_getOption("gateway_environment");
479
 
 
 
 
480
  //build query
481
  global $wpdb;
482
  $sqlQuery = "SELECT COUNT(*) FROM $wpdb->pmpro_membership_orders WHERE total > 0 AND status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . esc_sql( $startdate ) . "' AND gateway_environment = '" . esc_sql( $gateway_environment ) . "' ";
@@ -523,6 +532,9 @@ function pmpro_get_prices_paid( $period, $count = NULL ) {
523
  $startdate = '1970-01-01';
524
  }
525
 
 
 
 
526
  $gateway_environment = pmpro_getOption( 'gateway_environment' );
527
 
528
  // Build query.
@@ -581,7 +593,10 @@ function pmpro_getRevenue($period, $levels = NULL)
581
  elseif($period == "this year")
582
  $startdate = date_i18n("Y", current_time('timestamp')) . "-01-01";
583
  else
584
- $startdate = "";
 
 
 
585
 
586
  $gateway_environment = pmpro_getOption("gateway_environment");
587
 
69
  <?php } ?>
70
  </th>
71
  <td><?php echo esc_html( number_format_i18n( pmpro_getSales( $report_type ) ) ); ?></td>
72
+ <td><?php echo pmpro_escape_price( pmpro_formatPrice( pmpro_getRevenue( $report_type ) ) ); ?></td>
73
  </tr>
74
  <?php
75
  //sale prices stats
82
  }
83
  ?>
84
  <tr class="pmpro_report_tr_sub" style="display: none;">
85
+ <th scope="row">- <?php echo pmpro_escape_price( pmpro_formatPrice( $price ) );?></th>
86
  <td><?php echo esc_html( number_format_i18n( $quantity ) ); ?></td>
87
+ <td><?php echo pmpro_escape_price( pmpro_formatPrice( $price * $quantity ) ); ?></td>
88
  </tr>
89
  <?php
90
  }
152
  if($period == "daily")
153
  {
154
  $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
155
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-' . date_i18n('t', strtotime( $startdate ) );
156
  $date_function = 'DAY';
157
  $currently_in_period = ( intval( date( 'Y' ) ) == $year && intval( date( 'n' ) ) == $month );
158
  }
173
  //testing or live data
174
  $gateway_environment = pmpro_getOption("gateway_environment");
175
 
176
+ // Get the estimated second offset to convert from GMT time to local.This is not perfect as daylight
177
+ // savings time can come and go in the middle of a month, but it's a tradeoff that we are making
178
+ // for performance so that we don't need to go through each order manually to calculate the local time.
179
+ $tz_offset = strtotime( $startdate ) - strtotime( get_gmt_from_date( $startdate . " 00:00:00" ) );
180
+
181
  //get data
182
+ $sqlQuery = "SELECT $date_function( DATE_ADD( o.timestamp, INTERVAL $tz_offset SECOND ) ) as date, $type_function(o.total) as value FROM $wpdb->pmpro_membership_orders o ";
183
 
184
  if ( ! empty( $discount_code ) ) {
185
  $sqlQuery .= "LEFT JOIN $wpdb->pmpro_discount_codes_uses dc ON o.id = dc.order_id ";
186
  }
187
 
188
+ $sqlQuery .= "WHERE o.total > 0 AND o.timestamp >= DATE_ADD( '$startdate' , INTERVAL - $tz_offset SECOND ) AND o.status NOT IN('refunded', 'review', 'token', 'error') AND o.gateway_environment = '" . esc_sql( $gateway_environment ) . "' ";
189
 
190
  if(!empty($enddate))
191
+ $sqlQuery .= "AND o.timestamp <= DATE_ADD( '$enddate 23:59:59' , INTERVAL - $tz_offset SECOND )";
192
 
193
  if(!empty($l))
194
  $sqlQuery .= "AND o.membership_id IN(" . esc_sql( $l ) . ") ";
316
  <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'paid-memberships-pro' );?></option>
317
  <?php
318
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
319
+ $levels = pmpro_sort_levels_by_order( $levels );
320
  foreach($levels as $level)
321
  {
322
  ?>
479
  elseif($period == "this year")
480
  $startdate = date_i18n("Y", current_time('timestamp')) . "-01-01";
481
  else
482
+ $startdate = date_i18n("Y-m-d", 0);
483
 
484
  $gateway_environment = pmpro_getOption("gateway_environment");
485
 
486
+ // Convert from local to UTC.
487
+ $startdate = get_gmt_from_date( $startdate );
488
+
489
  //build query
490
  global $wpdb;
491
  $sqlQuery = "SELECT COUNT(*) FROM $wpdb->pmpro_membership_orders WHERE total > 0 AND status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . esc_sql( $startdate ) . "' AND gateway_environment = '" . esc_sql( $gateway_environment ) . "' ";
532
  $startdate = '1970-01-01';
533
  }
534
 
535
+ // Convert from local to UTC.
536
+ $startdate = get_gmt_from_date( $startdate );
537
+
538
  $gateway_environment = pmpro_getOption( 'gateway_environment' );
539
 
540
  // Build query.
593
  elseif($period == "this year")
594
  $startdate = date_i18n("Y", current_time('timestamp')) . "-01-01";
595
  else
596
+ $startdate = date_i18n("Y-m-d", 0);
597
+
598
+ // Convert from local to UTC.
599
+ $startdate = get_gmt_from_date( $startdate );
600
 
601
  $gateway_environment = pmpro_getOption("gateway_environment");
602
 
adminpages/templates/orders-email.php CHANGED
@@ -15,12 +15,12 @@
15
  </tr>
16
  <tr>
17
  <td>
18
- <?php echo __( 'Date:', 'paid-memberships-pro' ) . '&nbsp;' . date_i18n( 'Y-m-d', $order->getTimestamp() ) ?>
19
  </td>
20
  </tr>
21
  <?php if(!empty($order->billing->name)): ?>
22
  <tr>
23
- <td>
24
  <strong><?php _e( 'Bill to:', 'paid-memberships-pro' ); ?></strong><br>
25
  <?php
26
  echo pmpro_formatAddress(
@@ -41,29 +41,33 @@
41
  <tbody>
42
  <tr>
43
  <td colspan="2">
44
- <table style="width:100%;border-width:1px;border-style:solid;border-collapse:collapse;">
45
  <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
46
- <th style="text-align:center;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('ID', 'paid-memberships-pro' ); ?></th>
47
- <th style="border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Item', 'paid-memberships-pro' ); ?></th>
48
- <th style="border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Price', 'paid-memberships-pro' ); ?></th>
49
  </tr>
50
  <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
51
- <td style="text-align:center;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $level->id; ?></td>
52
- <td style="border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $level->name; ?></td>
53
- <td style="text-align:right;"><?php echo $order->subtotal; ?></td>
54
- </tr>
55
- <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
56
- <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Subtotal', 'paid-memberships-pro' ); ?></th>
57
- <td style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $order->subtotal; ?></td>
58
- </tr>
59
- <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
60
- <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Tax', 'paid-memberships-pro' ); ?></th>
61
- <td style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo $order->tax; ?></td>
62
- </tr>
63
- <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
64
- <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php _e('Total', 'paid-memberships-pro' ); ?></th>
65
- <th style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;"><?php echo pmpro_formatPrice($order->total); ?></th>
66
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  </table>
68
  </td>
69
  </tr>
15
  </tr>
16
  <tr>
17
  <td>
18
+ <?php echo __( 'Date:', 'paid-memberships-pro' ) . '&nbsp;' . date_i18n( get_option( 'date_format' ), $order->getTimestamp() ); ?>
19
  </td>
20
  </tr>
21
  <?php if(!empty($order->billing->name)): ?>
22
  <tr>
23
+ <td style="padding-bottom:10px;">
24
  <strong><?php _e( 'Bill to:', 'paid-memberships-pro' ); ?></strong><br>
25
  <?php
26
  echo pmpro_formatAddress(
41
  <tbody>
42
  <tr>
43
  <td colspan="2">
44
+ <table style="width:100%;border-width:0px;border-collapse:collapse;">
45
  <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
46
+ <th style="text-align:center;border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;"><?php _e('ID', 'paid-memberships-pro' ); ?></th>
47
+ <th style="border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;"><?php _e('Item', 'paid-memberships-pro' ); ?></th>
48
+ <th style="border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;"><?php _e('Price', 'paid-memberships-pro' ); ?></th>
49
  </tr>
50
  <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
51
+ <td style="text-align:center;border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;"><?php echo $level->id; ?></td>
52
+ <td style="border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;"><?php echo $level->name; ?></td>
53
+ <td style="border-width:1px;border-style:solid;border-collapse:collapse;text-align:right;padding:4px;"><?php echo pmpro_escape_price( pmpro_formatPrice( $order->subtotal ) ); ?></td>
 
 
 
 
 
 
 
 
 
 
 
 
54
  </tr>
55
+ <?php
56
+ if ( (float)$order->total > 0 ) {
57
+ $pmpro_price_parts = pmpro_get_price_parts( $order, 'array' );
58
+ foreach ( $pmpro_price_parts as $pmpro_price_part ) { ?>
59
+ <tr style="border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;">
60
+ <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;">
61
+ <?php esc_html_e( $pmpro_price_part['label'] ); ?>
62
+ </th>
63
+ <td style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;padding:4px;">
64
+ <?php esc_html_e( $pmpro_price_part['value'] ); ?>
65
+ </td>
66
+ </tr>
67
+ <?php
68
+ }
69
+ }
70
+ ?>
71
  </table>
72
  </td>
73
  </tr>
adminpages/templates/orders-print.php CHANGED
@@ -70,7 +70,7 @@
70
  $order->billing->phone
71
  ); ?>
72
  </p>
73
- <table class="invoice">
74
  <tr>
75
  <th><?php _e('ID', 'paid-memberships-pro' ); ?></th>
76
  <th><?php _e('Item', 'paid-memberships-pro' ); ?></th>
@@ -79,20 +79,24 @@
79
  <tr>
80
  <td class="aligncenter"><?php echo $level->id; ?></td>
81
  <td><?php echo $level->name; ?></td>
82
- <td class="alignright"><?php echo $order->subtotal; ?></td>
83
- </tr>
84
- <tr>
85
- <th colspan="2" class="alignright"><?php _e('Subtotal', 'paid-memberships-pro' ); ?></th>
86
- <td class="alignright"><?php echo $order->subtotal; ?></td>
87
- </tr>
88
- <tr>
89
- <th colspan="2" class="alignright"><?php _e('Tax', 'paid-memberships-pro' ); ?></th>
90
- <td class="alignright"><?php echo $order->tax; ?></td>
91
- </tr>
92
- <tr>
93
- <th colspan="2" class="alignright"><?php _e('Total', 'paid-memberships-pro' ); ?></th>
94
- <th class="alignright"><?php echo pmpro_formatPrice( $order->total ); ?></th>
95
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  </table>
97
  </main>
98
  </body>
70
  $order->billing->phone
71
  ); ?>
72
  </p>
73
+ <table class="invoice" style="border-width:0px;border-collapse:collapse;">
74
  <tr>
75
  <th><?php _e('ID', 'paid-memberships-pro' ); ?></th>
76
  <th><?php _e('Item', 'paid-memberships-pro' ); ?></th>
79
  <tr>
80
  <td class="aligncenter"><?php echo $level->id; ?></td>
81
  <td><?php echo $level->name; ?></td>
82
+ <td class="alignright"><?php echo pmpro_escape_price( pmpro_formatPrice( $order->subtotal ) ); ?></td>
 
 
 
 
 
 
 
 
 
 
 
 
83
  </tr>
84
+ <?php
85
+ if ( (float)$order->total > 0 ) {
86
+ $pmpro_price_parts = pmpro_get_price_parts( $order, 'array' );
87
+ foreach ( $pmpro_price_parts as $pmpro_price_part ) { ?>
88
+ <tr style="border-width:1px;border-style:solid;border-collapse:collapse;">
89
+ <th colspan="2" style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;">
90
+ <?php esc_html_e( $pmpro_price_part['label'] ); ?>
91
+ </th>
92
+ <td style="text-align:right;border-width:1px;border-style:solid;border-collapse:collapse;">
93
+ <?php esc_html_e( $pmpro_price_part['value'] ); ?>
94
+ </td>
95
+ </tr>
96
+ <?php
97
+ }
98
+ }
99
+ ?>
100
  </table>
101
  </main>
102
  </body>
blocks/blocks.php CHANGED
@@ -27,8 +27,12 @@ require_once( 'login/block.php' );
27
 
28
  /**
29
  * Add PMPro block category
 
 
 
 
30
  */
31
- function pmpro_place_blocks_in_panel( $categories, $post ) {
32
  return array_merge(
33
  $categories,
34
  array(
@@ -39,7 +43,15 @@ function pmpro_place_blocks_in_panel( $categories, $post ) {
39
  )
40
  );
41
  }
42
- add_filter( 'block_categories', 'pmpro_place_blocks_in_panel', 10, 2 );
 
 
 
 
 
 
 
 
43
 
44
  /**
45
  * Enqueue block editor only JavaScript and CSS
@@ -49,7 +61,15 @@ function pmpro_block_editor_scripts() {
49
  wp_enqueue_script(
50
  'pmpro-blocks-editor-js',
51
  plugins_url( 'js/blocks.build.js', PMPRO_BASE_FILE ),
52
- array('wp-i18n', 'wp-element', 'wp-blocks', 'wp-components', 'wp-api', 'wp-editor', 'pmpro_admin'),
 
 
 
 
 
 
 
 
53
  PMPRO_VERSION
54
  );
55
 
27
 
28
  /**
29
  * Add PMPro block category
30
+ * This callback is used with the block_categories (pre 5.8)
31
+ * and block_categories_all (5.8+) filters. In the first filter,
32
+ * the second parameter is a $post, in the latter it's a $context.
33
+ * We don't use the second parameter yet though.
34
  */
35
+ function pmpro_place_blocks_in_panel( $categories, $post_or_context ) {
36
  return array_merge(
37
  $categories,
38
  array(
43
  )
44
  );
45
  }
46
+
47
+ // Use the correct filter based on WP version.
48
+ if ( function_exists( 'get_default_block_categories' ) ) {
49
+ // 5.8+, context is 2nd parameter.
50
+ add_filter( 'block_categories_all', 'pmpro_place_blocks_in_panel', 10, 2 );
51
+ } else {
52
+ // Pre-5.8, post is 2nd parameter.
53
+ add_filter( 'block_categories', 'pmpro_place_blocks_in_panel', 10, 2 );
54
+ }
55
 
56
  /**
57
  * Enqueue block editor only JavaScript and CSS
61
  wp_enqueue_script(
62
  'pmpro-blocks-editor-js',
63
  plugins_url( 'js/blocks.build.js', PMPRO_BASE_FILE ),
64
+ [
65
+ 'wp-i18n',
66
+ 'wp-element',
67
+ 'wp-blocks',
68
+ 'wp-components',
69
+ 'wp-api',
70
+ 'wp-block-editor',
71
+ 'pmpro_admin',
72
+ ],
73
  PMPRO_VERSION
74
  );
75
 
blocks/membership/block.js CHANGED
@@ -13,9 +13,8 @@
13
  } = wp.blocks;
14
  const {
15
  PanelBody,
16
- SelectControl,
17
  } = wp.components;
18
-
19
  const {
20
  InspectorControls,
21
  InnerBlocks,
@@ -49,33 +48,47 @@ const all_levels = [{ value: 0, label: "Non-Members" }].concat( pmpro.all_level_
49
  },
50
  },
51
  edit: props => {
52
- const { attributes: {levels, uid}, setAttributes, isSelected } = props;
53
  if( uid=='' ) {
54
  var rand = Math.random()+"";
55
  setAttributes( { uid:rand } );
56
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  return [
58
  isSelected && <InspectorControls>
59
- <PanelBody>
60
- <SelectControl
61
- multiple
62
- label={ __( 'Select levels to show content to:', 'paid-memberships-pro' ) }
63
- value={ levels }
64
- onChange={ levels => { setAttributes( { levels } ) } }
65
- options={ all_levels }
66
- />
67
  </PanelBody>
68
  </InspectorControls>,
69
  isSelected && <div className="pmpro-block-require-membership-element" >
70
  <span className="pmpro-block-title">{ __( 'Require Membership', 'paid-memberships-pro' ) }</span>
71
- <PanelBody>
72
- <SelectControl
73
- multiple
74
- label={ __( 'Select levels to show content to:', 'paid-memberships-pro' ) }
75
- value={ levels }
76
- onChange={ levels => { setAttributes( { levels } ) } }
77
- options={ all_levels }
78
- />
79
  </PanelBody>
80
  <InnerBlocks
81
  renderAppender={ () => (
13
  } = wp.blocks;
14
  const {
15
  PanelBody,
16
+ CheckboxControl,
17
  } = wp.components;
 
18
  const {
19
  InspectorControls,
20
  InnerBlocks,
48
  },
49
  },
50
  edit: props => {
51
+ const { attributes: {levels, uid}, setAttributes, isSelected } = props;
52
  if( uid=='' ) {
53
  var rand = Math.random()+"";
54
  setAttributes( { uid:rand } );
55
  }
56
+
57
+ // Build an array of checkboxes for each level.
58
+ var checkboxes = all_levels.map( function(level) {
59
+ function setLevelsAttribute( nowChecked ) {
60
+ if ( nowChecked && ! ( levels.some( levelID => levelID == level.value ) ) ) {
61
+ // Add the level.
62
+ const newLevels = levels.slice();
63
+ newLevels.push( level.value + '' );
64
+ setAttributes( { levels:newLevels } );
65
+ } else if ( ! nowChecked && levels.some( levelID => levelID == level.value ) ) {
66
+ // Remove the level.
67
+ const newLevels = levels.filter(( levelID ) => levelID != level.value);
68
+ setAttributes( { levels:newLevels } );
69
+ }
70
+ }
71
+ return [
72
+ <CheckboxControl
73
+ label = { level.label }
74
+ checked = { levels.some( levelID => levelID == level.value ) }
75
+ onChange = { setLevelsAttribute }
76
+ />
77
+ ]
78
+ });
79
+
80
  return [
81
  isSelected && <InspectorControls>
82
+ <PanelBody>
83
+ <div class="pmpro-block-inspector-scrollable">
84
+ {checkboxes}
85
+ </div>
 
 
 
 
86
  </PanelBody>
87
  </InspectorControls>,
88
  isSelected && <div className="pmpro-block-require-membership-element" >
89
  <span className="pmpro-block-title">{ __( 'Require Membership', 'paid-memberships-pro' ) }</span>
90
+ <PanelBody>
91
+ {checkboxes}
 
 
 
 
 
 
92
  </PanelBody>
93
  <InnerBlocks
94
  renderAppender={ () => (
classes/class-deny-network-activation.php CHANGED
@@ -12,15 +12,27 @@ class PMPro_Deny_Network_Activation {
12
 
13
  public function wp_admin_style() {
14
  global $current_screen;
15
- if ( is_admin() && ( 'sites-network' === $current_screen->id || 'plugins-network' === $current_screen->id ) ) {
16
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  <style type="text/css">
18
  .notice.notice-info {
19
  background-color: #ffd;
20
  }
21
  </style>
22
- <?php
23
- }
24
  }
25
 
26
  public function display_message_after_network_activation_attempt() {
12
 
13
  public function wp_admin_style() {
14
  global $current_screen;
15
+ // Bail if not in the dashboard.
16
+ if ( ! is_admin() ) {
17
+ return;
18
+ }
19
+
20
+ // Bail if there is no current screen.
21
+ if ( empty( $current_screen ) ) {
22
+ return;
23
+ }
24
+
25
+ // Bail if not on the screens we want.
26
+ if ( 'sites-network' !== $current_screen->id && 'plugins-network' !== $current_screen->id ) {
27
+ return;
28
+ }
29
+ ?>
30
  <style type="text/css">
31
  .notice.notice-info {
32
  background-color: #ffd;
33
  }
34
  </style>
35
+ <?php
 
36
  }
37
 
38
  public function display_message_after_network_activation_attempt() {
classes/class-pmpro-admin-activity-email.php CHANGED
@@ -79,8 +79,10 @@ class PMPro_Admin_Activity_Email extends PMProEmail {
79
  ?>
80
  <tr>
81
  <td valign="top" style="background:#FFFFFF;font-family:Helvetica,Arial,sans-serif;font-size:16px;line-height:25px;color:#222222;padding:30px;text-align:center;">
82
- <h2 style="color:#2997c8;font-size:30px;margin:0px 0px 20px 0px;padding:0px;"><?php get_bloginfo( 'name' ); ?></h2>
83
- <p style="font-size:20px;line-height:30px;margin:0px;padding:0px;"><?php printf( __( "Here's a summary of what happened in your Paid Memberships Pro site %s.", 'paid-memberships-pro' ), esc_html( $term ) ); ?></p>
 
 
84
  </td>
85
  </tr>
86
  <tr>
@@ -99,7 +101,7 @@ class PMPro_Admin_Activity_Email extends PMProEmail {
99
  if ( $revenue > 0 ) {
100
  ?>
101
  <h3 style="color:#2997c8;font-size:20px;line-height:30px;margin:0px 0px 15px 0px;padding:0px;"><?php esc_html_e( 'Sales and Revenue', 'paid-memberships-pro' ); ?></h3>
102
- <p style="margin:0px 0px 15px 0px;padding:0px;"><?php printf( __( 'Your membership site made <strong>%1$s</strong> in revenue %2$s.', 'paid-memberships-pro' ), esc_html( pmpro_formatPrice( $revenue ) ), esc_html( $term ) ); ?></p>
103
  <?php } else { ?>
104
  <h3 style="color:#2997c8;font-size:20px;line-height:30px;margin:0px 0px 15px 0px;padding:0px;"><?php esc_html_e( 'Signups and Cancellations', 'paid-memberships-pro' ); ?></h3>
105
  <?php } ?>
@@ -240,7 +242,7 @@ class PMPro_Admin_Activity_Email extends PMProEmail {
240
  <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border:0;background-color:#F1F1F1;text-align:center;font-family:Helvetica,Arial,sans-serif;font-size:16px;line-height:25px;color:#222222;">
241
  <tr>
242
  <?php
243
- // Get addon statistics.
244
  $free_addons = 0;
245
  $plus_addons = 0;
246
  $update_addons = 0;
@@ -354,7 +356,7 @@ class PMPro_Admin_Activity_Email extends PMProEmail {
354
  <p style="margin:0px 0px 15px 0px;padding:0px;"><a style="color:#2997c8;" href="https://www.paidmembershipspro.com/support/" target="_blank"><?php esc_html_e( 'Get Support', 'paid-memberships-pro' ); ?></a></p>
355
  <p style="margin:0px 0px 15px 0px;padding:0px;"><a style="color:#2997c8;" href="https://twitter.com/pmproplugin" target="_blank"><?php esc_html_e( 'Follow @pmproplugin on Twitter', 'paid-memberships-pro' ); ?></a></p>
356
  <p style="margin:0px 0px 15px 0px;padding:0px;"><a style="color:#2997c8;" href="https://www.facebook.com/PaidMembershipsPro/" target="_blank"><?php esc_html_e( 'Like us on Facebook', 'paid-memberships-pro' ); ?></p></p>
357
- <p style="margin:0px;padding:0px;"><a style="color:#2997c8;" href="https://www.youtube.com/user/strangerstudiostv" target="_blank"><?php esc_html_e( 'Subscribe to our YouTube Channel', 'paid-memberships-pro' ); ?></a></p>
358
  </td>
359
  </tr>
360
  </table>
79
  ?>
80
  <tr>
81
  <td valign="top" style="background:#FFFFFF;font-family:Helvetica,Arial,sans-serif;font-size:16px;line-height:25px;color:#222222;padding:30px;text-align:center;">
82
+ <p style="font-size:20px;line-height:30px;margin:0px;padding:0px;">
83
+ <a href="<?php echo site_url(); ?>" target="_blank" style="color:#2997c8;font-weight:bold;">[<?php echo get_bloginfo( 'name' ); ?>]</a><br />
84
+ <?php printf( __( "Here's a summary of what happened in your Paid Memberships Pro site %s.", 'paid-memberships-pro' ), esc_html( $term ) ); ?>
85
+ </p>
86
  </td>
87
  </tr>
88
  <tr>
101
  if ( $revenue > 0 ) {
102
  ?>
103
  <h3 style="color:#2997c8;font-size:20px;line-height:30px;margin:0px 0px 15px 0px;padding:0px;"><?php esc_html_e( 'Sales and Revenue', 'paid-memberships-pro' ); ?></h3>
104
+ <p style="margin:0px 0px 15px 0px;padding:0px;"><?php printf( __( 'Your membership site made <strong>%1$s</strong> in revenue %2$s.', 'paid-memberships-pro' ), pmpro_escape_price( pmpro_formatPrice( $revenue ) ), esc_html( $term ) ); ?></p>
105
  <?php } else { ?>
106
  <h3 style="color:#2997c8;font-size:20px;line-height:30px;margin:0px 0px 15px 0px;padding:0px;"><?php esc_html_e( 'Signups and Cancellations', 'paid-memberships-pro' ); ?></h3>
107
  <?php } ?>
242
  <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border:0;background-color:#F1F1F1;text-align:center;font-family:Helvetica,Arial,sans-serif;font-size:16px;line-height:25px;color:#222222;">
243
  <tr>
244
  <?php
245
+ // Get Add On statistics.
246
  $free_addons = 0;
247
  $plus_addons = 0;
248
  $update_addons = 0;
356
  <p style="margin:0px 0px 15px 0px;padding:0px;"><a style="color:#2997c8;" href="https://www.paidmembershipspro.com/support/" target="_blank"><?php esc_html_e( 'Get Support', 'paid-memberships-pro' ); ?></a></p>
357
  <p style="margin:0px 0px 15px 0px;padding:0px;"><a style="color:#2997c8;" href="https://twitter.com/pmproplugin" target="_blank"><?php esc_html_e( 'Follow @pmproplugin on Twitter', 'paid-memberships-pro' ); ?></a></p>
358
  <p style="margin:0px 0px 15px 0px;padding:0px;"><a style="color:#2997c8;" href="https://www.facebook.com/PaidMembershipsPro/" target="_blank"><?php esc_html_e( 'Like us on Facebook', 'paid-memberships-pro' ); ?></p></p>
359
+ <p style="margin:0px;padding:0px;"><a style="color:#2997c8;" href="https://www.youtube.com/user/strangerstudiostv?sub_confirmation=1" target="_blank"><?php esc_html_e( 'Subscribe to our YouTube Channel', 'paid-memberships-pro' ); ?></a></p>
360
  </td>
361
  </tr>
362
  </table>
classes/class-pmpro-members-list-table.php CHANGED
@@ -72,9 +72,8 @@ class PMPro_Members_List_Table extends WP_List_Table {
72
  */
73
  public function get_columns() {
74
  $columns = array(
75
- // 'cb' => '<input type="checkbox" />',
76
- 'ID' => 'ID',
77
  'username' => 'Username',
 
78
  'first_name' => 'First Name',
79
  'last_name' => 'Last Name',
80
  'display_name' => 'Display Name',
@@ -119,7 +118,7 @@ class PMPro_Members_List_Table extends WP_List_Table {
119
 
120
  // Shortcut for editing columns in default memberslist location.
121
  $current_screen = get_current_screen();
122
- if ( ! empty( $current_screen ) && 'memberships_page_pmpro-memberslist' === $current_screen->id ) {
123
  $columns = apply_filters( 'pmpro_manage_memberslist_columns', $columns );
124
  }
125
 
@@ -242,7 +241,7 @@ class PMPro_Members_List_Table extends WP_List_Table {
242
  /**
243
  * Get the table data
244
  *
245
- * @return Array or integer if $count parameter = true
246
  */
247
  private function sql_table_data( $count = false ) {
248
  global $wpdb;
@@ -543,7 +542,7 @@ class PMPro_Members_List_Table extends WP_List_Table {
543
  } else {
544
  // Display the member's initial payment.
545
  if ( (float)$item['initial_payment'] > 0 ) {
546
- $fee .= pmpro_formatPrice( $item['initial_payment'] );
547
  }
548
  // If there is a recurring payment, show a plus sign.
549
  if ( (float)$item['initial_payment'] > 0 && (float)$item['billing_amount'] > 0 ) {
@@ -551,7 +550,7 @@ class PMPro_Members_List_Table extends WP_List_Table {
551
  }
552
  // If there is a recurring payment, show the recurring payment amount and cycle.
553
  if ( (float)$item['billing_amount'] > 0 ) {
554
- $fee .= pmpro_formatPrice( $item['billing_amount'] );
555
  $fee .= esc_html( ' per ', 'paid-memberships-pro' );
556
  if ( $item['cycle_number'] > 1 ) {
557
  $fee .= $item['cycle_number'] . " " . $item['cycle_period'] . "s";
@@ -574,7 +573,7 @@ class PMPro_Members_List_Table extends WP_List_Table {
574
  if ( empty( $joindate ) ) {
575
  return;
576
  }
577
- return date_i18n( get_option('date_format'), $joindate );
578
  }
579
 
580
  /**
@@ -621,7 +620,7 @@ class PMPro_Members_List_Table extends WP_List_Table {
621
  $l = false;
622
  }
623
  _e('Show', 'paid-memberships-pro' );?>
624
- <select name="l" onchange="jQuery('#member-list-form').submit();">
625
  <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'paid-memberships-pro' );?></option>
626
  <?php
627
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
72
  */
73
  public function get_columns() {
74
  $columns = array(
 
 
75
  'username' => 'Username',
76
+ 'ID' => 'ID',
77
  'first_name' => 'First Name',
78
  'last_name' => 'Last Name',
79
  'display_name' => 'Display Name',
118
 
119
  // Shortcut for editing columns in default memberslist location.
120
  $current_screen = get_current_screen();
121
+ if ( ! empty( $current_screen ) && strpos( $current_screen->id, "pmpro-memberslist" ) !== false ) {
122
  $columns = apply_filters( 'pmpro_manage_memberslist_columns', $columns );
123
  }
124
 
241
  /**
242
  * Get the table data
243
  *
244
+ * @return Array|integer if $count parameter = true
245
  */
246
  private function sql_table_data( $count = false ) {
247
  global $wpdb;
542
  } else {
543
  // Display the member's initial payment.
544
  if ( (float)$item['initial_payment'] > 0 ) {
545
+ $fee .= pmpro_escape_price( pmpro_formatPrice( $item['initial_payment'] ) );
546
  }
547
  // If there is a recurring payment, show a plus sign.
548
  if ( (float)$item['initial_payment'] > 0 && (float)$item['billing_amount'] > 0 ) {
550
  }
551
  // If there is a recurring payment, show the recurring payment amount and cycle.
552
  if ( (float)$item['billing_amount'] > 0 ) {
553
+ $fee .= pmpro_escape_price( pmpro_formatPrice( $item['billing_amount'] ) );
554
  $fee .= esc_html( ' per ', 'paid-memberships-pro' );
555
  if ( $item['cycle_number'] > 1 ) {
556
  $fee .= $item['cycle_number'] . " " . $item['cycle_period'] . "s";
573
  if ( empty( $joindate ) ) {
574
  return;
575
  }
576
+ return date_i18n( get_option( 'date_format' ), strtotime( get_date_from_gmt( date( 'Y-m-d H:i:s', $joindate ) ) ) );
577
  }
578
 
579
  /**
620
  $l = false;
621
  }
622
  _e('Show', 'paid-memberships-pro' );?>
623
+ <select name="l" onchange="jQuery('#current-page-selector').val('1'); jQuery('#member-list-form').submit();">
624
  <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'paid-memberships-pro' );?></option>
625
  <?php
626
  $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
classes/class-pmpro-site-health.php ADDED
@@ -0,0 +1,548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The functionality that includes PMPro data within Site Health information.
5
+ *
6
+ * @since 2.6.2
7
+ */
8
+ class PMPro_Site_Health {
9
+
10
+ /**
11
+ * The current object instance.
12
+ *
13
+ * @var self
14
+ */
15
+ private static $instance;
16
+
17
+ /**
18
+ * Initialize class object and use it for future init calls.
19
+ *
20
+ * @since 2.6.2
21
+ *
22
+ * @return self The class object.
23
+ */
24
+ public static function init() {
25
+ if ( ! is_object( self::$instance ) ) {
26
+ self::$instance = new self();
27
+ }
28
+
29
+ return self::$instance;
30
+ }
31
+
32
+ /**
33
+ * Add hooks needed for functionality.
34
+ *
35
+ * @since 2.6.2
36
+ */
37
+ public function hook() {
38
+ add_filter( 'debug_information', [ $this, 'debug_information' ] );
39
+ }
40
+
41
+ /**
42
+ * Remove hooks needed for functionality.
43
+ *
44
+ * @since 2.6.2
45
+ */
46
+ public function unhook() {
47
+ remove_filter( 'debug_information', [ $this, 'debug_information' ] );
48
+ }
49
+
50
+ /**
51
+ * Add our data to Site Health information.
52
+ *
53
+ * @since 2.6.2
54
+ *
55
+ * @param array $info The Site Health information.
56
+ *
57
+ * @return array The updated Site Health information.
58
+ */
59
+ public function debug_information( $info ) {
60
+ $info['pmpro'] = [
61
+ 'label' => 'Paid Memberships Pro',
62
+ 'description' => __( 'This debug information for your Paid Memberships Pro installation can assist you in getting support.', 'paid-memberships-pro' ),
63
+ 'fields' => [
64
+ 'pmpro-cron-jobs' => [
65
+ 'label' => __( 'Cron Job Status', 'paid-memberships-pro' ),
66
+ 'value' => self::get_cron_jobs(),
67
+ ],
68
+ 'pmpro-gateway' => [
69
+ 'label' => __( 'Payment Gateway', 'paid-memberships-pro' ),
70
+ 'value' => self::get_gateway(),
71
+ ],
72
+ 'pmpro-gateway-env' => [
73
+ 'label' => __( 'Payment Gateway Environment', 'paid-memberships-pro' ),
74
+ 'value' => self::get_gateway_env(),
75
+ ],
76
+ 'pmpro-orders' => [
77
+ 'label' => __( 'Orders', 'paid-memberships-pro' ),
78
+ 'value' => self::get_orders(),
79
+ ],
80
+ 'pmpro-discount-codes' => [
81
+ 'label' => __( 'Discount Codes', 'paid-memberships-pro' ),
82
+ 'value' => self::get_discount_codes(),
83
+ ],
84
+ 'pmpro-membership-levels' => [
85
+ 'label' => __( 'Membership Levels', 'paid-memberships-pro' ),
86
+ 'value' => self::get_levels(),
87
+ ],
88
+ 'pmpro-custom-templates' => [
89
+ 'label' => __( 'Custom Templates', 'paid-memberships-pro' ),
90
+ 'value' => self::get_custom_templates(),
91
+ ],
92
+ 'pmpro-getfile-usage' => [
93
+ 'label' => __( 'getfile.php Usage', 'paid-memberships-pro' ),
94
+ 'value' => self::get_getfile_usage(),
95
+ ],
96
+ 'pmpro-htaccess-cache-usage' => [
97
+ 'label' => __( '.htaccess Cache Usage', 'paid-memberships-pro' ),
98
+ 'value' => self::get_htaccess_cache_usage(),
99
+ ],
100
+ 'pmpro-pages' => [
101
+ 'label' => __( 'Membership Pages', 'paid-memberships-pro' ),
102
+ 'value' => self::get_pmpro_pages(),
103
+ ]
104
+ ],
105
+ ];
106
+
107
+ // Automatically add information about constants set.
108
+ $info['pmpro']['fields'] = array_merge( $info['pmpro']['fields'], self::get_constants() );
109
+
110
+ return $info;
111
+ }
112
+
113
+ /**
114
+ * Gets the level information.
115
+ *
116
+ * @since 2.6.2
117
+ *
118
+ * @return string The level information.
119
+ */
120
+ public function get_levels() {
121
+ $membership_levels = pmpro_getAllLevels( true, true );
122
+
123
+ if ( ! $membership_levels ) {
124
+ return __( 'No Levels Found', 'paid-memberships-pro' );
125
+ }
126
+
127
+ return wp_json_encode( $membership_levels, JSON_PRETTY_PRINT );
128
+ }
129
+
130
+ /**
131
+ * Get the discount code information.
132
+ *
133
+ * @since 2.6.2
134
+ *
135
+ * @return string The discount code information.
136
+ */
137
+ public function get_discount_codes() {
138
+ global $wpdb;
139
+
140
+ $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `$wpdb->pmpro_discount_codes`" );
141
+
142
+ // translators: %d: The total count of discount codes.
143
+ return sprintf( _n( '%d discount code', '%d discount codes', $count, 'paid-memberships-pro' ), $count );
144
+ }
145
+
146
+ /**
147
+ * Get the order information.
148
+ *
149
+ * @since 2.6.2
150
+ *
151
+ * @return string The order information.
152
+ */
153
+ public function get_orders() {
154
+ global $wpdb;
155
+
156
+ $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `$wpdb->pmpro_membership_orders`" );
157
+
158
+ // translators: %d: The total count of orders.
159
+ return sprintf( _n( '%d order', '%d orders', $count, 'paid-memberships-pro' ), $count );
160
+ }
161
+
162
+ /**
163
+ * Get the payment gateway information.
164
+ *
165
+ * @since 2.6.2
166
+ *
167
+ * @return string The payment gateway information.
168
+ */
169
+ public function get_gateway() {
170
+ $gateway = pmpro_getOption( 'gateway' );
171
+ $gateways = pmpro_gateways();
172
+
173
+ // Check if gateway is registered.
174
+ if ( ! isset( $gateways[ $gateway ] ) ) {
175
+ // translators: %s: The gateway name that is not registered.
176
+ return sprintf( __( '%s (gateway not registered)', 'paid-memberships-pro' ), $gateway );
177
+ }
178
+
179
+ $gateway_text = $gateways[ $gateway ];
180
+
181
+ // Custom Stripe gateway information.
182
+ if ( 'stripe' === $gateway ) {
183
+ $stripe = new PMProGateway_stripe();
184
+
185
+ $legacy = $stripe->using_legacy_keys();
186
+ $connect = $stripe->has_connect_credentials();
187
+
188
+ if ( $legacy ) {
189
+ $gateway_text .= ' (' . __( 'Legacy Keys', 'paid-memberships-pro' ) . ')';
190
+ }
191
+
192
+ if ( $connect ) {
193
+ $gateway_text .= ' (' . __( 'Stripe Connect', 'paid-memberships-pro' ) . ')';
194
+ }
195
+ }
196
+
197
+ return $gateway_text;
198
+ }
199
+
200
+ /**
201
+ * Get the payment gateway environment information.
202
+ *
203
+ * @since 2.6.2
204
+ *
205
+ * @return string The payment gateway environment information.
206
+ */
207
+ public function get_gateway_env() {
208
+ $environment = pmpro_getOption( 'gateway_environment' );
209
+ $environments = [
210
+ 'sandbox' => __( 'Sandbox/Testing', 'paid-memberships-pro' ),
211
+ 'live' => __( 'Live/Production', 'paid-memberships-pro' ),
212
+ ];
213
+
214
+ // Check if environment is registered.
215
+ if ( ! isset( $environments[ $environment ] ) ) {
216
+ // translators: %s: The environment name that is not registered.
217
+ return sprintf( __( '%s (environment not registered)', 'paid-memberships-pro' ), $environment );
218
+ }
219
+
220
+ return $environments[ $environment ];
221
+ }
222
+
223
+ /**
224
+ * Get the custom template information.
225
+ *
226
+ * @since 2.6.2
227
+ *
228
+ * @return string The custom template information.
229
+ */
230
+ public function get_custom_templates() {
231
+ $parent_theme_path = get_template_directory() . '/paid-memberships-pro/';
232
+ $child_theme_path = get_stylesheet_directory() . '/paid-memberships-pro/';
233
+
234
+ $parent_theme_templates = $this->get_custom_templates_from_path( $parent_theme_path );
235
+ $child_theme_templates = null;
236
+
237
+ if ( $parent_theme_path !== $child_theme_path ) {
238
+ $child_theme_templates = $this->get_custom_templates_from_path( $child_theme_path );
239
+ }
240
+
241
+ if ( is_wp_error( $parent_theme_templates ) ) {
242
+ return $parent_theme_templates->get_error_message();
243
+ }
244
+
245
+ $templates = $parent_theme_templates;
246
+
247
+ if ( null !== $child_theme_templates ) {
248
+ if ( is_wp_error( $child_theme_templates ) ) {
249
+ $child_theme_templates = $child_theme_templates->get_error_message();
250
+ }
251
+
252
+ $templates = [
253
+ 'parent' => $parent_theme_templates,
254
+ 'child' => $child_theme_templates,
255
+ ];
256
+ }
257
+
258
+ return wp_json_encode( $templates, JSON_PRETTY_PRINT );
259
+ }
260
+
261
+ private function get_custom_templates_from_path( $path ) {
262
+ require_once ABSPATH . 'wp-admin/includes/file.php';
263
+
264
+ /**
265
+ * @var $wp_filesystem WP_Filesystem_Base
266
+ */
267
+ global $wp_filesystem;
268
+
269
+ WP_Filesystem();
270
+
271
+ if ( ! $wp_filesystem ) {
272
+ return new WP_Error( 'access-denied', __( 'Unable to verify', 'paid-memberships-pro' ) );
273
+ }
274
+
275
+ $override_path = get_stylesheet_directory() . '/paid-memberships-pro/';
276
+ $override_list = false;
277
+
278
+ if ( ! $wp_filesystem->is_dir( $path ) ) {
279
+ return new WP_Error( 'path-not-found', __( 'No template overrides', 'paid-membership-pro' ) );
280
+ }
281
+
282
+ $override_list = $wp_filesystem->dirlist( $path );
283
+
284
+ if ( ! $override_list ) {
285
+ return new WP_Error( 'path-empty', __( 'Empty override folder -- no template overrides', 'paid-membership-pro' ) );
286
+ }
287
+
288
+ $templates = [];
289
+
290
+ foreach ( $override_list as $template => $info ) {
291
+ $last_modified = $info['lastmod'] . ' ' . $info['time'];
292
+
293
+ if ( isset( $info['lastmodunix'] ) ) {
294
+ $last_modified = date( 'Y-m-d H:i:s', $info['lastmodunix'] );
295
+ }
296
+
297
+ $templates[ $template ] = [
298
+ 'last_updated' => $last_modified,
299
+ 'path' => str_replace( ABSPATH, '', $path ) . $template,
300
+ ];
301
+ }
302
+
303
+ return $templates;
304
+ }
305
+
306
+ /**
307
+ * Get the cron job information.
308
+ *
309
+ * @since 2.6.2
310
+ *
311
+ * @return string The cron job information.
312
+ */
313
+ public function get_cron_jobs() {
314
+ $crons = _get_cron_array();
315
+
316
+ $cron_times = [];
317
+
318
+ // These are our crons.
319
+ $expected_crons = [
320
+ 'pmpro_cron_expire_memberships',
321
+ 'pmpro_cron_expiration_warnings',
322
+ 'pmpro_cron_credit_card_expiring_warnings',
323
+ 'pmpro_cron_admin_activity_email',
324
+ ];
325
+
326
+ $gateway = pmpro_getOption( 'gateway' );
327
+
328
+ if ( 'stripe' === $gateway ) {
329
+ $expected_crons[] = 'pmpro_cron_stripe_subscription_updates';
330
+ }
331
+
332
+ // Find any of our crons and when their next run is.
333
+ if ( $crons ) {
334
+ foreach ( $crons as $time => $cron ) {
335
+ $keys = array_keys( $cron );
336
+ $matches = array_intersect( $expected_crons, $keys );
337
+
338
+ foreach ( $matches as $cron_hook ) {
339
+ $cron_times[ $cron_hook ] = date( 'Y-m-d H:i:s', $time );
340
+ }
341
+ }
342
+ }
343
+
344
+ $missing_crons = array_diff( $expected_crons, array_keys( $cron_times ) );
345
+
346
+ $cron_information = [];
347
+
348
+ foreach ( $missing_crons as $cron_hook ) {
349
+ $cron_information[] = $cron_hook . ' (' . __( 'missing', 'paid-memberships-pro' ) . ')';
350
+ }
351
+
352
+ // Build the information of what crons are missing and what crons are going to run.
353
+ foreach ( $cron_times as $cron_hook => $next_run ) {
354
+ $cron_information[] = $cron_hook . ' (' . $next_run . ')';
355
+ }
356
+
357
+ return implode( " | \n", $cron_information );
358
+ }
359
+
360
+ /**
361
+ * Get the assigned Member pages and their URL's
362
+ *
363
+ * @since TBA
364
+ *
365
+ * @return string The member page information
366
+ */
367
+ public function get_pmpro_pages() {
368
+
369
+ global $pmpro_pages;
370
+
371
+ $page_information = array();
372
+
373
+ if( !empty( $pmpro_pages ) ){
374
+
375
+ foreach( $pmpro_pages as $key => $val ){
376
+
377
+ $permalink = get_the_permalink( (int)$val );
378
+
379
+ if( empty( $permalink ) ){
380
+ $page_information[$key] = 'Not Set'; //Not translating this
381
+ } else {
382
+ $page_information[$key] = $permalink;
383
+ }
384
+
385
+ }
386
+
387
+ } else {
388
+
389
+ return __( 'No Membership Pages Found', 'paid-memberships-pro' );
390
+
391
+ }
392
+
393
+ return $page_information;
394
+
395
+ }
396
+
397
+ /**
398
+ * Get the .htaccess services/getfile.php usage information.
399
+ *
400
+ * @since 2.6.4
401
+ *
402
+ * @return string The .htaccess services/getfile.php usage information.
403
+ */
404
+ public function get_getfile_usage() {
405
+ if ( ! defined( 'PMPRO_GETFILE_ENABLED' ) ) {
406
+ return __( 'PMPRO_GETFILE_ENABLED is not set', 'paid-memberships-pro' );
407
+ }
408
+
409
+ if ( ! PMPRO_GETFILE_ENABLED ) {
410
+ return __( 'PMPRO_GETFILE_ENABLED is off', 'paid-memberships-pro' );
411
+ }
412
+
413
+ require_once ABSPATH . 'wp-admin/includes/file.php';
414
+
415
+ /**
416
+ * @var $wp_filesystem WP_Filesystem_Base
417
+ */
418
+ global $wp_filesystem;
419
+
420
+ WP_Filesystem();
421
+
422
+ if ( ! $wp_filesystem ) {
423
+ return __( 'Unable to access .htaccess file', 'paid-memberships-pro' );
424
+ }
425
+
426
+ if ( ! $wp_filesystem->exists( ABSPATH . '/.htaccess' ) ) {
427
+ return __( 'Off - No .htaccess file', 'paid-memberships-pro' );
428
+ }
429
+
430
+ $htaccess_contents = $wp_filesystem->get_contents( ABSPATH . '/.htaccess' );
431
+
432
+ if ( false === strpos( $htaccess_contents, '/services/getfile.php' ) ) {
433
+ return __( 'Off', 'paid-memberships-pro' );
434
+ }
435
+
436
+ return __( 'On - .htaccess contains services/getfile.php usage', 'paid-memberships-pro' );
437
+ }
438
+
439
+ /**
440
+ * Get the .htaccess cache usage information.
441
+ *
442
+ * @since 2.6.4
443
+ *
444
+ * @return string The .htaccess cache usage information.
445
+ */
446
+ public function get_htaccess_cache_usage() {
447
+ require_once ABSPATH . 'wp-admin/includes/file.php';
448
+
449
+ /**
450
+ * @var $wp_filesystem WP_Filesystem_Base
451
+ */
452
+ global $wp_filesystem;
453
+
454
+ WP_Filesystem();
455
+
456
+ if ( ! $wp_filesystem ) {
457
+ return __( 'Unable to access .htaccess file', 'paid-memberships-pro' );
458
+ }
459
+
460
+ if ( ! $wp_filesystem->exists( ABSPATH . '/.htaccess' ) ) {
461
+ return __( 'Off - No .htaccess file', 'paid-memberships-pro' );
462
+ }
463
+
464
+ $htaccess_contents = $wp_filesystem->get_contents( ABSPATH . '/.htaccess' );
465
+
466
+ if ( false !== strpos( $htaccess_contents, 'ExpiresByType text/html' ) ) {
467
+ return __( 'On - Browser cache enabled for HTML (ExpiresByType text/html), this may interfere with Content Restriction after Login. Remove that line from your .htaccess to resolve this problem.', 'paid-memberships-pro' );
468
+ } elseif ( false !== strpos( $htaccess_contents, 'ExpiresDefault' ) ) {
469
+ return __( 'On - Browser cache enabled for HTML (ExpiresDefault), this may interfere with Content Restriction after Login. Remove that line from your .htaccess to resolve this problem.', 'paid-memberships-pro' );
470
+ }
471
+
472
+ return __( 'Off', 'paid-memberships-pro' );
473
+ }
474
+
475
+ /**
476
+ * Get the constants site health information.
477
+ *
478
+ * @since 2.6.4
479
+ *
480
+ * @return array The constants site health information.
481
+ */
482
+ public function get_constants() {
483
+ $constants = [
484
+ 'PMPRO_CRON_LIMIT' => __( 'Cron Limit', 'paid-memberships-pro' ),
485
+ 'PMPRO_DEFAULT_LEVEL' => __( 'Default Membership Level', 'paid-memberships-pro' ),
486
+ 'PMPRO_USE_SESSIONS' => __( 'Use Sessions', 'paid-memberships-pro' ),
487
+ ];
488
+
489
+ $gateway_specific_constants = [
490
+ 'authorizenet' => [
491
+ 'PMPRO_AUTHNET_SILENT_POST_DEBUG' => __( 'Authorize.net Silent Post Debug Mode', 'paid-memberships-pro' ),
492
+ ],
493
+ 'braintree' => [
494
+ 'PMPRO_BRAINTREE_WEBHOOK_DEBUG' => __( 'Braintree Webhook Debug Mode', 'paid-memberships-pro' ),
495
+ ],
496
+ 'paypal' => [
497
+ 'PMPRO_IPN_DEBUG' => __( 'PayPal IPN Debug Mode', 'paid-memberships-pro' ),
498
+ ],
499
+ 'paypalexpress' => [
500
+ 'PMPRO_IPN_DEBUG' => __( 'PayPal IPN Debug Mode', 'paid-memberships-pro' ),
501
+ ],
502
+ 'paypalstandard' => [
503
+ 'PMPRO_IPN_DEBUG' => __( 'PayPal IPN Debug Mode', 'paid-memberships-pro' ),
504
+ ],
505
+ 'stripe' => [
506
+ 'PMPRO_STRIPE_WEBHOOK_DELAY' => __( 'Stripe Webhook Delay', 'paid-memberships-pro' ),
507
+ 'PMPRO_STRIPE_WEBHOOK_DEBUG' => __( 'Stripe Webhook Debug Mode', 'paid-memberships-pro' ),
508
+ ],
509
+ 'twocheckout' => [
510
+ 'PMPRO_INS_DEBUG' => __( '2Checkout INS Debug Mode', 'paid-memberships-pro' ),
511
+ ],
512
+ ];
513
+
514
+ $gateway = pmpro_getOption( 'gateway' );
515
+
516
+ if ( $gateway && isset( $gateway_specific_constants[ $gateway ] ) ) {
517
+ $constants = array_merge( $constants, $gateway_specific_constants[ $gateway ] );
518
+ }
519
+
520
+ /**
521
+ * Allow filtering the supported Site Health constants by other add ons.
522
+ *
523
+ * @since 2.6.4
524
+ *
525
+ * @param array $constants The list of constants to show in Site Health.
526
+ * @param string $gateway The current payment gateway.
527
+ */
528
+ $constants = apply_filters( 'pmpro_site_health_constants', $constants, $gateway );
529
+
530
+ // Get and format constant information.
531
+ $constants_formatted = [];
532
+
533
+ foreach ( $constants as $constant => $label ) {
534
+ // Only get site health info for constants that are set.
535
+ if ( ! defined( $constant ) ) {
536
+ continue;
537
+ }
538
+
539
+ $constants_formatted[ 'pmpro-constants-' . $constant ] = [
540
+ 'label' => $label . ' (' . $constant . ')',
541
+ 'value' => var_export( constant( $constant ), true ),
542
+ ];
543
+ }
544
+
545
+ return $constants_formatted;
546
+ }
547
+
548
+ }
classes/class.memberorder.php CHANGED
@@ -151,6 +151,68 @@
151
  return false; //didn't find it in the DB
152
  }
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  /**
155
  * Set up the Gateway class to use with this order.
156
  *
@@ -494,6 +556,8 @@
494
 
495
  //set values array for filter
496
  $values = array("price" => $price, "tax_state" => $tax_state, "tax_rate" => $tax_rate);
 
 
497
  if(!empty($this->billing->state))
498
  $values['billing_state'] = $this->billing->state;
499
  if(!empty($this->billing->city))
@@ -536,6 +600,10 @@
536
  * Change the timestamp of an order by passing in year, month, day, time.
537
  *
538
  * $time should be adjusted for local timezone.
 
 
 
 
539
  */
540
  function updateTimestamp($year, $month, $day, $time = NULL)
541
  {
@@ -552,8 +620,11 @@
552
  global $wpdb;
553
  $this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET timestamp = '" . $date . "' WHERE id = '" . $this->id . "' LIMIT 1";
554
 
 
555
  if($wpdb->query($this->sqlQuery) !== "false") {
556
  $this->timestamp = strtotime( $date );
 
 
557
  return $this->getMemberOrderByID($this->id);
558
  } else {
559
  return false;
@@ -592,10 +663,12 @@
592
  //calculate total
593
  if(!empty($this->total))
594
  $total = $this->total;
595
- else {
596
  $total = (float)$amount + (float)$tax;
597
  $this->total = $total;
598
- }
 
 
599
 
600
  //these fix some warnings/notices
601
  if(empty($this->billing))
@@ -767,22 +840,24 @@
767
  /**
768
  * Get a random code to use as the order code.
769
  */
770
- function getRandomCode()
771
- {
772
  global $wpdb;
773
 
774
- while(empty($code))
775
- {
 
776
 
777
- $scramble = md5(AUTH_KEY . current_time('timestamp') . SECURE_AUTH_KEY);
778
- $code = substr($scramble, 0, 10);
779
- $code = apply_filters("pmpro_random_code", $code, $this); //filter
780
- $check = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = '$code' LIMIT 1");
781
- if($check || is_numeric($code))
 
782
  $code = NULL;
 
783
  }
784
 
785
- return strtoupper($code);
786
  }
787
 
788
  /**
@@ -795,12 +870,17 @@
795
  if(empty($this->id))
796
  return false;
797
 
798
- $this->status = $newstatus;
799
  $this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET status = '" . esc_sql($newstatus) . "' WHERE id = '" . $this->id . "' LIMIT 1";
800
- if($wpdb->query($this->sqlQuery) !== false)
 
 
 
 
 
801
  return true;
802
- else
803
  return false;
 
804
  }
805
 
806
  /**
@@ -857,8 +937,10 @@
857
  $this->gateway,
858
  $this->gateway_environment,
859
  $this->subscription_transaction_id
860
- );
 
861
  $wpdb->query($sqlQuery);
 
862
 
863
  //cancel the gateway subscription first
864
  if (is_object($this->Gateway)) {
@@ -948,6 +1030,58 @@
948
  return false;
949
  }
950
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
951
  /**
952
  * Delete an order and associated data.
953
  */
@@ -966,4 +1100,46 @@
966
  else
967
  return false;
968
  }
969
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  return false; //didn't find it in the DB
152
  }
153
 
154
+ /**
155
+ * Get the first order for this subscription.
156
+ * Useful to find the original order from a recurring order.
157
+ * @since 2.5
158
+ * @return mixed Order object if found or false if not.
159
+ */
160
+ function get_original_subscription_order( $subscription_id = '' ){
161
+ global $wpdb;
162
+
163
+ // Default to use the subscription ID on this order object.
164
+ if ( empty( $subscription_id ) && ! empty( $this->subscription_transaction_id ) ) {
165
+ $subscription_id = $this->subscription_transaction_id;
166
+ }
167
+
168
+ // Must have a subscription ID.
169
+ if ( empty( $subscription_id ) ) {
170
+ return false;
171
+ }
172
+
173
+ // Get some other values from this order to narrow the search.
174
+ if ( ! empty( $this->user_id ) ) {
175
+ $user_id = $this->user_id;
176
+ } else {
177
+ $user_id = '';
178
+ }
179
+ if ( ! empty( $this->gateway ) ) {
180
+ $gateway = $this->gateway;
181
+ } else {
182
+ $gateway = '';
183
+ }
184
+ if ( ! empty( $this->gateway_environment ) ) {
185
+ $gateway_environment = $this->gateway_environment;
186
+ } else {
187
+ $gateway_environment = '';
188
+ }
189
+
190
+ // Double check for a user_id, gateway and gateway environment.
191
+ $sql = $wpdb->prepare(
192
+ "SELECT ID
193
+ FROM $wpdb->pmpro_membership_orders
194
+ WHERE `subscription_transaction_id` = %s
195
+ AND `user_id` = %d
196
+ AND `gateway` = %s
197
+ AND `gateway_environment` = %s
198
+ ORDER BY id ASC
199
+ LIMIT 1",
200
+ array(
201
+ $subscription_id,
202
+ $user_id,
203
+ $gateway,
204
+ $gateway_environment
205
+ )
206
+ );
207
+
208
+ $order_id = $wpdb->get_var( $sql );
209
+ if ( ! empty( $order_id ) ) {
210
+ return new MemberOrder( $order_id );
211
+ } else {
212
+ return false;
213
+ }
214
+ }
215
+
216
  /**
217
  * Set up the Gateway class to use with this order.
218
  *
556
 
557
  //set values array for filter
558
  $values = array("price" => $price, "tax_state" => $tax_state, "tax_rate" => $tax_rate);
559
+ if(!empty($this->billing->street))
560
+ $values['billing_street'] = $this->billing->street;
561
  if(!empty($this->billing->state))
562
  $values['billing_state'] = $this->billing->state;
563
  if(!empty($this->billing->city))
600
  * Change the timestamp of an order by passing in year, month, day, time.
601
  *
602
  * $time should be adjusted for local timezone.
603
+ *
604
+ * NOTE: This function should no longer be used. Instead, set the timestamp
605
+ * for the order directly and call the MemberOrder->saveOrder() function.
606
+ * This function is no longer used on the /adminpages/orders.php page.
607
  */
608
  function updateTimestamp($year, $month, $day, $time = NULL)
609
  {
620
  global $wpdb;
621
  $this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET timestamp = '" . $date . "' WHERE id = '" . $this->id . "' LIMIT 1";
622
 
623
+ do_action('pmpro_update_order', $this);
624
  if($wpdb->query($this->sqlQuery) !== "false") {
625
  $this->timestamp = strtotime( $date );
626
+ do_action('pmpro_updated_order', $this);
627
+
628
  return $this->getMemberOrderByID($this->id);
629
  } else {
630
  return false;
663
  //calculate total
664
  if(!empty($this->total))
665
  $total = $this->total;
666
+ elseif ( ! isset( $this->total ) || $this->total === '' ) {
667
  $total = (float)$amount + (float)$tax;
668
  $this->total = $total;
669
+ } else {
670
+ $total = 0;
671
+ }
672
 
673
  //these fix some warnings/notices
674
  if(empty($this->billing))
840
  /**
841
  * Get a random code to use as the order code.
842
  */
843
+ function getRandomCode() {
 
844
  global $wpdb;
845
 
846
+ // We mix this with the seed to make sure we get unique codes.
847
+ static $count = 0;
848
+ $count++;
849
 
850
+ while( empty( $code ) ) {
851
+ $scramble = md5( AUTH_KEY . microtime() . SECURE_AUTH_KEY . $count );
852
+ $code = substr( $scramble, 0, 10 );
853
+ $code = apply_filters( 'pmpro_random_code', $code, $this ); //filter
854
+ $check = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = '$code' LIMIT 1" );
855
+ if( $check || is_numeric( $code ) ) {
856
  $code = NULL;
857
+ }
858
  }
859
 
860
+ return strtoupper( $code );
861
  }
862
 
863
  /**
870
  if(empty($this->id))
871
  return false;
872
 
 
873
  $this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET status = '" . esc_sql($newstatus) . "' WHERE id = '" . $this->id . "' LIMIT 1";
874
+
875
+ do_action('pmpro_update_order', $this);
876
+ if($wpdb->query($this->sqlQuery) !== false){
877
+ $this->status = $newstatus;
878
+ do_action('pmpro_updated_order', $this);
879
+
880
  return true;
881
+ }else{
882
  return false;
883
+ }
884
  }
885
 
886
  /**
937
  $this->gateway,
938
  $this->gateway_environment,
939
  $this->subscription_transaction_id
940
+ );
941
+ do_action('pmpro_update_order', $this);
942
  $wpdb->query($sqlQuery);
943
+ do_action('pmpro_updated_order', $this);
944
 
945
  //cancel the gateway subscription first
946
  if (is_object($this->Gateway)) {
1030
  return false;
1031
  }
1032
 
1033
+ /**
1034
+ * Sets the billing address fields on the order object.
1035
+ * Checks the last order for the same sub or pulls from user meta.
1036
+ * @since 2.5.5
1037
+ */
1038
+ function find_billing_address() {
1039
+ global $wpdb;
1040
+
1041
+ if ( empty( $this->billing ) || empty( $this->billing->street ) ) {
1042
+ // We do not already have a billing address.
1043
+ $last_subscription_order = new MemberOrder();
1044
+ $last_subscription_order->getLastMemberOrderBySubscriptionTransactionID( $this->subscription_transaction_id );
1045
+ if ( ! empty( $last_subscription_order->billing ) && ! empty( $last_subscription_order->billing->street ) ) {
1046
+ // Last order in subscription has biling information. Pull data from there.
1047
+ $this->Address1 = $last_subscription_order->billing->street;
1048
+ $this->City = $last_subscription_order->billing->city;
1049
+ $this->State = $last_subscription_order->billing->state;
1050
+ $this->Zip = $last_subscription_order->billing->zip;
1051
+ $this->CountryCode = $last_subscription_order->billing->country;
1052
+ $this->PhoneNumber = $last_subscription_order->billing->phone;
1053
+ $this->Email = $wpdb->get_var("SELECT user_email FROM $wpdb->users WHERE ID = '" . $this->user_id . "' LIMIT 1");
1054
+
1055
+ $this->billing = new stdClass();
1056
+ $this->billing->name = $last_subscription_order->billing->name;
1057
+ $this->billing->street = $last_subscription_order->billing->street;
1058
+ $this->billing->city = $last_subscription_order->billing->city;
1059
+ $this->billing->state = $last_subscription_order->billing->state;
1060
+ $this->billing->zip = $last_subscription_order->billing->zip;
1061
+ $this->billing->country = $last_subscription_order->billing->country;
1062
+ $this->billing->phone = $last_subscription_order->billing->phone;
1063
+ } else {
1064
+ // Last order did not have billing information. Try to pull from usermeta.
1065
+ $this->Address1 = get_user_meta( $this->user_id, "pmpro_baddress1", true );
1066
+ $this->City = get_user_meta( $this->user_id, "pmpro_bcity", true );
1067
+ $this->State = get_user_meta( $this->user_id, "pmpro_bstate", true );
1068
+ $this->Zip = get_user_meta( $this->user_id, "pmpro_bzip", true );
1069
+ $this->CountryCode = get_user_meta( $this->user_id, "pmpro_bcountry", true );
1070
+ $this->PhoneNumber = get_user_meta( $this->user_id, "pmpro_bphone", true );
1071
+ $this->Email = $wpdb->get_var("SELECT user_email FROM $wpdb->users WHERE ID = '" . $this->user_id . "' LIMIT 1");
1072
+
1073
+ $this->billing = new stdClass();
1074
+ $this->billing->name = get_user_meta( $this->user_id, "pmpro_bfirstname", true ) . " " . get_user_meta( $this->user_id, "pmpro_blastname", true ) ;
1075
+ $this->billing->street = $this->Address1;
1076
+ $this->billing->city = $this->City;
1077
+ $this->billing->state = $this->State;
1078
+ $this->billing->zip = $this->Zip;
1079
+ $this->billing->country = $this->CountryCode;
1080
+ $this->billing->phone = $this->PhoneNumber;
1081
+ }
1082
+ }
1083
+ }
1084
+
1085
  /**
1086
  * Delete an order and associated data.
1087
  */
1100
  else
1101
  return false;
1102
  }
1103
+
1104
+ /*
1105
+ * Generates a test order on the fly for orders.
1106
+ */
1107
+ function get_test_order() {
1108
+ global $current_user;
1109
+
1110
+ //$test_order = $this->getEmptyMemberOrder();
1111
+ $all_levels = pmpro_getAllLevels();
1112
+
1113
+ if ( ! empty( $all_levels ) ) {
1114
+ $first_level = array_shift( $all_levels );
1115
+ $this->membership_id = $first_level->id;
1116
+ $this->InitialPayment = $first_level->initial_payment;
1117
+ } else {
1118
+ $this->membership_id = 1;
1119
+ $this->InitialPayment = 1;
1120
+ }
1121
+ $this->user_id = $current_user->ID;
1122
+ $this->cardtype = "Visa";
1123
+ $this->accountnumber = "4111111111111111";
1124
+ $this->expirationmonth = date( 'm', current_time( 'timestamp' ) );
1125
+ $this->expirationyear = ( intval( date( 'Y', current_time( 'timestamp' ) ) ) + 1 );
1126
+ $this->ExpirationDate = $this->expirationmonth . $this->expirationyear;
1127
+ $this->CVV2 = '123';
1128
+ $this->FirstName = 'Jane';
1129
+ $this->LastName = 'Doe';
1130
+ $this->Address1 = '123 Street';
1131
+ $this->billing = new stdClass();
1132
+ $this->billing->name = 'Jane Doe';
1133
+ $this->billing->street = '123 Street';
1134
+ $this->billing->city = 'City';
1135
+ $this->billing->state = 'ST';
1136
+ $this->billing->country = 'US';
1137
+ $this->billing->zip = '12345';
1138
+ $this->billing->phone = '5558675309';
1139
+ $this->gateway_environment = 'sandbox';
1140
+ $this->timestamp = time();
1141
+ $this->notes = __( 'This is a test order used with the PMPro Email Templates addon.', 'paid-memberships-pro' );
1142
+
1143
+ return apply_filters( 'pmpro_test_order_data', $this );
1144
+ }
1145
+ } // End of Class
classes/class.pmproemail.php CHANGED
@@ -1,13 +1,14 @@
1
  <?php
2
  class PMProEmail
3
  {
 
4
  function __construct()
5
  {
6
  $this->email = $this->from = $this->fromname = $this->subject = $this->template = $this->data = $this->body = NULL;
7
  }
8
 
9
  function sendEmail($email = NULL, $from = NULL, $fromname = NULL, $subject = NULL, $template = NULL, $data = NULL)
10
- {
11
  //if values were passed
12
  if($email)
13
  $this->email = $email;
@@ -21,9 +22,16 @@
21
  $this->template = $template;
22
  if($data)
23
  $this->data = $data;
24
-
 
 
 
 
 
 
 
25
  //default values
26
- global $current_user;
27
  if(!$this->email)
28
  $this->email = $current_user->user_email;
29
 
@@ -32,15 +40,20 @@
32
 
33
  if(!$this->fromname)
34
  $this->fromname = pmpro_getOption("from_name");
35
-
36
- if(!$this->subject)
37
- $this->subject = sprintf(__("An Email From %s", 'paid-memberships-pro' ), get_option("blogname"));
38
-
39
- //decode the subject line in case there are apostrophes/etc in it
40
- $this->subject = html_entity_decode($this->subject, ENT_QUOTES, 'UTF-8');
41
 
42
  if(!$this->template)
43
  $this->template = "default";
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  $this->headers = array("Content-Type: text/html");
46
 
@@ -49,7 +62,9 @@
49
  //load the template
50
  $locale = apply_filters("plugin_locale", get_locale(), "paid-memberships-pro");
51
 
52
- if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $locale . "/" . $this->template . ".html"))
 
 
53
  $this->body = file_get_contents(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $locale . "/" . $this->template . ".html"); //localized email folder in child theme
54
  elseif(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $this->template . ".html"))
55
  $this->body = file_get_contents(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $this->template . ".html"); //email folder in child theme
@@ -67,25 +82,29 @@
67
  $this->body = file_get_contents(WP_LANG_DIR . '/pmpro/email/' . $this->template . ".html"); //email folder in WP language folder
68
  elseif(file_exists(PMPRO_DIR . "/languages/email/" . $locale . "/" . $this->template . ".html"))
69
  $this->body = file_get_contents(PMPRO_DIR . "/languages/email/" . $locale . "/" . $this->template . ".html"); //email folder in PMPro language folder
70
- elseif($this->getDefaultEmailTemplate($this->template))
71
- $this->body = $this->getDefaultEmailTemplate($this->template);
72
- elseif(file_exists(PMPRO_DIR . "/email/" . $this->template . ".html"))
73
- $this->body = file_get_contents(PMPRO_DIR . "/email/" . $this->template . ".html"); //default template in plugin
74
  elseif(!empty($this->data) && !empty($this->data['body']))
75
  $this->body = $this->data['body']; //data passed in
76
 
77
- //header and footer
78
- /* This is handled for all emails via the pmpro_send_html function in paid-memberships-pro now
79
- if(file_exists(get_template_directory() . "/email_header.html"))
80
- {
81
- $this->body = file_get_contents(get_template_directory() . "/email_header.html") . "\n" . $this->body;
82
- }
83
- if(file_exists(get_template_directory() . "/email_footer.html"))
84
- {
85
- $this->body = $this->body . "\n" . file_get_contents(get_template_directory() . "/email_footer.html");
86
  }
87
- */
88
-
 
 
 
 
 
 
 
 
 
89
  //if data is a string, assume we mean to replace !!body!! with it
90
  if(is_string($this->data))
91
  $this->data = array("body"=>$data);
@@ -93,13 +112,14 @@
93
  //filter for data
94
  $this->data = apply_filters("pmpro_email_data", $this->data, $this); //filter
95
 
96
- //swap data into body
97
  if(is_array($this->data))
98
  {
99
  foreach($this->data as $key => $value)
100
  {
101
  if ( 'body' != $key ) {
102
  $this->body = str_replace("!!" . $key . "!!", $value, $this->body);
 
103
  }
104
  }
105
  }
@@ -195,15 +215,10 @@
195
  if(!$user)
196
  return false;
197
 
198
- //check settings
199
- $send = pmpro_getOption("email_admin_cancels");
200
- if(empty($send))
201
- return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
202
-
203
  $this->email = get_bloginfo("admin_email");
204
  $this->subject = sprintf(__("Membership for %s at %s has been CANCELLED", 'paid-memberships-pro'), $user->user_login, get_option("blogname"));
205
 
206
- $this->data = array("user_login" => $user->user_login, "user_email" => $user->user_email, "display_name" => $user->display_name, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url());
207
 
208
  if(!empty($old_level_id)) {
209
  if(!is_array($old_level_id))
@@ -236,7 +251,7 @@
236
 
237
  function sendCheckoutEmail($user = NULL, $invoice = NULL)
238
  {
239
- global $wpdb, $current_user;
240
  if(!$user)
241
  $user = $current_user;
242
 
@@ -265,27 +280,38 @@
265
  "membership_level_confirmation_message" => $confirmation_message,
266
  "membership_cost" => pmpro_getLevelCost($user->membership_level),
267
  "login_link" => pmpro_login_url(),
 
268
  "display_name" => $user->display_name,
269
  "user_email" => $user->user_email,
270
  );
271
-
272
- if(!empty($invoice) && !pmpro_isLevelFree($user->membership_level))
273
- {
274
- if($invoice->gateway == "paypalexpress")
275
- $this->template = "checkout_express";
276
- elseif($invoice->gateway == "check")
277
- {
278
- $this->template = "checkout_check";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  $this->data["instructions"] = wpautop(pmpro_getOption("instructions"));
280
  }
281
- elseif(pmpro_isLevelTrial($user->membership_level))
282
- $this->template = "checkout_trial";
283
- else
284
- $this->template = "checkout_paid";
285
-
286
- //BUG: Didn't apply template filter before it was being used in sendEmail()
287
- $this->template = apply_filters("pmpro_email_template", $this->template, $this);
288
-
289
  $this->data["invoice_id"] = $invoice->code;
290
  $this->data["invoice_total"] = pmpro_formatPrice($invoice->total);
291
  $this->data["invoice_date"] = date_i18n( get_option( 'date_format' ), $invoice->getTimestamp() );
@@ -309,55 +335,46 @@
309
  $invoice->billing->country,
310
  $invoice->billing->phone);
311
 
312
- if($invoice->getDiscountCode())
313
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $invoice->discount_code->code . "</p>\n";
314
- else
315
  $this->data["discount_code"] = "";
316
- }
317
- elseif(pmpro_isLevelFree($user->membership_level))
318
- {
319
- $this->template = "checkout_free";
320
- global $discount_code;
321
- if(!empty($discount_code))
322
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $discount_code . "</p>\n";
323
- else
324
- $this->data["discount_code"] = "";
325
- }
326
- else
327
- {
328
- $this->template = "checkout_freetrial";
329
- global $discount_code;
330
- if(!empty($discount_code))
331
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $discount_code . "</p>\n";
332
- else
333
- $this->data["discount_code"] = "";
 
334
  }
335
 
336
  $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(CONVERT_TZ(enddate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND status = 'active' LIMIT 1");
337
- if($enddate)
338
  $this->data["membership_expiration"] = "<p>" . sprintf(__("This membership will expire on %s.", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $enddate)) . "</p>\n";
339
- else
340
  $this->data["membership_expiration"] = "";
 
341
 
342
  return $this->sendEmail();
343
  }
344
 
345
  function sendCheckoutAdminEmail($user = NULL, $invoice = NULL)
346
  {
347
- global $wpdb, $current_user;
348
  if(!$user)
349
  $user = $current_user;
350
 
351
  if(!$user)
352
  return false;
353
 
354
- //check settings
355
- $send = pmpro_getOption("email_admin_checkout");
356
- if(empty($send))
357
- return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
358
-
359
  $this->email = get_bloginfo("admin_email");
360
- $this->subject = sprintf(__("Member Checkout for %s at %s", 'paid-memberships-pro' ), $user->membership_level->name, get_option("blogname"));
361
 
362
  $this->data = array(
363
  "subject" => $this->subject,
@@ -369,23 +386,34 @@
369
  "membership_level_name" => $user->membership_level->name,
370
  "membership_cost" => pmpro_getLevelCost($user->membership_level),
371
  "login_link" => pmpro_login_url(),
 
372
  "display_name" => $user->display_name,
373
  "user_email" => $user->user_email,
374
  );
375
 
376
- if(!empty($invoice) && !pmpro_isLevelFree($user->membership_level))
377
- {
378
- if($invoice->gateway == "paypalexpress")
379
- $this->template = "checkout_express_admin";
380
- elseif($invoice->gateway == "check")
381
- $this->template = "checkout_check_admin";
382
- elseif(pmpro_isLevelTrial($user->membership_level))
383
- $this->template = "checkout_trial_admin";
384
- else
385
- $this->template = "checkout_paid_admin";
386
-
387
- $this->template = apply_filters( "pmpro_email_template", $this->template, $this );
388
-
 
 
 
 
 
 
 
 
 
 
389
  $this->data["invoice_id"] = $invoice->code;
390
  $this->data["invoice_total"] = pmpro_formatPrice($invoice->total);
391
  $this->data["invoice_date"] = date_i18n(get_option('date_format'), $invoice->getTimestamp());
@@ -409,31 +437,27 @@
409
  $invoice->billing->country,
410
  $invoice->billing->phone);
411
 
412
- if($invoice->getDiscountCode())
413
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $invoice->discount_code->code . "</p>\n";
414
- else
415
  $this->data["discount_code"] = "";
416
- }
417
- elseif(pmpro_isLevelFree($user->membership_level))
418
- {
419
- $this->template = "checkout_free_admin";
420
- global $discount_code;
421
- if(!empty($discount_code))
422
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $discount_code . "</p>\n";
423
- else
424
- $this->data["discount_code"] = "";
425
- }
426
- else
427
- {
428
- $this->template = "checkout_freetrial_admin";
429
  $this->data["discount_code"] = "";
430
- }
431
 
432
  $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(CONVERT_TZ(enddate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND status = 'active' LIMIT 1");
433
- if($enddate)
434
  $this->data["membership_expiration"] = "<p>" . sprintf(__("This membership will expire on %s.", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $enddate)) . "</p>\n";
435
- else
436
  $this->data["membership_expiration"] = "";
 
437
 
438
  return $this->sendEmail();
439
  }
@@ -471,7 +495,8 @@
471
  "accountnumber" => hideCardNumber($invoice->accountnumber),
472
  "expirationmonth" => $invoice->expirationmonth,
473
  "expirationyear" => $invoice->expirationyear,
474
- "login_link" => pmpro_login_url()
 
475
  );
476
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
477
  $invoice->billing->street,
@@ -496,11 +521,6 @@
496
  if(!$user || !$invoice)
497
  return false;
498
 
499
- //check settings
500
- $send = pmpro_getOption("email_admin_billing");
501
- if(empty($send))
502
- return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
503
-
504
  $this->email = get_bloginfo("admin_email");
505
  $this->subject = sprintf(__("Billing information has been updated for %s at %s", "paid-memberships-pro"), $user->user_login, get_option("blogname"));
506
 
@@ -525,7 +545,8 @@
525
  "accountnumber" => hideCardNumber($invoice->accountnumber),
526
  "expirationmonth" => $invoice->expirationmonth,
527
  "expirationyear" => $invoice->expirationyear,
528
- "login_link" => pmpro_login_url()
 
529
  );
530
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
531
  $invoice->billing->street,
@@ -549,9 +570,11 @@
549
 
550
  if(!$user || !$invoice)
551
  return false;
 
 
552
 
553
  $this->email = $user->user_email;
554
- $this->subject = sprintf(__("Membership Payment Failed at %s", "paid-memberships-pro"), get_option("blogname"));
555
 
556
  $this->data = array(
557
  "subject" => $this->subject,
@@ -559,8 +582,8 @@
559
  "user_login" => $user->user_login,
560
  "sitename" => get_option("blogname"),
561
  "siteemail" => pmpro_getOption("from_email"),
562
- "membership_id" => $user->membership_level->id,
563
- "membership_level_name" => $user->membership_level->name,
564
  "display_name" => $user->display_name,
565
  "user_email" => $user->user_email,
566
  "billing_name" => $invoice->billing->name,
@@ -574,7 +597,8 @@
574
  "accountnumber" => hideCardNumber($invoice->accountnumber),
575
  "expirationmonth" => $invoice->expirationmonth,
576
  "expirationyear" => $invoice->expirationyear,
577
- "login_link" => pmpro_login_url(pmpro_url("billing"))
 
578
  );
579
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
580
  $invoice->billing->street,
@@ -596,9 +620,10 @@
596
  return false;
597
 
598
  $user = get_userdata($invoice->user_id);
 
599
 
600
  $this->email = $email;
601
- $this->subject = sprintf(__("Membership Payment Failed For %s at %s", "paid-memberships-pro"), $user->display_name, get_option("blogname"));
602
 
603
  $this->data = array(
604
  "subject" => $this->subject,
@@ -606,8 +631,8 @@
606
  "user_login" => $user->user_login,
607
  "sitename" => get_option("blogname"),
608
  "siteemail" => pmpro_getOption("from_email"),
609
- "membership_id" => $user->membership_level->id,
610
- "membership_level_name" => $user->membership_level->name,
611
  "display_name" => $user->display_name,
612
  "user_email" => $user->user_email,
613
  "billing_name" => $invoice->billing->name,
@@ -621,7 +646,8 @@
621
  "accountnumber" => hideCardNumber($invoice->accountnumber),
622
  "expirationmonth" => $invoice->expirationmonth,
623
  "expirationyear" => $invoice->expirationyear,
624
- "login_link" => pmpro_login_url( get_edit_user_link( $user->ID ) )
 
625
  );
626
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
627
  $invoice->billing->street,
@@ -646,7 +672,7 @@
646
  return false;
647
 
648
  $this->email = $user->user_email;
649
- $this->subject = sprintf(__("Credit Card on File Expiring Soon at %s", "paid-memberships-pro"), get_option("blogname"));
650
 
651
  $this->data = array(
652
  "subject" => $this->subject,
@@ -669,7 +695,8 @@
669
  "accountnumber" => hideCardNumber($invoice->accountnumber),
670
  "expirationmonth" => $invoice->expirationmonth,
671
  "expirationyear" => $invoice->expirationyear,
672
- "login_link" => pmpro_login_url(pmpro_url("billing"))
 
673
  );
674
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
675
  $invoice->billing->street,
@@ -697,7 +724,7 @@
697
  $user->membership_level = pmpro_getMembershipLevelForUser($user->ID);
698
 
699
  $this->email = $user->user_email;
700
- $this->subject = sprintf(__("INVOICE for %s membership", "paid-memberships-pro"), get_option("blogname"));
701
 
702
  $this->data = array(
703
  "subject" => $this->subject,
@@ -724,8 +751,10 @@
724
  "expirationmonth" => $invoice->expirationmonth,
725
  "expirationyear" => $invoice->expirationyear,
726
  "login_link" => pmpro_login_url(),
727
- "invoice_link" => pmpro_login_url(pmpro_url("invoice", "?invoice=" . $invoice->code)
728
- ));
 
 
729
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
730
  $invoice->billing->street,
731
  "", //address 2
@@ -784,7 +813,8 @@
784
  "membership_id" => $user->membership_level->id,
785
  "membership_level_name" => $user->membership_level->name,
786
  "siteemail" => pmpro_getOption("from_email"),
787
- "login_link" => pmpro_login_url(),
 
788
  "display_name" => $user->display_name,
789
  "user_email" => $user->user_email,
790
  "billing_amount" => pmpro_formatPrice($user->membership_level->billing_amount),
@@ -812,7 +842,7 @@
812
  $this->email = $user->user_email;
813
  $this->subject = sprintf(__("Your membership at %s has ended", "paid-memberships-pro"), get_option("blogname"));
814
 
815
- $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url(), "display_name" => $user->display_name, "user_email" => $user->user_email, "levels_link" => pmpro_url("levels"));
816
 
817
  $this->template = apply_filters("pmpro_email_template", "membership_expired", $this);
818
 
@@ -839,7 +869,7 @@
839
  $this->email = $user->user_email;
840
  $this->subject = sprintf(__("Your membership at %s will end soon", "paid-memberships-pro"), get_option("blogname"));
841
 
842
- $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "membership_id" => $user->membership_level->id, "membership_level_name" => $user->membership_level->name, "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url(), "enddate" => date_i18n(get_option('date_format'), $user->membership_level->enddate), "display_name" => $user->display_name, "user_email" => $user->user_email);
843
 
844
  $this->template = apply_filters("pmpro_email_template", "membership_expiring", $this);
845
 
@@ -860,30 +890,30 @@
860
 
861
  if(!empty($user->membership_level) && !empty($user->membership_level->name)) {
862
  $membership_level_name = $user->membership_level->name;
863
- $membership_level_id = '';
864
  } else {
865
  $membership_level_name = __('None', 'paid-memberships-pro');
866
- $membership_level_id = $user->membership_level->id;
867
  }
868
 
869
  $this->email = $user->user_email;
870
  $this->subject = sprintf(__("Your membership at %s has been changed", "paid-memberships-pro"), get_option("blogname"));
871
 
872
- $this->data = array("subject" => $this->subject, "name" => $user->display_name, "display_name" => $user->display_name, "user_login" => $user->user_login, "user_email" => $user->user_email, "sitename" => get_option("blogname"), "membership_id" => $membership_level_id, "membership_level_name" => $membership_level_name, "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url());
873
 
874
  if(!empty($user->membership_level) && !empty($user->membership_level->ID)) {
875
- $this->data["membership_change"] = sprintf(__("The new level is %s", 'paid-memberships-pro' ), $user->membership_level->name);
876
  } else {
877
- $this->data["membership_change"] = __("Your membership has been cancelled", "paid-memberships-pro");
878
  }
879
 
880
  if(!empty($user->membership_level->enddate))
881
  {
882
- $this->data["membership_change"] .= ". " . sprintf(__("This membership will expire on %s", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $user->membership_level->enddate));
883
  }
884
  elseif(!empty($this->expiration_changed))
885
  {
886
- $this->data["membership_change"] .= ". " . __("This membership does not expire", 'paid-memberships-pro' );
887
  }
888
 
889
  $this->template = apply_filters("pmpro_email_template", "admin_change", $this);
@@ -899,41 +929,36 @@
899
 
900
  if(!$user)
901
  return false;
902
-
903
- //check settings
904
- $send = pmpro_getOption("email_admin_changes");
905
- if(empty($send))
906
- return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
907
-
908
  //make sure we have the current membership level data
909
  $user->membership_level = pmpro_getMembershipLevelForUser($user->ID, true);
910
 
911
  if(!empty($user->membership_level) && !empty($user->membership_level->name)) {
912
  $membership_level_name = $user->membership_level->name;
913
- $membership_level_id = '';
914
  } else {
915
  $membership_level_name = __('None', 'paid-memberships-pro');
916
- $membership_level_id = $user->membership_level->id;
917
  }
918
 
919
  $this->email = get_bloginfo("admin_email");
920
  $this->subject = sprintf(__("Membership for %s at %s has been changed", "paid-memberships-pro"), $user->user_login, get_option("blogname"));
921
 
922
- $this->data = array("subject" => $this->subject, "name" => $user->display_name, "display_name" => $user->display_name, "user_login" => $user->user_login, "user_email" => $user->user_email, "sitename" => get_option("blogname"), "membership_id" => $membership_level_id, "membership_level_name" => $membership_level_name, "siteemail" => get_bloginfo("admin_email"), "login_link" => pmpro_login_url());
923
 
924
  if(!empty($user->membership_level) && !empty($user->membership_level->ID)) {
925
- $this->data["membership_change"] = sprintf(__("The new level is %s", 'paid-memberships-pro' ), $user->membership_level->name);
926
  } else {
927
- $this->data["membership_change"] = __("Membership has been cancelled", 'paid-memberships-pro' );
928
  }
929
 
930
  if(!empty($user->membership_level) && !empty($user->membership_level->enddate))
931
  {
932
- $this->data["membership_change"] .= ". " . sprintf(__("This membership will expire on %s", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $user->membership_level->enddate));
933
  }
934
  elseif(!empty($this->expiration_changed))
935
  {
936
- $this->data["membership_change"] .= ". " . __("This membership does not expire", 'paid-memberships-pro' );
937
  }
938
 
939
  $this->template = apply_filters("pmpro_email_template", "admin_change_admin", $this);
@@ -964,7 +989,7 @@
964
  $level = pmpro_getLevel($order->membership_id);
965
 
966
  $this->email = $user->user_email;
967
- $this->subject = __('Invoice for Order #: ', 'paid-memberships-pro') . $order->code;
968
 
969
  // Load invoice template
970
  if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-email.php' ) ) {
@@ -984,7 +1009,10 @@
984
  $this->data = array(
985
  'order_code' => $order->code,
986
  'login_link' => pmpro_login_url(),
 
987
  'invoice_link' => pmpro_login_url(pmpro_url("invoice", "?invoice=" . $order->code)),
 
 
988
  'invoice' => $invoice
989
  );
990
 
@@ -1024,6 +1052,7 @@
1024
  "user_login" => $user->user_login,
1025
  "sitename" => get_option("blogname"),
1026
  "siteemail" => pmpro_getOption("from_email"),
 
1027
  "invoice_url" => $invoice_url,
1028
  );
1029
 
@@ -1062,39 +1091,10 @@
1062
  "sitename" => get_option("blogname"),
1063
  "siteemail" => pmpro_getOption("from_email"),
1064
  "user_email" => $user->user_email,
 
1065
  "invoice_url" => $invoice_url,
1066
  );
1067
 
1068
  return $this->sendEmail();
1069
  }
1070
-
1071
-
1072
- /**
1073
- * Load the text for each default email template.
1074
- * This overrides the old /email/*.html templates.
1075
- */
1076
- function getDefaultEmailTemplate( $template = null ) {
1077
- if( empty( $template ) && !empty( $this->template ) )
1078
- $template = $this->template;
1079
-
1080
- if( empty( $template ) )
1081
- return false;
1082
-
1083
- $r = '';
1084
-
1085
- switch($template) {
1086
- case "admin_change":
1087
- $r = __( "<p>An administrator at !!sitename!! has changed your membership level.</p>
1088
-
1089
- <p>!!membership_change!!.</p>
1090
-
1091
- <p>If you did not request this membership change and would like more information please contact us at !!siteemail!!</p>
1092
-
1093
- <p>Log in to your membership account here: !!login_link!!</p>", 'paid-memberships-pro' );
1094
- break;
1095
- //repeat above for each template
1096
- }
1097
-
1098
- return $r;
1099
- }
1100
  }
1
  <?php
2
  class PMProEmail
3
  {
4
+
5
  function __construct()
6
  {
7
  $this->email = $this->from = $this->fromname = $this->subject = $this->template = $this->data = $this->body = NULL;
8
  }
9
 
10
  function sendEmail($email = NULL, $from = NULL, $fromname = NULL, $subject = NULL, $template = NULL, $data = NULL)
11
+ {
12
  //if values were passed
13
  if($email)
14
  $this->email = $email;
22
  $this->template = $template;
23
  if($data)
24
  $this->data = $data;
25
+
26
+ // If email is disabled don't send it.
27
+ // Note option may have 'false' stored as a string.
28
+ $template_disabled = pmpro_getOption( 'email_' . $this->template . '_disabled' );
29
+ if ( ! empty( $template_disabled ) && $template_disabled !== 'false' ) {
30
+ return false;
31
+ }
32
+
33
  //default values
34
+ global $current_user, $pmpro_email_templates_defaults;
35
  if(!$this->email)
36
  $this->email = $current_user->user_email;
37
 
40
 
41
  if(!$this->fromname)
42
  $this->fromname = pmpro_getOption("from_name");
 
 
 
 
 
 
43
 
44
  if(!$this->template)
45
  $this->template = "default";
46
+
47
+ //Okay let's get the subject stuff.
48
+ $template_subject = pmpro_getOption( 'email_' . $this->template . '_subject' );
49
+ if ( ! empty( $template_subject ) ) {
50
+ $this->subject = $template_subject;
51
+ } elseif ( empty( $this->subject ) ) {
52
+ $this->subject = ! empty( $pmpro_email_templates_defaults[$this->template]['subject'] ) ? sanitize_text_field( $pmpro_email_templates_defaults[$this->template]['subject'] ) : sprintf(__("An Email From %s", 'paid-memberships-pro' ), get_option("blogname"));
53
+ }
54
+
55
+ //decode the subject line in case there are apostrophes/etc in it
56
+ $this->subject = html_entity_decode($this->subject, ENT_QUOTES, 'UTF-8');
57
 
58
  $this->headers = array("Content-Type: text/html");
59
 
62
  //load the template
63
  $locale = apply_filters("plugin_locale", get_locale(), "paid-memberships-pro");
64
 
65
+ if( empty( $this->data['body'] ) && ! empty( pmpro_getOption( 'email_' . $this->template . '_body' ) ) )
66
+ $this->body = pmpro_getOption( 'email_' . $this->template . '_body' );
67
+ elseif(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $locale . "/" . $this->template . ".html"))
68
  $this->body = file_get_contents(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $locale . "/" . $this->template . ".html"); //localized email folder in child theme
69
  elseif(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $this->template . ".html"))
70
  $this->body = file_get_contents(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $this->template . ".html"); //email folder in child theme
82
  $this->body = file_get_contents(WP_LANG_DIR . '/pmpro/email/' . $this->template . ".html"); //email folder in WP language folder
83
  elseif(file_exists(PMPRO_DIR . "/languages/email/" . $locale . "/" . $this->template . ".html"))
84
  $this->body = file_get_contents(PMPRO_DIR . "/languages/email/" . $locale . "/" . $this->template . ".html"); //email folder in PMPro language folder
85
+ elseif( empty( $this->data['body'] ) && ! empty( $pmpro_email_templates_defaults[$this->template]['body'] ) )
86
+ $this->body = $pmpro_email_templates_defaults[$this->template]['body']; //default template in plugin
 
 
87
  elseif(!empty($this->data) && !empty($this->data['body']))
88
  $this->body = $this->data['body']; //data passed in
89
 
90
+
91
+ // Get template header.
92
+ if( pmpro_getOption( 'email_header_disabled' ) != 'true' ) {
93
+ $email_header = pmpro_email_templates_get_template_body('header');
94
+ } else {
95
+ $email_header = '';
 
 
 
96
  }
97
+
98
+ // Get template footer
99
+ if( pmpro_getOption( 'email_footer_disabled' ) != 'true' ) {
100
+ $email_footer = pmpro_email_templates_get_template_body('footer');
101
+ } else {
102
+ $email_footer = '';
103
+ }
104
+
105
+ // Add header and footer to email body.
106
+ $this->body = $email_header . $this->body . $email_footer;
107
+
108
  //if data is a string, assume we mean to replace !!body!! with it
109
  if(is_string($this->data))
110
  $this->data = array("body"=>$data);
112
  //filter for data
113
  $this->data = apply_filters("pmpro_email_data", $this->data, $this); //filter
114
 
115
+ //swap data into body and subject line
116
  if(is_array($this->data))
117
  {
118
  foreach($this->data as $key => $value)
119
  {
120
  if ( 'body' != $key ) {
121
  $this->body = str_replace("!!" . $key . "!!", $value, $this->body);
122
+ $this->subject = str_replace("!!" . $key . "!!", $value, $this->subject);
123
  }
124
  }
125
  }
215
  if(!$user)
216
  return false;
217
 
 
 
 
 
 
218
  $this->email = get_bloginfo("admin_email");
219
  $this->subject = sprintf(__("Membership for %s at %s has been CANCELLED", 'paid-memberships-pro'), $user->user_login, get_option("blogname"));
220
 
221
+ $this->data = array("user_login" => $user->user_login, "user_email" => $user->user_email, "display_name" => $user->display_name, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url(), "login_url" => pmpro_login_url());
222
 
223
  if(!empty($old_level_id)) {
224
  if(!is_array($old_level_id))
251
 
252
  function sendCheckoutEmail($user = NULL, $invoice = NULL)
253
  {
254
+ global $wpdb, $current_user, $discount_code;
255
  if(!$user)
256
  $user = $current_user;
257
 
280
  "membership_level_confirmation_message" => $confirmation_message,
281
  "membership_cost" => pmpro_getLevelCost($user->membership_level),
282
  "login_link" => pmpro_login_url(),
283
+ "login_url" => pmpro_login_url(),
284
  "display_name" => $user->display_name,
285
  "user_email" => $user->user_email,
286
  );
287
+
288
+ // Figure out which template to use.
289
+ if ( empty( $this->template ) ) {
290
+ if( ! empty( $invoice ) && ! pmpro_isLevelFree( $user->membership_level ) ) {
291
+ if( $invoice->gateway == "paypalexpress") {
292
+ $this->template = "checkout_express";
293
+ } elseif( $invoice->gateway == "check" ) {
294
+ $this->template = "checkout_check";
295
+ } elseif( pmpro_isLevelTrial( $user->membership_level ) ) {
296
+ $this->template = "checkout_trial";
297
+ } else {
298
+ $this->template = "checkout_paid";
299
+ }
300
+ } elseif( pmpro_isLevelFree( $user->membership_level ) ) {
301
+ $this->template = "checkout_free";
302
+ } else {
303
+ $this->template = "checkout_freetrial";
304
+ }
305
+ }
306
+
307
+ $this->template = apply_filters( "pmpro_email_template", $this->template, $this );
308
+
309
+ // Gather data depending on template being used.
310
+ if( in_array( $this->template, array( 'checkout_express', 'checkout_check', 'checkout_trial', 'checkout_paid' ) ) ) {
311
+ if( $this->template === 'checkout_check' ) {
312
  $this->data["instructions"] = wpautop(pmpro_getOption("instructions"));
313
  }
314
+
 
 
 
 
 
 
 
315
  $this->data["invoice_id"] = $invoice->code;
316
  $this->data["invoice_total"] = pmpro_formatPrice($invoice->total);
317
  $this->data["invoice_date"] = date_i18n( get_option( 'date_format' ), $invoice->getTimestamp() );
335
  $invoice->billing->country,
336
  $invoice->billing->phone);
337
 
338
+ if( $invoice->getDiscountCode() ) {
339
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $invoice->discount_code->code . "</p>\n";
340
+ } else {
341
  $this->data["discount_code"] = "";
342
+ }
343
+ } elseif( $this->template === 'checkout_free' ) {
344
+ if( ! empty( $discount_code ) ) {
 
 
 
345
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $discount_code . "</p>\n";
346
+ } else {
347
+ $this->data["discount_code"] = "";
348
+ }
349
+ } elseif ( $this->template === 'checkout_freetrial' ) {
350
+ if( ! empty( $discount_code ) ) {
 
 
 
351
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $discount_code . "</p>\n";
352
+ } else {
353
+ $this->data["discount_code"] = "";
354
+ }
355
  }
356
 
357
  $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(CONVERT_TZ(enddate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND status = 'active' LIMIT 1");
358
+ if( $enddate ) {
359
  $this->data["membership_expiration"] = "<p>" . sprintf(__("This membership will expire on %s.", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $enddate)) . "</p>\n";
360
+ } else {
361
  $this->data["membership_expiration"] = "";
362
+ }
363
 
364
  return $this->sendEmail();
365
  }
366
 
367
  function sendCheckoutAdminEmail($user = NULL, $invoice = NULL)
368
  {
369
+ global $wpdb, $current_user, $discount_code;
370
  if(!$user)
371
  $user = $current_user;
372
 
373
  if(!$user)
374
  return false;
375
 
 
 
 
 
 
376
  $this->email = get_bloginfo("admin_email");
377
+ $this->subject = sprintf(__("Member checkout for %s at %s", 'paid-memberships-pro' ), $user->membership_level->name, get_option("blogname"));
378
 
379
  $this->data = array(
380
  "subject" => $this->subject,
386
  "membership_level_name" => $user->membership_level->name,
387
  "membership_cost" => pmpro_getLevelCost($user->membership_level),
388
  "login_link" => pmpro_login_url(),
389
+ "login_url" => pmpro_login_url(),
390
  "display_name" => $user->display_name,
391
  "user_email" => $user->user_email,
392
  );
393
 
394
+ // Figure out which template to use.
395
+ if ( empty( $this->template ) ) {
396
+ if( ! empty( $invoice ) && ! pmpro_isLevelFree( $user->membership_level ) ) {
397
+ if( $invoice->gateway == "paypalexpress") {
398
+ $this->template = "checkout_express_admin";
399
+ } elseif( $invoice->gateway == "check" ) {
400
+ $this->template = "checkout_check_admin";
401
+ } elseif( pmpro_isLevelTrial( $user->membership_level ) ) {
402
+ $this->template = "checkout_trial_admin";
403
+ } else {
404
+ $this->template = "checkout_paid_admin";
405
+ }
406
+ } elseif( pmpro_isLevelFree( $user->membership_level ) ) {
407
+ $this->template = "checkout_free_admin";
408
+ } else {
409
+ $this->template = "checkout_freetrial_admin";
410
+ }
411
+ }
412
+
413
+ $this->template = apply_filters( "pmpro_email_template", $this->template, $this );
414
+
415
+ // Gather data depending on template being used.
416
+ if( in_array( $this->template, array( 'checkout_express_admin', 'checkout_check_admin', 'checkout_trial_admin', 'checkout_paid_admin' ) ) ) {
417
  $this->data["invoice_id"] = $invoice->code;
418
  $this->data["invoice_total"] = pmpro_formatPrice($invoice->total);
419
  $this->data["invoice_date"] = date_i18n(get_option('date_format'), $invoice->getTimestamp());
437
  $invoice->billing->country,
438
  $invoice->billing->phone);
439
 
440
+ if( $invoice->getDiscountCode() ) {
441
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $invoice->discount_code->code . "</p>\n";
442
+ } else {
443
  $this->data["discount_code"] = "";
444
+ }
445
+ } elseif( $this->template === 'checkout_free_admin' ) {
446
+ if( ! empty( $discount_code ) ) {
 
 
 
447
  $this->data["discount_code"] = "<p>" . __("Discount Code", 'paid-memberships-pro' ) . ": " . $discount_code . "</p>\n";
448
+ } else {
449
+ $this->data["discount_code"] = "";
450
+ }
451
+ } elseif( $this->template === 'checkout_freetrial_admin' ) {
 
 
452
  $this->data["discount_code"] = "";
453
+ }
454
 
455
  $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(CONVERT_TZ(enddate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND status = 'active' LIMIT 1");
456
+ if( $enddate ) {
457
  $this->data["membership_expiration"] = "<p>" . sprintf(__("This membership will expire on %s.", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $enddate)) . "</p>\n";
458
+ } else {
459
  $this->data["membership_expiration"] = "";
460
+ }
461
 
462
  return $this->sendEmail();
463
  }
495
  "accountnumber" => hideCardNumber($invoice->accountnumber),
496
  "expirationmonth" => $invoice->expirationmonth,
497
  "expirationyear" => $invoice->expirationyear,
498
+ "login_link" => pmpro_login_url(),
499
+ "login_url" => pmpro_login_url(),
500
  );
501
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
502
  $invoice->billing->street,
521
  if(!$user || !$invoice)
522
  return false;
523
 
 
 
 
 
 
524
  $this->email = get_bloginfo("admin_email");
525
  $this->subject = sprintf(__("Billing information has been updated for %s at %s", "paid-memberships-pro"), $user->user_login, get_option("blogname"));
526
 
545
  "accountnumber" => hideCardNumber($invoice->accountnumber),
546
  "expirationmonth" => $invoice->expirationmonth,
547
  "expirationyear" => $invoice->expirationyear,
548
+ "login_link" => pmpro_login_url(),
549
+ "login_url" => pmpro_login_url(),
550
  );
551
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
552
  $invoice->billing->street,
570
 
571
  if(!$user || !$invoice)
572
  return false;
573
+
574
+ $membership_level = pmpro_getSpecificMembershipLevelForUser( $user->ID, $invoice->membership_id );
575
 
576
  $this->email = $user->user_email;
577
+ $this->subject = sprintf(__("Membership payment failed at %s", "paid-memberships-pro"), get_option("blogname"));
578
 
579
  $this->data = array(
580
  "subject" => $this->subject,
582
  "user_login" => $user->user_login,
583
  "sitename" => get_option("blogname"),
584
  "siteemail" => pmpro_getOption("from_email"),
585
+ "membership_id" => $membership_level->id,
586
+ "membership_level_name" => $membership_level->name,
587
  "display_name" => $user->display_name,
588
  "user_email" => $user->user_email,
589
  "billing_name" => $invoice->billing->name,
597
  "accountnumber" => hideCardNumber($invoice->accountnumber),
598
  "expirationmonth" => $invoice->expirationmonth,
599
  "expirationyear" => $invoice->expirationyear,
600
+ "login_link" => pmpro_login_url(pmpro_url("billing")),
601
+ "login_url" => pmpro_login_url(pmpro_url("billing")),
602
  );
603
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
604
  $invoice->billing->street,
620
  return false;
621
 
622
  $user = get_userdata($invoice->user_id);
623
+ $membership_level = pmpro_getSpecificMembershipLevelForUser( $user->ID, $invoice->membership_id );
624
 
625
  $this->email = $email;
626
+ $this->subject = sprintf(__("Membership payment failed For %s at %s", "paid-memberships-pro"), $user->display_name, get_option("blogname"));
627
 
628
  $this->data = array(
629
  "subject" => $this->subject,
631
  "user_login" => $user->user_login,
632
  "sitename" => get_option("blogname"),
633
  "siteemail" => pmpro_getOption("from_email"),
634
+ "membership_id" => $membership_level->id,
635
+ "membership_level_name" => $membership_level->name,
636
  "display_name" => $user->display_name,
637
  "user_email" => $user->user_email,
638
  "billing_name" => $invoice->billing->name,
646
  "accountnumber" => hideCardNumber($invoice->accountnumber),
647
  "expirationmonth" => $invoice->expirationmonth,
648
  "expirationyear" => $invoice->expirationyear,
649
+ "login_link" => pmpro_login_url( get_edit_user_link( $user->ID ) ),
650
+ "login_url" => pmpro_login_url( get_edit_user_link( $user->ID ) ),
651
  );
652
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
653
  $invoice->billing->street,
672
  return false;
673
 
674
  $this->email = $user->user_email;
675
+ $this->subject = sprintf(__("Credit card on file expiring soon at %s", "paid-memberships-pro"), get_option("blogname"));
676
 
677
  $this->data = array(
678
  "subject" => $this->subject,
695
  "accountnumber" => hideCardNumber($invoice->accountnumber),
696
  "expirationmonth" => $invoice->expirationmonth,
697
  "expirationyear" => $invoice->expirationyear,
698
+ "login_link" => pmpro_login_url(pmpro_url("billing")),
699
+ "login_url" => pmpro_login_url(pmpro_url("billing"))
700
  );
701
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
702
  $invoice->billing->street,
724
  $user->membership_level = pmpro_getMembershipLevelForUser($user->ID);
725
 
726
  $this->email = $user->user_email;
727
+ $this->subject = sprintf(__("Invoice for %s membership", "paid-memberships-pro"), get_option("blogname"));
728
 
729
  $this->data = array(
730
  "subject" => $this->subject,
751
  "expirationmonth" => $invoice->expirationmonth,
752
  "expirationyear" => $invoice->expirationyear,
753
  "login_link" => pmpro_login_url(),
754
+ "login_url" => pmpro_login_url(),
755
+ "invoice_link" => pmpro_login_url(pmpro_url("invoice", "?invoice=" . $invoice->code)),
756
+ "invoice_url" => pmpro_login_url(pmpro_url("invoice", "?invoice=" . $invoice->code))
757
+ );
758
  $this->data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
759
  $invoice->billing->street,
760
  "", //address 2
813
  "membership_id" => $user->membership_level->id,
814
  "membership_level_name" => $user->membership_level->name,
815
  "siteemail" => pmpro_getOption("from_email"),
816
+ "login_link" => pmpro_login_url(),
817
+ "login_url" => pmpro_login_url(),
818
  "display_name" => $user->display_name,
819
  "user_email" => $user->user_email,
820
  "billing_amount" => pmpro_formatPrice($user->membership_level->billing_amount),
842
  $this->email = $user->user_email;
843
  $this->subject = sprintf(__("Your membership at %s has ended", "paid-memberships-pro"), get_option("blogname"));
844
 
845
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url(), "login_url" => pmpro_login_url(), "display_name" => $user->display_name, "user_email" => $user->user_email, "levels_link" => pmpro_url("levels"), "levels_url" => pmpro_url("levels"));
846
 
847
  $this->template = apply_filters("pmpro_email_template", "membership_expired", $this);
848
 
869
  $this->email = $user->user_email;
870
  $this->subject = sprintf(__("Your membership at %s will end soon", "paid-memberships-pro"), get_option("blogname"));
871
 
872
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "membership_id" => $user->membership_level->id, "membership_level_name" => $user->membership_level->name, "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url(), "login_url" => pmpro_login_url(), "enddate" => date_i18n(get_option('date_format'), $user->membership_level->enddate), "display_name" => $user->display_name, "user_email" => $user->user_email);
873
 
874
  $this->template = apply_filters("pmpro_email_template", "membership_expiring", $this);
875
 
890
 
891
  if(!empty($user->membership_level) && !empty($user->membership_level->name)) {
892
  $membership_level_name = $user->membership_level->name;
893
+ $membership_level_id = $user->membership_level->id;
894
  } else {
895
  $membership_level_name = __('None', 'paid-memberships-pro');
896
+ $membership_level_id = '';
897
  }
898
 
899
  $this->email = $user->user_email;
900
  $this->subject = sprintf(__("Your membership at %s has been changed", "paid-memberships-pro"), get_option("blogname"));
901
 
902
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "display_name" => $user->display_name, "user_login" => $user->user_login, "user_email" => $user->user_email, "sitename" => get_option("blogname"), "membership_id" => $membership_level_id, "membership_level_name" => $membership_level_name, "siteemail" => pmpro_getOption("from_email"), "login_link" => pmpro_login_url(), "login_url" => pmpro_login_url());
903
 
904
  if(!empty($user->membership_level) && !empty($user->membership_level->ID)) {
905
+ $this->data["membership_change"] = sprintf(__("The new level is %s.", 'paid-memberships-pro' ), $user->membership_level->name);
906
  } else {
907
+ $this->data["membership_change"] = __("Your membership has been cancelled.", "paid-memberships-pro");
908
  }
909
 
910
  if(!empty($user->membership_level->enddate))
911
  {
912
+ $this->data["membership_change"] .= " " . sprintf(__("This membership will expire on %s.", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $user->membership_level->enddate));
913
  }
914
  elseif(!empty($this->expiration_changed))
915
  {
916
+ $this->data["membership_change"] .= " " . __("This membership does not expire.", 'paid-memberships-pro' );
917
  }
918
 
919
  $this->template = apply_filters("pmpro_email_template", "admin_change", $this);
929
 
930
  if(!$user)
931
  return false;
932
+
 
 
 
 
 
933
  //make sure we have the current membership level data
934
  $user->membership_level = pmpro_getMembershipLevelForUser($user->ID, true);
935
 
936
  if(!empty($user->membership_level) && !empty($user->membership_level->name)) {
937
  $membership_level_name = $user->membership_level->name;
938
+ $membership_level_id = $user->membership_level->id;
939
  } else {
940
  $membership_level_name = __('None', 'paid-memberships-pro');
941
+ $membership_level_id = '';
942
  }
943
 
944
  $this->email = get_bloginfo("admin_email");
945
  $this->subject = sprintf(__("Membership for %s at %s has been changed", "paid-memberships-pro"), $user->user_login, get_option("blogname"));
946
 
947
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "display_name" => $user->display_name, "user_login" => $user->user_login, "user_email" => $user->user_email, "sitename" => get_option("blogname"), "membership_id" => $membership_level_id, "membership_level_name" => $membership_level_name, "siteemail" => get_bloginfo("admin_email"), "login_link" => pmpro_login_url(), "login_url" => pmpro_login_url());
948
 
949
  if(!empty($user->membership_level) && !empty($user->membership_level->ID)) {
950
+ $this->data["membership_change"] = sprintf(__("The new level is %s.", 'paid-memberships-pro' ), $user->membership_level->name);
951
  } else {
952
+ $this->data["membership_change"] = __("Membership has been cancelled.", 'paid-memberships-pro' );
953
  }
954
 
955
  if(!empty($user->membership_level) && !empty($user->membership_level->enddate))
956
  {
957
+ $this->data["membership_change"] .= " " . sprintf(__("This membership will expire on %s.", 'paid-memberships-pro' ), date_i18n(get_option('date_format'), $user->membership_level->enddate));
958
  }
959
  elseif(!empty($this->expiration_changed))
960
  {
961
+ $this->data["membership_change"] .= " " . __("This membership does not expire.", 'paid-memberships-pro' );
962
  }
963
 
964
  $this->template = apply_filters("pmpro_email_template", "admin_change_admin", $this);
989
  $level = pmpro_getLevel($order->membership_id);
990
 
991
  $this->email = $user->user_email;
992
+ $this->subject = __('Invoice for order #: ', 'paid-memberships-pro') . $order->code;
993
 
994
  // Load invoice template
995
  if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/pages/orders-email.php' ) ) {
1009
  $this->data = array(
1010
  'order_code' => $order->code,
1011
  'login_link' => pmpro_login_url(),
1012
+ 'login_url' => pmpro_login_url(),
1013
  'invoice_link' => pmpro_login_url(pmpro_url("invoice", "?invoice=" . $order->code)),
1014
+ 'invoice_url' => pmpro_login_url(pmpro_url("invoice", "?invoice=" . $order->code)),
1015
+ "invoice_id" => $order->id,
1016
  'invoice' => $invoice
1017
  );
1018
 
1052
  "user_login" => $user->user_login,
1053
  "sitename" => get_option("blogname"),
1054
  "siteemail" => pmpro_getOption("from_email"),
1055
+ "invoice_link" => $invoice_url,
1056
  "invoice_url" => $invoice_url,
1057
  );
1058
 
1091
  "sitename" => get_option("blogname"),
1092
  "siteemail" => pmpro_getOption("from_email"),
1093
  "user_email" => $user->user_email,
1094
+ "invoice_link" => $invoice_url,
1095
  "invoice_url" => $invoice_url,
1096
  );
1097
 
1098
  return $this->sendEmail();
1099
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1100
  }
classes/gateways/class.pmprogateway.php CHANGED
@@ -20,7 +20,7 @@
20
  if(!pmpro_isLevelTrial($order->membership_level))
21
  {
22
  //subscription will start today with a 1 period trial
23
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
24
  $order->TrialBillingPeriod = $order->BillingPeriod;
25
  $order->TrialBillingFrequency = $order->BillingFrequency;
26
  $order->TrialBillingCycles = 1;
@@ -33,7 +33,7 @@
33
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
34
  {
35
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
36
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
37
  $order->TrialBillingCycles++;
38
 
39
  //add a billing cycle to make up for the trial, if applicable
@@ -43,7 +43,7 @@
43
  else
44
  {
45
  //add a period to the start date to account for the initial payment
46
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
47
  }
48
 
49
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
@@ -67,7 +67,7 @@
67
  if(!pmpro_isLevelTrial($order->membership_level))
68
  {
69
  //subscription will start today with a 1 period trial
70
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
71
  $order->TrialBillingPeriod = $order->BillingPeriod;
72
  $order->TrialBillingFrequency = $order->BillingFrequency;
73
  $order->TrialBillingCycles = 1;
@@ -80,7 +80,7 @@
80
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
81
  {
82
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
83
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
84
  $order->TrialBillingCycles++;
85
 
86
  //add a billing cycle to make up for the trial, if applicable
@@ -90,7 +90,7 @@
90
  else
91
  {
92
  //add a period to the start date to account for the initial payment
93
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
94
  }
95
 
96
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
20
  if(!pmpro_isLevelTrial($order->membership_level))
21
  {
22
  //subscription will start today with a 1 period trial
23
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
24
  $order->TrialBillingPeriod = $order->BillingPeriod;
25
  $order->TrialBillingFrequency = $order->BillingFrequency;
26
  $order->TrialBillingCycles = 1;
33
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
34
  {
35
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
36
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
37
  $order->TrialBillingCycles++;
38
 
39
  //add a billing cycle to make up for the trial, if applicable
43
  else
44
  {
45
  //add a period to the start date to account for the initial payment
46
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
47
  }
48
 
49
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
67
  if(!pmpro_isLevelTrial($order->membership_level))
68
  {
69
  //subscription will start today with a 1 period trial
70
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
71
  $order->TrialBillingPeriod = $order->BillingPeriod;
72
  $order->TrialBillingFrequency = $order->BillingFrequency;
73
  $order->TrialBillingCycles = 1;
80
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
81
  {
82
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
83
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
84
  $order->TrialBillingCycles++;
85
 
86
  //add a billing cycle to make up for the trial, if applicable
90
  else
91
  {
92
  //add a period to the start date to account for the initial payment
93
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
94
  }
95
 
96
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
classes/gateways/class.pmprogateway_authorizenet.php CHANGED
@@ -123,7 +123,7 @@ class PMProGateway_authorizenet extends PMProGateway
123
  <label for="transactionkey"><?php _e('Transaction Key', 'paid-memberships-pro' );?>:</label>
124
  </th>
125
  <td>
126
- <input type="text" id="transactionkey" name="transactionkey" value="<?php echo esc_attr($values['transactionkey'])?>" class="regular-text code" />
127
  </td>
128
  </tr>
129
  <tr class="gateway gateway_authorizenet" <?php if($gateway != "authorizenet") { ?>style="display: none;"<?php } ?>>
@@ -154,7 +154,7 @@ class PMProGateway_authorizenet extends PMProGateway
154
  if(!pmpro_isLevelTrial($order->membership_level))
155
  {
156
  //subscription will start today with a 1 period trial
157
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
158
  $order->TrialBillingPeriod = $order->BillingPeriod;
159
  $order->TrialBillingFrequency = $order->BillingFrequency;
160
  $order->TrialBillingCycles = 1;
@@ -167,7 +167,7 @@ class PMProGateway_authorizenet extends PMProGateway
167
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
168
  {
169
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
170
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
171
  $order->TrialBillingCycles++;
172
 
173
  //add a billing cycle to make up for the trial, if applicable
@@ -177,7 +177,7 @@ class PMProGateway_authorizenet extends PMProGateway
177
  else
178
  {
179
  //add a period to the start date to account for the initial payment
180
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
181
  }
182
 
183
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
@@ -201,7 +201,7 @@ class PMProGateway_authorizenet extends PMProGateway
201
  if(!pmpro_isLevelTrial($order->membership_level))
202
  {
203
  //subscription will start today with a 1 period trial
204
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
205
  $order->TrialBillingPeriod = $order->BillingPeriod;
206
  $order->TrialBillingFrequency = $order->BillingFrequency;
207
  $order->TrialBillingCycles = 1;
@@ -214,7 +214,7 @@ class PMProGateway_authorizenet extends PMProGateway
214
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
215
  {
216
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
217
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
218
  $order->TrialBillingCycles++;
219
 
220
  //add a billing cycle to make up for the trial, if applicable
@@ -224,7 +224,7 @@ class PMProGateway_authorizenet extends PMProGateway
224
  else
225
  {
226
  //add a period to the start date to account for the initial payment
227
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
228
  }
229
 
230
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
@@ -1097,4 +1097,4 @@ class PMProGateway_authorizenet extends PMProGateway
1097
  return substr($haystack,$start_position,$end_position-$start_position);
1098
  }
1099
  }
1100
- }
123
  <label for="transactionkey"><?php _e('Transaction Key', 'paid-memberships-pro' );?>:</label>
124
  </th>
125
  <td>
126
+ <input type="text" id="transactionkey" name="transactionkey" value="<?php echo esc_attr($values['transactionkey'])?>" autocomplete="off" class="regular-text code pmpro-admin-secure-key" />
127
  </td>
128
  </tr>
129
  <tr class="gateway gateway_authorizenet" <?php if($gateway != "authorizenet") { ?>style="display: none;"<?php } ?>>
154
  if(!pmpro_isLevelTrial($order->membership_level))
155
  {
156
  //subscription will start today with a 1 period trial
157
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
158
  $order->TrialBillingPeriod = $order->BillingPeriod;
159
  $order->TrialBillingFrequency = $order->BillingFrequency;
160
  $order->TrialBillingCycles = 1;
167
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
168
  {
169
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
170
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
171
  $order->TrialBillingCycles++;
172
 
173
  //add a billing cycle to make up for the trial, if applicable
177
  else
178
  {
179
  //add a period to the start date to account for the initial payment
180
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
181
  }
182
 
183
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
201
  if(!pmpro_isLevelTrial($order->membership_level))
202
  {
203
  //subscription will start today with a 1 period trial
204
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
205
  $order->TrialBillingPeriod = $order->BillingPeriod;
206
  $order->TrialBillingFrequency = $order->BillingFrequency;
207
  $order->TrialBillingCycles = 1;
214
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
215
  {
216
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
217
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
218
  $order->TrialBillingCycles++;
219
 
220
  //add a billing cycle to make up for the trial, if applicable
224
  else
225
  {
226
  //add a period to the start date to account for the initial payment
227
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
228
  }
229
 
230
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
1097
  return substr($haystack,$start_position,$end_position-$start_position);
1098
  }
1099
  }
1100
+ }
classes/gateways/class.pmprogateway_braintree.php CHANGED
@@ -329,7 +329,7 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
329
  <label for="braintree_privatekey"><?php _e('Private Key', 'paid-memberships-pro' );?>:</label>
330
  </th>
331
  <td>
332
- <input type="text" id="braintree_privatekey" name="braintree_privatekey" value="<?php echo esc_attr($values['braintree_privatekey'])?>" class="regular-text code" />
333
  </td>
334
  </tr>
335
  <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
@@ -337,7 +337,7 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
337
  <label for="braintree_encryptionkey"><?php _e('Client-Side Encryption Key', 'paid-memberships-pro' );?>:</label>
338
  </th>
339
  <td>
340
- <textarea id="braintree_encryptionkey" name="braintree_encryptionkey" rows="3" cols="50" class="large-text code"><?php echo esc_textarea($values['braintree_encryptionkey'])?></textarea>
341
  </td>
342
  </tr>
343
  <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
@@ -706,8 +706,7 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
706
  $this->customer = Braintree_Customer::find($customer_id);
707
 
708
  //update the customer address, description and card
709
- if(!empty($order->accountnumber))
710
- {
711
  //put data in array for Braintree API calls
712
  $update_array = array(
713
  'firstName' => $order->FirstName,
@@ -882,7 +881,7 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
882
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
883
 
884
  //convert to a profile start date
885
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
886
 
887
  //filter the start date
888
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
@@ -990,18 +989,17 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
990
 
991
  try {
992
  $webhookNotification = Braintree_WebhookNotification::parse( $_POST['bt_signature'], $_POST['bt_payload'] );
 
 
 
 
993
  } catch ( \Exception $e ) {
994
  // Don't do anything
995
  }
996
  }
997
 
998
  // Always cancel, even if Braintree fails
999
- $order->updateStatus("cancelled" );
1000
-
1001
- if ( Braintree_WebhookNotification::SUBSCRIPTION_CANCELED === $webhookNotification->kind ) {
1002
- // Return, we're already processing the cancellation
1003
- return true;
1004
- }
1005
 
1006
  //require a subscription id
1007
  if(empty($order->subscription_transaction_id))
@@ -1071,4 +1069,44 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
1071
  */
1072
  return apply_filters( 'pmpro_braintree_plan_id', 'pmpro_' . $level_id, $level_id );
1073
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1074
  }
329
  <label for="braintree_privatekey"><?php _e('Private Key', 'paid-memberships-pro' );?>:</label>
330
  </th>
331
  <td>
332
+ <input type="text" id="braintree_privatekey" name="braintree_privatekey" value="<?php echo esc_attr($values['braintree_privatekey'])?>" autocomplete="off" class="regular-text code pmpro-admin-secure-key" />
333
  </td>
334
  </tr>
335
  <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
337
  <label for="braintree_encryptionkey"><?php _e('Client-Side Encryption Key', 'paid-memberships-pro' );?>:</label>
338
  </th>
339
  <td>
340
+ <textarea id="braintree_encryptionkey" name="braintree_encryptionkey" autocomplete="off" rows="3" cols="50" class="large-text code pmpro-admin-secure-key"><?php echo esc_textarea($values['braintree_encryptionkey'])?></textarea>
341
  </td>
342
  </tr>
343
  <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
706
  $this->customer = Braintree_Customer::find($customer_id);
707
 
708
  //update the customer address, description and card
709
+ if( ! empty( $order->braintree ) && ! empty( $order->braintree->number ) ) {
 
710
  //put data in array for Braintree API calls
711
  $update_array = array(
712
  'firstName' => $order->FirstName,
881
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
882
 
883
  //convert to a profile start date
884
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp")));
885
 
886
  //filter the start date
887
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
989
 
990
  try {
991
  $webhookNotification = Braintree_WebhookNotification::parse( $_POST['bt_signature'], $_POST['bt_payload'] );
992
+ if ( Braintree_WebhookNotification::SUBSCRIPTION_CANCELED === $webhookNotification->kind ) {
993
+ // Return, we're already processing the cancellation
994
+ return true;
995
+ }
996
  } catch ( \Exception $e ) {
997
  // Don't do anything
998
  }
999
  }
1000
 
1001
  // Always cancel, even if Braintree fails
1002
+ $order->updateStatus("cancelled" );
 
 
 
 
 
1003
 
1004
  //require a subscription id
1005
  if(empty($order->subscription_transaction_id))
1069
  */
1070
  return apply_filters( 'pmpro_braintree_plan_id', 'pmpro_' . $level_id, $level_id );
1071
  }
1072
+
1073
+ function get_subscription( &$order ) {
1074
+ // Does order have a subscription?
1075
+ if ( empty( $order ) || empty( $order->subscription_transaction_id ) ) {
1076
+ return false;
1077
+ }
1078
+
1079
+ try {
1080
+ $subscription = Braintree_Subscription::find( $order->subscription_transaction_id );
1081
+ } catch ( Exception $e ) {
1082
+ $order->error = __( "Error getting subscription with Braintree:", 'paid-memberships-pro' ) . $e->getMessage();
1083
+ $order->shorterror = $order->error;
1084
+ return false;
1085
+ }
1086
+
1087
+ return $subscription;
1088
+ }
1089
+
1090
+ /**
1091
+ * Filter pmpro_next_payment to get date via API if possible
1092
+ */
1093
+ static function pmpro_next_payment( $timestamp, $user_id, $order_status ) {
1094
+ // Check that we have a user ID...
1095
+ if ( ! empty( $user_id ) ) {
1096
+ // Get last order...
1097
+ $order = new MemberOrder();
1098
+ $order->getLastMemberOrder( $user_id, $order_status );
1099
+
1100
+ // Check if this is a Braintree order with a subscription transaction id...
1101
+ if ( ! empty( $order->id ) && ! empty( $order->subscription_transaction_id ) && $order->gateway == "braintree" ) {
1102
+ // Get the subscription and return the next billing date.
1103
+ $subscription = $order->Gateway->get_subscription( $order );
1104
+ if ( ! empty( $subscription ) ) {
1105
+ $timestamp = $subscription->nextBillingDate->getTimestamp();
1106
+ }
1107
+ }
1108
+ }
1109
+
1110
+ return $timestamp;
1111
+ }
1112
  }
classes/gateways/class.pmprogateway_check.php CHANGED
@@ -176,7 +176,7 @@
176
  if(!pmpro_isLevelTrial($order->membership_level))
177
  {
178
  //subscription will start today with a 1 period trial
179
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
180
  $order->TrialBillingPeriod = $order->BillingPeriod;
181
  $order->TrialBillingFrequency = $order->BillingFrequency;
182
  $order->TrialBillingCycles = 1;
@@ -189,7 +189,7 @@
189
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
190
  {
191
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
192
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
193
  $order->TrialBillingCycles++;
194
 
195
  //add a billing cycle to make up for the trial, if applicable
@@ -199,7 +199,7 @@
199
  else
200
  {
201
  //add a period to the start date to account for the initial payment
202
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
203
  }
204
 
205
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
@@ -223,7 +223,7 @@
223
  if(!pmpro_isLevelTrial($order->membership_level))
224
  {
225
  //subscription will start today with a 1 period trial
226
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
227
  $order->TrialBillingPeriod = $order->BillingPeriod;
228
  $order->TrialBillingFrequency = $order->BillingFrequency;
229
  $order->TrialBillingCycles = 1;
@@ -236,7 +236,7 @@
236
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
237
  {
238
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
239
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
240
  $order->TrialBillingCycles++;
241
 
242
  //add a billing cycle to make up for the trial, if applicable
@@ -246,7 +246,7 @@
246
  else
247
  {
248
  //add a period to the start date to account for the initial payment
249
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
250
  }
251
 
252
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
176
  if(!pmpro_isLevelTrial($order->membership_level))
177
  {
178
  //subscription will start today with a 1 period trial
179
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
180
  $order->TrialBillingPeriod = $order->BillingPeriod;
181
  $order->TrialBillingFrequency = $order->BillingFrequency;
182
  $order->TrialBillingCycles = 1;
189
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
190
  {
191
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
192
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
193
  $order->TrialBillingCycles++;
194
 
195
  //add a billing cycle to make up for the trial, if applicable
199
  else
200
  {
201
  //add a period to the start date to account for the initial payment
202
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
203
  }
204
 
205
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
223
  if(!pmpro_isLevelTrial($order->membership_level))
224
  {
225
  //subscription will start today with a 1 period trial
226
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
227
  $order->TrialBillingPeriod = $order->BillingPeriod;
228
  $order->TrialBillingFrequency = $order->BillingFrequency;
229
  $order->TrialBillingCycles = 1;
236
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
237
  {
238
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
239
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
240
  $order->TrialBillingCycles++;
241
 
242
  //add a billing cycle to make up for the trial, if applicable
246
  else
247
  {
248
  //add a period to the start date to account for the initial payment
249
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
250
  }
251
 
252
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
classes/gateways/class.pmprogateway_cybersource.php CHANGED
@@ -102,7 +102,7 @@
102
  <label for="cybersource_securitykey"><?php _e('Transaction Security Key', 'paid-memberships-pro' );?>:</label>
103
  </th>
104
  <td>
105
- <textarea id="cybersource_securitykey" name="cybersource_securitykey" rows="3" cols="50" class="large-text code"><?php echo esc_textarea($values['cybersource_securitykey']);?></textarea>
106
  </td>
107
  </tr>
108
  <?php
@@ -123,7 +123,7 @@
123
  if(!pmpro_isLevelTrial($order->membership_level))
124
  {
125
  //subscription will start today with a 1 period trial
126
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
127
  $order->TrialBillingPeriod = $order->BillingPeriod;
128
  $order->TrialBillingFrequency = $order->BillingFrequency;
129
  $order->TrialBillingCycles = 1;
@@ -135,7 +135,7 @@
135
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
136
  {
137
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
138
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
139
  $order->TrialBillingCycles++;
140
  //add a billing cycle to make up for the trial, if applicable
141
  if($order->TotalBillingCycles)
@@ -144,7 +144,7 @@
144
  else
145
  {
146
  //add a period to the start date to account for the initial payment
147
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
148
  }
149
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
150
  return $this->subscribe($order);
@@ -167,7 +167,7 @@
167
  if(!pmpro_isLevelTrial($order->membership_level))
168
  {
169
  //subscription will start today with a 1 period trial
170
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
171
  $order->TrialBillingPeriod = $order->BillingPeriod;
172
  $order->TrialBillingFrequency = $order->BillingFrequency;
173
  $order->TrialBillingCycles = 1;
@@ -179,7 +179,7 @@
179
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
180
  {
181
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
182
- $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
183
  $order->TrialBillingCycles++;
184
  //add a billing cycle to make up for the trial, if applicable
185
  if(!empty($order->TotalBillingCycles))
@@ -188,7 +188,7 @@
188
  else
189
  {
190
  //add a period to the start date to account for the initial payment
191
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
192
  }
193
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
194
  if($this->subscribe($order))
@@ -572,7 +572,7 @@
572
  else
573
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
574
  //convert to a profile start date
575
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
576
  //filter the start date
577
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
578
  //convert back to days
102
  <label for="cybersource_securitykey"><?php _e('Transaction Security Key', 'paid-memberships-pro' );?>:</label>
103
  </th>
104
  <td>
105
+ <textarea id="cybersource_securitykey" name="cybersource_securitykey" autocomplete="off" rows="3" cols="50" class="large-text code pmpro-admin-secure-key"><?php echo esc_textarea($values['cybersource_securitykey']);?></textarea>
106
  </td>
107
  </tr>
108
  <?php
123
  if(!pmpro_isLevelTrial($order->membership_level))
124
  {
125
  //subscription will start today with a 1 period trial
126
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
127
  $order->TrialBillingPeriod = $order->BillingPeriod;
128
  $order->TrialBillingFrequency = $order->BillingFrequency;
129
  $order->TrialBillingCycles = 1;
135
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
136
  {
137
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
138
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
139
  $order->TrialBillingCycles++;
140
  //add a billing cycle to make up for the trial, if applicable
141
  if($order->TotalBillingCycles)
144
  else
145
  {
146
  //add a period to the start date to account for the initial payment
147
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
148
  }
149
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
150
  return $this->subscribe($order);
167
  if(!pmpro_isLevelTrial($order->membership_level))
168
  {
169
  //subscription will start today with a 1 period trial
170
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
171
  $order->TrialBillingPeriod = $order->BillingPeriod;
172
  $order->TrialBillingFrequency = $order->BillingFrequency;
173
  $order->TrialBillingCycles = 1;
179
  elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
180
  {
181
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
182
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
183
  $order->TrialBillingCycles++;
184
  //add a billing cycle to make up for the trial, if applicable
185
  if(!empty($order->TotalBillingCycles))
188
  else
189
  {
190
  //add a period to the start date to account for the initial payment
191
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
192
  }
193
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
194
  if($this->subscribe($order))
572
  else
573
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
574
  //convert to a profile start date
575
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp")));
576
  //filter the start date
577
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
578
  //convert back to days
classes/gateways/class.pmprogateway_payflowpro.php CHANGED
@@ -163,7 +163,7 @@
163
  if($authorization_id)
164
  {
165
  $this->void($order, $authorization_id);
166
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
167
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
168
  return $this->subscribe($order);
169
  }
@@ -182,7 +182,7 @@
182
  //set up recurring billing
183
  if(pmpro_isLevelRecurring($order->membership_level))
184
  {
185
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
186
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
187
  if($this->subscribe($order))
188
  {
@@ -320,7 +320,22 @@
320
 
321
  //paypal profile stuff
322
  $nvpStr = "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  $nvpStr .="&AMT=" . $amount . "&TAXAMT=" . $amount_tax . "&CURRENCY=" . $pmpro_currency;
 
324
  /* PayFlow Pro doesn't use IPN so this is a little confusing */
325
  // $nvpStr .= "&NOTIFYURL=" . urlencode( add_query_arg( 'action', 'ipnhandler', admin_url('admin-ajax.php') ) );
326
  //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
@@ -390,7 +405,19 @@
390
 
391
  //paypal profile stuff
392
  $nvpStr = "&ACTION=A";
 
 
 
 
 
 
 
 
 
 
 
393
  $nvpStr .="&AMT=" . $amount . "&TAXAMT=" . $amount_tax . "&CURRENCY=" . $pmpro_currency;
 
394
  /* PayFlow Pro doesn't use IPN so this is a little confusing */
395
  // $nvpStr .= "&NOTIFYURL=" . urlencode( add_query_arg( 'action', 'ipnhandler', admin_url('admin-ajax.php') ) );
396
  //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
@@ -426,7 +453,7 @@
426
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
427
 
428
  //convert to a profile start date
429
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
430
 
431
  //filter the start date
432
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
@@ -449,7 +476,7 @@
449
  }
450
 
451
  //convert back into a date
452
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
453
 
454
  //start date
455
  $nvpStr .= "&START=" . date_i18n("mdY", strtotime($order->ProfileStartDate));
@@ -497,6 +524,17 @@
497
 
498
  //paypal profile stuff
499
  $nvpStr = "&ORIGPROFILEID=" . $order->subscription_transaction_id . "&ACTION=M";
 
 
 
 
 
 
 
 
 
 
 
500
  /* PayFlow Pro doesn't use IPN so this is a little confusing */
501
  // $nvpStr .= "&NOTIFYURL=" . urlencode( add_query_arg( 'action', 'ipnhandler', admin_url('admin-ajax.php') ) );
502
 
163
  if($authorization_id)
164
  {
165
  $this->void($order, $authorization_id);
166
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
167
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
168
  return $this->subscribe($order);
169
  }
182
  //set up recurring billing
183
  if(pmpro_isLevelRecurring($order->membership_level))
184
  {
185
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
186
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
187
  if($this->subscribe($order))
188
  {
320
 
321
  //paypal profile stuff
322
  $nvpStr = "";
323
+
324
+ // Only add CARDONFILE for initial charge if it's recurring.
325
+ if ( pmpro_isLevelRecurring( $order->membership_level ) ) {
326
+ /*
327
+ * Card on File is now required.
328
+ *
329
+ * CITR: The customer just performed an action to make the transaction.
330
+ * MITR: The customer passively approved during CITR to make this subsequent (recurring) transaction.
331
+ *
332
+ * @link https://developer.paypal.com/docs/payflow/integration-guide/card-on-file/
333
+ */
334
+ $nvpStr .= "&CARDONFILE=CITR";
335
+ }
336
+
337
  $nvpStr .="&AMT=" . $amount . "&TAXAMT=" . $amount_tax . "&CURRENCY=" . $pmpro_currency;
338
+
339
  /* PayFlow Pro doesn't use IPN so this is a little confusing */
340
  // $nvpStr .= "&NOTIFYURL=" . urlencode( add_query_arg( 'action', 'ipnhandler', admin_url('admin-ajax.php') ) );
341
  //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
405
 
406
  //paypal profile stuff
407
  $nvpStr = "&ACTION=A";
408
+
409
+ /*
410
+ * Card on File is now required.
411
+ *
412
+ * CITR: The customer just performed an action to make the transaction.
413
+ * MITR: The customer passively approved during CITR to make this subsequent (recurring) transaction.
414
+ *
415
+ * @link https://developer.paypal.com/docs/payflow/integration-guide/card-on-file/
416
+ */
417
+ $nvpStr .="&CARDONFILE=CITR";
418
+
419
  $nvpStr .="&AMT=" . $amount . "&TAXAMT=" . $amount_tax . "&CURRENCY=" . $pmpro_currency;
420
+
421
  /* PayFlow Pro doesn't use IPN so this is a little confusing */
422
  // $nvpStr .= "&NOTIFYURL=" . urlencode( add_query_arg( 'action', 'ipnhandler', admin_url('admin-ajax.php') ) );
423
  //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
453
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
454
 
455
  //convert to a profile start date
456
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp")));
457
 
458
  //filter the start date
459
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
476
  }
477
 
478
  //convert back into a date
479
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp")));
480
 
481
  //start date
482
  $nvpStr .= "&START=" . date_i18n("mdY", strtotime($order->ProfileStartDate));
524
 
525
  //paypal profile stuff
526
  $nvpStr = "&ORIGPROFILEID=" . $order->subscription_transaction_id . "&ACTION=M";
527
+
528
+ /*
529
+ * Card on File is now required.
530
+ *
531
+ * CITR: The customer just performed an action to make the transaction.
532
+ * MITR: The customer passively approved during CIT to make this subsequent (recurring) transaction.
533
+ *
534
+ * @link https://developer.paypal.com/docs/payflow/integration-guide/card-on-file/
535
+ */
536
+ $nvpStr .= "&CARDONFILE=CITR";
537
+
538
  /* PayFlow Pro doesn't use IPN so this is a little confusing */
539
  // $nvpStr .= "&NOTIFYURL=" . urlencode( add_query_arg( 'action', 'ipnhandler', admin_url('admin-ajax.php') ) );
540
 
classes/gateways/class.pmprogateway_paypal.php CHANGED
@@ -169,7 +169,7 @@
169
  <label for="apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
170
  </th>
171
  <td>
172
- <input type="text" id="apipassword" name="apipassword" value="<?php echo esc_attr($values['apipassword'])?>" class="regular-text code" />
173
  </td>
174
  </tr>
175
  <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
@@ -305,7 +305,7 @@
305
  if($authorization_id)
306
  {
307
  $this->void($order, $authorization_id);
308
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
309
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
310
  return $this->subscribe($order);
311
  }
@@ -324,7 +324,7 @@
324
  //set up recurring billing
325
  if(pmpro_isLevelRecurring($order->membership_level))
326
  {
327
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
328
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
329
  if($this->subscribe($order))
330
  {
@@ -601,8 +601,16 @@
601
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&COUNTRYCODE=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&SHIPTOPHONENUM=" . $order->billing->phone;
602
  }
603
 
 
 
 
 
 
604
  //for debugging let's add this to the class object
605
  $this->nvpStr = $nvpStr;
 
 
 
606
 
607
  $this->httpParsedResponseAr = $this->PPHttpPost('CreateRecurringPaymentsProfile', $nvpStr);
608
 
@@ -625,7 +633,7 @@
625
  {
626
  //paypal profile stuff
627
  $nvpStr = "";
628
- $nvpStr .= "&PROFILEID=" . $order->subscription_transaction_id;
629
 
630
  //credit card fields
631
  if($order->cardtype == "American Express")
@@ -635,26 +643,26 @@
635
 
636
  //credit card fields
637
  if($cardtype)
638
- $nvpStr .= "&CREDITCARDTYPE=" . $cardtype . "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->ExpirationDate . "&CVV2=" . $order->CVV2;
639
 
640
  //Maestro/Solo card fields. (Who uses these?) :)
641
  if($order->StartDate)
642
- $nvpStr .= "&STARTDATE=" . $order->StartDate . "&ISSUENUMBER=" . $order->IssueNumber;
643
 
644
  // Name and email info
645
  if ( $order->FirstName && $order->LastName && $order->Email ) {
646
- $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName;
647
  }
648
 
649
  //billing address, etc
650
  if($order->Address1)
651
  {
652
- $nvpStr .= "&STREET=" . $order->Address1;
653
 
654
  if($order->Address2)
655
- $nvpStr .= "&STREET2=" . $order->Address2;
656
 
657
- $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&COUNTRYCODE=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&SHIPTOPHONENUM=" . $order->billing->phone;
658
  }
659
 
660
  $this->httpParsedResponseAr = $this->PPHttpPost('UpdateRecurringPaymentsProfile', $nvpStr);
169
  <label for="apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
170
  </th>
171
  <td>
172
+ <input type="text" id="apipassword" name="apipassword" value="<?php echo esc_attr($values['apipassword'])?>" autocomplete="off" class="regular-text code pmpro-admin-secure-key" />
173
  </td>
174
  </tr>
175
  <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
305
  if($authorization_id)
306
  {
307
  $this->void($order, $authorization_id);
308
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
309
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
310
  return $this->subscribe($order);
311
  }
324
  //set up recurring billing
325
  if(pmpro_isLevelRecurring($order->membership_level))
326
  {
327
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
328
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
329
  if($this->subscribe($order))
330
  {
601
  $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&COUNTRYCODE=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&SHIPTOPHONENUM=" . $order->billing->phone;
602
  }
603
 
604
+ // Set MAXFAILEDPAYMENTS so subscriptions are cancelled after 1 failed payment.
605
+ $nvpStr .= "&MAXFAILEDPAYMENTS=1";
606
+
607
+ $nvpStr = apply_filters("pmpro_create_recurring_payments_profile_nvpstr", $nvpStr, $order);
608
+
609
  //for debugging let's add this to the class object
610
  $this->nvpStr = $nvpStr;
611
+
612
+ ///echo str_replace("&", "&<br />", $nvpStr);
613
+ ///exit;
614
 
615
  $this->httpParsedResponseAr = $this->PPHttpPost('CreateRecurringPaymentsProfile', $nvpStr);
616
 
633
  {
634
  //paypal profile stuff
635
  $nvpStr = "";
636
+ $nvpStr .= "&PROFILEID=" . urlencode( $order->subscription_transaction_id );
637
 
638
  //credit card fields
639
  if($order->cardtype == "American Express")
643
 
644
  //credit card fields
645
  if($cardtype)
646
+ $nvpStr .= "&CREDITCARDTYPE=" . urlencode( $cardtype ) . "&ACCT=" . urlencode( $order->accountnumber ) . "&EXPDATE=" . urlencode( $order->ExpirationDate ) . "&CVV2=" . urlencode( $order->CVV2 );
647
 
648
  //Maestro/Solo card fields. (Who uses these?) :)
649
  if($order->StartDate)
650
+ $nvpStr .= "&STARTDATE=" . urlencode( $order->StartDate ) . "&ISSUENUMBER=" . urlencode( $order->IssueNumber );
651
 
652
  // Name and email info
653
  if ( $order->FirstName && $order->LastName && $order->Email ) {
654
+ $nvpStr .= "&EMAIL=" . urlencode( $order->Email ) . "&FIRSTNAME=" . urlencode( $order->FirstName ) . "&LASTNAME=" . urlencode( $order->LastName );
655
  }
656
 
657
  //billing address, etc
658
  if($order->Address1)
659
  {
660
+ $nvpStr .= "&STREET=" . urlencode( $order->Address1 );
661
 
662
  if($order->Address2)
663
+ $nvpStr .= "&STREET2=" . urlencode( $order->Address2 );
664
 
665
+ $nvpStr .= "&CITY=" . urlencode( $order->billing->city ) . "&STATE=" . urlencode( $order->billing->state ) . "&COUNTRYCODE=" . urlencode( $order->billing->country ) . "&ZIP=" . urlencode( $order->billing->zip ) . "&SHIPTOPHONENUM=" . urlencode( $order->billing->phone );
666
  }
667
 
668
  $this->httpParsedResponseAr = $this->PPHttpPost('UpdateRecurringPaymentsProfile', $nvpStr);
classes/gateways/class.pmprogateway_paypalexpress.php CHANGED
@@ -7,6 +7,16 @@
7
 
8
  class PMProGateway_paypalexpress extends PMProGateway
9
  {
 
 
 
 
 
 
 
 
 
 
10
  function __construct($gateway = NULL)
11
  {
12
  $this->gateway = $gateway;
@@ -178,7 +188,7 @@
178
  <label for="apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
179
  </th>
180
  <td>
181
- <input type="text" id="apipassword" name="apipassword" value="<?php echo esc_attr($values['apipassword'])?>" class="regular-text code" />
182
  </td>
183
  </tr>
184
  <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
@@ -273,7 +283,7 @@
273
  else
274
  $username = "";
275
  if(isset($_REQUEST['password']))
276
- $password = sanitize_text_field($_REQUEST['password']);
277
  else
278
  $password = "";
279
  if(isset($_REQUEST['bemail']))
@@ -322,22 +332,28 @@
322
 
323
  $morder = new MemberOrder();
324
  $morder->getMemberOrderByPayPalToken(sanitize_text_field($_REQUEST['token']));
325
- $morder->Token = $morder->paypal_token; $pmpro_paypal_token = $morder->paypal_token;
326
- if($morder->Token)
327
- {
328
- if($morder->Gateway->getExpressCheckoutDetails($morder))
329
  {
330
- $pmpro_review = true;
 
 
 
 
 
 
 
 
331
  }
332
  else
333
  {
334
- $pmpro_msg = $morder->error;
335
  $pmpro_msgt = "pmpro_error";
336
  }
337
- }
338
- else
339
- {
340
- $pmpro_msg = __("The PayPal Token was lost.", 'paid-memberships-pro' );
341
  $pmpro_msgt = "pmpro_error";
342
  }
343
  }
@@ -358,7 +374,7 @@
358
  $morder->discount_code = $discount_code;
359
  $morder->InitialPayment = pmpro_round_price( $pmpro_level->initial_payment );
360
  $morder->PaymentAmount = pmpro_round_price( $pmpro_level->billing_amount );
361
- $morder->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
362
  $morder->BillingPeriod = $pmpro_level->cycle_period;
363
  $morder->BillingFrequency = $pmpro_level->cycle_number;
364
  $morder->Email = $bemail;
@@ -415,9 +431,15 @@
415
  if(!$current_user->ID)
416
  {
417
  //reload the user fields
418
- $new_user_array['user_login'] = $_SESSION['pmpro_signup_username'];
419
- $new_user_array['user_pass'] = $_SESSION['pmpro_signup_password'];
420
- $new_user_array['user_email'] = $_SESSION['pmpro_signup_email'];
 
 
 
 
 
 
421
 
422
  //unset the user fields in session
423
  unset($_SESSION['pmpro_signup_username']);
@@ -437,8 +459,10 @@
437
  {
438
  $order->payment_type = "PayPal Express";
439
  $order->cardtype = "";
440
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
441
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
 
 
442
 
443
  return $this->setExpressCheckout($order);
444
  }
@@ -452,8 +476,10 @@
452
  {
453
  if(pmpro_isLevelRecurring($order->membership_level))
454
  {
455
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
456
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
 
 
457
  return $this->subscribe($order);
458
  }
459
  else
@@ -572,6 +598,15 @@
572
  //update order
573
  $order->saveOrder();
574
 
 
 
 
 
 
 
 
 
 
575
  //redirect to paypal
576
  $paypal_url = "https://www.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=" . $this->httpParsedResponseAr['TOKEN'];
577
  $environment = pmpro_getOption("gateway_environment");
@@ -585,7 +620,6 @@
585
 
586
  //exit('SetExpressCheckout Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
587
  } else {
588
- $order->status = "error";
589
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
590
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
591
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
@@ -619,7 +653,6 @@
619
 
620
  return true;
621
  } else {
622
- $order->status = "error";
623
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
624
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
625
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
@@ -673,7 +706,6 @@
673
 
674
  return true;
675
  } else {
676
- $order->status = "error";
677
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
678
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
679
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
@@ -684,7 +716,7 @@
684
 
685
  function subscribe(&$order)
686
  {
687
- global $pmpro_currency;
688
 
689
  if(empty($order->code))
690
  $order->code = $order->getRandomCode();
@@ -729,8 +761,12 @@
729
  if(!empty($order->TrialBillingCycles))
730
  $nvpStr .= "&TRIALTOTALBILLINGCYCLES=" . $order->TrialBillingCycles;
731
 
 
 
 
732
  $nvpStr = apply_filters("pmpro_create_recurring_payments_profile_nvpstr", $nvpStr, $order);
733
 
 
734
  $this->nvpStr = $nvpStr;
735
 
736
  ///echo str_replace("&", "&<br />", $nvpStr);
@@ -739,16 +775,44 @@
739
  $this->httpParsedResponseAr = $this->PPHttpPost('CreateRecurringPaymentsProfile', $nvpStr);
740
 
741
  if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
742
- $order->status = "success";
743
- $order->payment_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
744
- $order->subscription_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
 
 
 
745
 
746
- //update order
747
- $order->saveOrder();
 
748
 
749
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
750
  } else {
751
- $order->status = "error";
 
 
752
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
753
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
754
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
@@ -786,7 +850,6 @@
786
  if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
787
  return true;
788
  } else {
789
- $order->status = "error";
790
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
791
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']) . ". " . __("Please contact the site owner or cancel your subscription from within PayPal to make sure you are not charged going forward.", 'paid-memberships-pro' );
792
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
@@ -814,7 +877,6 @@
814
  }
815
  else
816
  {
817
- $order->status = "error";
818
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
819
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
820
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
@@ -842,60 +904,76 @@
842
  return false;
843
 
844
  if( $order->payment_transaction_id == $order->subscription_transaction_id ){
845
- /** Initial payment **/
846
- $nvpStr = "";
847
- // STARTDATE is Required, even if useless here. Start from 24h before the order timestamp, to avoid timezone related issues.
848
- $nvpStr .= "&STARTDATE=" . urlencode( gmdate( DATE_W3C, $order->getTimestamp() - DAY_IN_SECONDS ) . 'Z' );
849
- // filter results by a specific transaction id.
850
- $nvpStr .= "&TRANSACTIONID=" . urlencode($order->subscription_transaction_id);
851
-
852
- $this->httpParsedResponseAr = $this->PPHttpPost('TransactionSearch', $nvpStr);
853
-
854
- if( ! in_array( strtoupper( $this->httpParsedResponseAr["ACK"] ), [ 'SUCCESS', 'SUCCESSWITHWARNING' ] ) ){
855
- // since we are using TRANSACTIONID=I-... which is NOT a transaction id,
856
- // paypal is returning an error. but the results are actually filtered by that transaction id, usually.
857
-
858
- // let's double check it.
859
- if( ! isset( $this->httpParsedResponseAr['L_TRANSACTIONID0'] ) ){
860
- // really no results? it's a real error.
861
- return false;
862
- }
863
  }
864
 
865
- $transaction_ids = [];
866
- for( $i = 0; $i < PHP_INT_MAX; $i++ ){
867
- // loop until we have results
868
- if( ! isset( $this->httpParsedResponseAr["L_TRANSACTIONID$i"] ) ){
869
- break;
870
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
871
 
872
- // ignore I-... results
873
- if( "I-" === substr( $this->httpParsedResponseAr["L_TRANSACTIONID$i"], 0 ,2 ) ){
874
- if( $order->subscription_transaction_id != $this->httpParsedResponseAr["L_TRANSACTIONID$i"] ){
875
- // if we got a result from another I- subscription transaction id,
876
- // then something changed into paypal responses.
877
- // var_dump( $this->httpParsedResponseAr, $this->httpParsedResponseAr["L_TRANSACTIONID$i"] );
878
- throw new Exception();
879
- }
880
 
881
- continue;
882
- }
 
883
 
884
- $transaction_ids[] = $this->httpParsedResponseAr["L_TRANSACTIONID$i"];
 
 
 
885
  }
 
886
 
887
- // no payment_transaction_ids in results
888
- if( empty( $transaction_ids ) ){
889
- return false;
 
 
890
  }
891
 
892
- // found the payment transaction id, it's the last one (the oldest)
893
- $payment_transaction_id = end( $transaction_ids );
894
- return $this->getTransactionDetails( $payment_transaction_id );
895
- }else{
896
- /** Recurring payment **/
897
- return $this->getTransactionDetails( $order->payment_transaction_id );
 
 
 
 
 
 
 
 
 
 
 
 
898
  }
 
 
 
 
 
899
  }
900
 
901
  function getTransactionDetails($payment_transaction_id)
@@ -956,13 +1034,50 @@
956
 
957
  /**
958
  * PAYPAL Function
959
- * Send HTTP POST Request
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
960
  *
961
  * @param string The API method name
962
  * @param string The POST Message fields in &name=value pair format
963
- * @return array Parsed HTTP Response body
964
  */
965
- function PPHttpPost($methodName_, $nvpStr_) {
966
  global $gateway_environment;
967
  $environment = $gateway_environment;
968
 
@@ -984,24 +1099,80 @@
984
  'timeout' => 60,
985
  'sslverify' => FALSE,
986
  'httpversion' => '1.1',
987
- 'body' => $nvpreq
 
 
 
 
988
  )
989
  );
990
 
991
  if ( is_wp_error( $response ) ) {
992
- $error_message = $response->get_error_message();
993
- die( "methodName_ failed: $error_message" );
994
- } else {
995
- //extract the response details
996
- $httpParsedResponseAr = array();
997
- parse_str(wp_remote_retrieve_body($response), $httpParsedResponseAr);
998
-
999
- //check for valid response
1000
- if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('ACK', $httpParsedResponseAr)) {
1001
- exit("Invalid HTTP Response for POST request($nvpreq) to $API_Endpoint.");
1002
- }
1003
  }
 
 
 
 
 
 
 
 
 
1004
 
1005
  return $httpParsedResponseAr;
1006
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1007
  }
7
 
8
  class PMProGateway_paypalexpress extends PMProGateway
9
  {
10
+ /** @var int Maximum number of request retries */
11
+ protected static $maxNetworkRetries = 5;
12
+
13
+ /** @var float Maximum delay between retries, in seconds */
14
+ protected static $maxNetworkRetryDelay = 2.0;
15
+
16
+ /** @var float Initial delay between retries, in seconds */
17
+ protected static $initialNetworkRetryDelay = 0.5;
18
+
19
+
20
  function __construct($gateway = NULL)
21
  {
22
  $this->gateway = $gateway;
188
  <label for="apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
189
  </th>
190
  <td>
191
+ <input type="text" id="apipassword" name="apipassword" value="<?php echo esc_attr($values['apipassword'])?>" autocomplete="off" class="regular-text code pmpro-admin-secure-key" />
192
  </td>
193
  </tr>
194
  <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
283
  else
284
  $username = "";
285
  if(isset($_REQUEST['password']))
286
+ $password = $_REQUEST['password'];
287
  else
288
  $password = "";
289
  if(isset($_REQUEST['bemail']))
332
 
333
  $morder = new MemberOrder();
334
  $morder->getMemberOrderByPayPalToken(sanitize_text_field($_REQUEST['token']));
335
+
336
+ if( $morder->status === 'token' ){
337
+ $morder->Token = $morder->paypal_token; $pmpro_paypal_token = $morder->paypal_token;
338
+ if($morder->Token)
339
  {
340
+ if($morder->Gateway->getExpressCheckoutDetails($morder))
341
+ {
342
+ $pmpro_review = true;
343
+ }
344
+ else
345
+ {
346
+ $pmpro_msg = $morder->error;
347
+ $pmpro_msgt = "pmpro_error";
348
+ }
349
  }
350
  else
351
  {
352
+ $pmpro_msg = __("The PayPal Token was lost.", 'paid-memberships-pro' );
353
  $pmpro_msgt = "pmpro_error";
354
  }
355
+ }else{
356
+ $pmpro_msg = __("Checkout was already processed.", 'paid-memberships-pro' );
 
 
357
  $pmpro_msgt = "pmpro_error";
358
  }
359
  }
374
  $morder->discount_code = $discount_code;
375
  $morder->InitialPayment = pmpro_round_price( $pmpro_level->initial_payment );
376
  $morder->PaymentAmount = pmpro_round_price( $pmpro_level->billing_amount );
377
+ $morder->ProfileStartDate = date_i18n("Y-m-d\TH:i:s");
378
  $morder->BillingPeriod = $pmpro_level->cycle_period;
379
  $morder->BillingFrequency = $pmpro_level->cycle_number;
380
  $morder->Email = $bemail;
431
  if(!$current_user->ID)
432
  {
433
  //reload the user fields
434
+ if( ! empty( $_SESSION['pmpro_signup_username'] ) ){
435
+ $new_user_array['user_login'] = $_SESSION['pmpro_signup_username'];
436
+ }
437
+ if( ! empty( $_SESSION['pmpro_signup_password'] ) ){
438
+ $new_user_array['user_pass'] = $_SESSION['pmpro_signup_password'];
439
+ }
440
+ if( ! empty( $_SESSION['pmpro_signup_email'] ) ){
441
+ $new_user_array['user_email'] = $_SESSION['pmpro_signup_email'];
442
+ }
443
 
444
  //unset the user fields in session
445
  unset($_SESSION['pmpro_signup_username']);
459
  {
460
  $order->payment_type = "PayPal Express";
461
  $order->cardtype = "";
462
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod));
463
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
464
+ // Convert to UTC for PayPal...
465
+ $order->ProfileStartDate = get_gmt_from_date( $order->ProfileStartDate, 'Y-m-d\TH:i:s\Z' );
466
 
467
  return $this->setExpressCheckout($order);
468
  }
476
  {
477
  if(pmpro_isLevelRecurring($order->membership_level))
478
  {
479
+ $order->ProfileStartDate = date_i18n("Y-m-d\TH:i:s", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp")));
480
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
481
+ // Convert to UTC for PayPal...
482
+ $order->ProfileStartDate = get_gmt_from_date( $order->ProfileStartDate, 'Y-m-d\TH:i:s\Z' );
483
  return $this->subscribe($order);
484
  }
485
  else
598
  //update order
599
  $order->saveOrder();
600
 
601
+ /**
602
+ * Allow performing actions just before sending the user to the gateway to complete the payment.
603
+ *
604
+ * @since 2.6.5
605
+ *
606
+ * @param MemberOrder $order The new order with status = token.
607
+ */
608
+ do_action( 'pmpro_before_commit_express_checkout', $order );
609
+
610
  //redirect to paypal
611
  $paypal_url = "https://www.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=" . $this->httpParsedResponseAr['TOKEN'];
612
  $environment = pmpro_getOption("gateway_environment");
620
 
621
  //exit('SetExpressCheckout Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
622
  } else {
 
623
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
624
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
625
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
653
 
654
  return true;
655
  } else {
 
656
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
657
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
658
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
706
 
707
  return true;
708
  } else {
 
709
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
710
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
711
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
716
 
717
  function subscribe(&$order)
718
  {
719
+ global $pmpro_currency, $pmpro_review;
720
 
721
  if(empty($order->code))
722
  $order->code = $order->getRandomCode();
761
  if(!empty($order->TrialBillingCycles))
762
  $nvpStr .= "&TRIALTOTALBILLINGCYCLES=" . $order->TrialBillingCycles;
763
 
764
+ // Set MAXFAILEDPAYMENTS so subscriptions are cancelled after 1 failed payment.
765
+ $nvpStr .= "&MAXFAILEDPAYMENTS=1";
766
+
767
  $nvpStr = apply_filters("pmpro_create_recurring_payments_profile_nvpstr", $nvpStr, $order);
768
 
769
+ //for debugging let's add this to the class object
770
  $this->nvpStr = $nvpStr;
771
 
772
  ///echo str_replace("&", "&<br />", $nvpStr);
775
  $this->httpParsedResponseAr = $this->PPHttpPost('CreateRecurringPaymentsProfile', $nvpStr);
776
 
777
  if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
778
+ // PayPal docs says that PROFILESTATUS can be:
779
+ // 1. ActiveProfile — The recurring payment profile has been successfully created and activated for scheduled payments according the billing instructions from the recurring payments profile.
780
+ // 2. PendingProfile — The system is in the process of creating the recurring payment profile. Please check your IPN messages for an update.
781
+ // Also, we have seen that PROFILESTATUS can be missing. That case would be an error.
782
+ if(isset($this->httpParsedResponseAr["PROFILESTATUS"]) && in_array($this->httpParsedResponseAr["PROFILESTATUS"], array("ActiveProfile", "PendingProfile"))) {
783
+ $order->status = "success";
784
 
785
+ // this is wrong, but we don't know the real transaction id at this point
786
+ $order->payment_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
787
+ $order->subscription_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
788
 
789
+ //update order
790
+ $order->saveOrder();
791
+
792
+ return true;
793
+ } else {
794
+ // stop processing the review request on checkout page
795
+ $pmpro_review = false;
796
+
797
+ $order->status = "error";
798
+
799
+ // this is wrong, but we don't know the real transaction id at this point
800
+ $order->payment_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
801
+ $order->subscription_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
802
+
803
+ $order->errorcode = '';
804
+ $order->error = __( 'Something went wrong creating plan with PayPal; missing PROFILESTATUS.', 'paid-memberships-pro' );
805
+ $order->shorterror = __( 'Error creating plan with PayPal.', 'paid-memberships-pro' );
806
+
807
+ //update order
808
+ $order->saveOrder();
809
+
810
+ return false;
811
+ }
812
  } else {
813
+ // stop processing the review request on checkout page
814
+ $pmpro_review = false;
815
+
816
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
817
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
818
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
850
  if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
851
  return true;
852
  } else {
 
853
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
854
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']) . ". " . __("Please contact the site owner or cancel your subscription from within PayPal to make sure you are not charged going forward.", 'paid-memberships-pro' );
855
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
877
  }
878
  else
879
  {
 
880
  $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
881
  $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
882
  $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
904
  return false;
905
 
906
  if( $order->payment_transaction_id == $order->subscription_transaction_id ){
907
+ $payment_transaction_id = $this->getRealPaymentTransactionId( $order );
908
+ if( ! $payment_transaction_id ){
909
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
910
  }
911
 
912
+ return $this->getTransactionDetails( $payment_transaction_id );
913
+ }else{
914
+ /** Recurring payment **/
915
+ return $this->getTransactionDetails( $order->payment_transaction_id );
916
+ }
917
+ }
918
+
919
+ /**
920
+ * Try to recover the real payment_transaction_id when payment_transaction_id === subscription_transaction_id === I-xxxxxxxx.
921
+ *
922
+ * @since 1.8.5
923
+ */
924
+ function getRealPaymentTransactionId(&$order)
925
+ {
926
+ /** Initial payment **/
927
+ $nvpStr = "";
928
+ // STARTDATE is Required, even if useless here. Start from 24h before the order timestamp, to avoid timezone related issues.
929
+ $nvpStr .= "&STARTDATE=" . urlencode( gmdate( DATE_W3C, $order->getTimestamp() - DAY_IN_SECONDS ) . 'Z' );
930
+ // filter results by a specific transaction id.
931
+ $nvpStr .= "&TRANSACTIONID=" . urlencode($order->subscription_transaction_id);
932
 
933
+ $this->httpParsedResponseAr = $this->PPHttpPost('TransactionSearch', $nvpStr);
 
 
 
 
 
 
 
934
 
935
+ if( ! in_array( strtoupper( $this->httpParsedResponseAr["ACK"] ), [ 'SUCCESS', 'SUCCESSWITHWARNING' ] ) ){
936
+ // since we are using TRANSACTIONID=I-... which is NOT a transaction id,
937
+ // paypal is returning an error. but the results are actually filtered by that transaction id, usually.
938
 
939
+ // let's double check it.
940
+ if( ! isset( $this->httpParsedResponseAr['L_TRANSACTIONID0'] ) ){
941
+ // really no results? it's a real error.
942
+ return false;
943
  }
944
+ }
945
 
946
+ $transaction_ids = [];
947
+ for( $i = 0; $i < PHP_INT_MAX; $i++ ){
948
+ // loop until we have results
949
+ if( ! isset( $this->httpParsedResponseAr["L_TRANSACTIONID$i"] ) ){
950
+ break;
951
  }
952
 
953
+ // ignore I-... results
954
+ if( "I-" === substr( $this->httpParsedResponseAr["L_TRANSACTIONID$i"], 0 ,2 ) ){
955
+ if( $order->subscription_transaction_id != $this->httpParsedResponseAr["L_TRANSACTIONID$i"] ){
956
+ // if we got a result from another I- subscription transaction id,
957
+ // then something changed into paypal responses.
958
+ // var_dump( $this->httpParsedResponseAr, $this->httpParsedResponseAr["L_TRANSACTIONID$i"] );
959
+ throw new Exception();
960
+ }
961
+
962
+ continue;
963
+ }
964
+
965
+ $transaction_ids[] = $this->httpParsedResponseAr["L_TRANSACTIONID$i"];
966
+ }
967
+
968
+ // no payment_transaction_ids in results
969
+ if( empty( $transaction_ids ) ){
970
+ return false;
971
  }
972
+
973
+ // found the payment transaction id, it's the last one (the oldest)
974
+ $payment_transaction_id = end( $transaction_ids );
975
+
976
+ return $payment_transaction_id;
977
  }
978
 
979
  function getTransactionDetails($payment_transaction_id)
1034
 
1035
  /**
1036
  * PAYPAL Function
1037
+ * Send HTTP POST Request with retries
1038
+ *
1039
+ * @param string The API method name
1040
+ * @param string The POST Message fields in &name=value pair format
1041
+ *
1042
+ * @return array Parsed HTTP Response body
1043
+ */
1044
+ function PPHttpPost( $methodName_, $nvpStr_ ) {
1045
+ // Create a UUID for this request, to enable idempotency which is supported
1046
+ // by PayPal Express even if it's legacy (thus it's not reported in the docs)
1047
+ // https://developer.paypal.com/docs/business/develop/idempotency/
1048
+ $uuid = self::RandomGenerator_uuid();
1049
+
1050
+ $numRetries = 0;
1051
+ do {
1052
+ $httpParsedResponseAr = $this->PPHttpPost_DontDieOnError( $methodName_, $nvpStr_, $uuid );
1053
+ if ( is_wp_error( $httpParsedResponseAr ) ) {
1054
+ $numRetries++;
1055
+ sleep( self::sleepTime( $numRetries ) );
1056
+ } else {
1057
+ break;
1058
+ }
1059
+ } while ( $numRetries <= self::$maxNetworkRetries );
1060
+
1061
+ // If we still have an error even with the retries, there's not much we can do.
1062
+ if ( is_wp_error( $httpParsedResponseAr ) ) {
1063
+ // exiting is never a good user experience and it's hard to debug, but we can
1064
+ // at least leave a trace in error log to make it easier to see this happening
1065
+ error_log( "Unable to complete $methodName_ request with $nvpStr_: " . $httpParsedResponseAr->get_error_message() );
1066
+ die( "Unable to complete $methodName_ request with $nvpStr_: " . $httpParsedResponseAr->get_error_message() );
1067
+ }
1068
+
1069
+ return $httpParsedResponseAr;
1070
+ }
1071
+
1072
+ /**
1073
+ * PAYPAL Function
1074
+ * Send HTTP POST Request with uuid
1075
  *
1076
  * @param string The API method name
1077
  * @param string The POST Message fields in &name=value pair format
1078
+ * @return array|\WP_Error Parsed HTTP Response body
1079
  */
1080
+ function PPHttpPost_DontDieOnError($methodName_, $nvpStr_, $uuid) {
1081
  global $gateway_environment;
1082
  $environment = $gateway_environment;
1083
 
1099
  'timeout' => 60,
1100
  'sslverify' => FALSE,
1101
  'httpversion' => '1.1',
1102
+ 'body' => $nvpreq,
1103
+ 'headers' => array(
1104
+ 'content-type' => 'application/json',
1105
+ 'PayPal-Request-Id' => $uuid,
1106
+ ),
1107
  )
1108
  );
1109
 
1110
  if ( is_wp_error( $response ) ) {
1111
+ return $response;
 
 
 
 
 
 
 
 
 
 
1112
  }
1113
+
1114
+ //extract the response details
1115
+ $httpParsedResponseAr = array();
1116
+ parse_str(wp_remote_retrieve_body($response), $httpParsedResponseAr);
1117
+
1118
+ //check for valid response
1119
+ if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('ACK', $httpParsedResponseAr)) {
1120
+ return new WP_Error( 1, "Invalid HTTP Response for POST request($nvpreq) to $API_Endpoint." );
1121
+ }
1122
 
1123
  return $httpParsedResponseAr;
1124
  }
1125
+
1126
+ /**
1127
+ * Provides the number of seconds to wait before retrying a request.
1128
+ * Inspired by Stripe\HttpClient\CurlClient::sleepTime
1129
+ *
1130
+ * @param int $numRetries
1131
+ *
1132
+ * @return int
1133
+ */
1134
+ public static function sleepTime( $numRetries ) {
1135
+ // Apply exponential backoff with $initialNetworkRetryDelay on the
1136
+ // number of $numRetries so far as inputs. Do not allow the number to exceed
1137
+ // $maxNetworkRetryDelay.
1138
+ $sleepSeconds = \min(
1139
+ self::$maxNetworkRetryDelay * 1.0 * 2 ** ( $numRetries - 1 ),
1140
+ self::$maxNetworkRetryDelay
1141
+ );
1142
+
1143
+ // Apply some jitter by randomizing the value in the range of
1144
+ // ($sleepSeconds / 2) to ($sleepSeconds).
1145
+ $sleepSeconds *= 0.5 * ( 1 + self::randFloat() );
1146
+
1147
+ // But never sleep less than the base sleep seconds.
1148
+ $sleepSeconds = \max( self::$initialNetworkRetryDelay, $sleepSeconds );
1149
+
1150
+ return $sleepSeconds;
1151
+ }
1152
+
1153
+ /**
1154
+ * Returns a random value between 0 and $max.
1155
+ * From Stripe\Util\RandomGenerator::randFloat()
1156
+ *
1157
+ * @param float $max (optional)
1158
+ *
1159
+ * @return float
1160
+ */
1161
+ public static function randFloat( $max = 1.0 ) {
1162
+ return \mt_rand() / \mt_getrandmax() * $max;
1163
+ }
1164
+
1165
+ /**
1166
+ * Returns a v4 UUID.
1167
+ * From Stripe\Util\RandomGenerator::uuid()
1168
+ *
1169
+ * @return string
1170
+ */
1171
+ public static function RandomGenerator_uuid() {
1172
+ $arr = \array_values( \unpack( 'N1a/n4b/N1c', \openssl_random_pseudo_bytes( 16 ) ) );
1173
+ $arr[2] = ( $arr[2] & 0x0fff ) | 0x4000;
1174
+ $arr[3] = ( $arr[3] & 0x3fff ) | 0x8000;
1175
+
1176
+ return \vsprintf( '%08x-%04x-%04x-%04x-%04x%08x', $arr );
1177
+ }
1178
  }
classes/gateways/class.pmprogateway_paypalstandard.php CHANGED
@@ -170,7 +170,7 @@
170
  <label for="apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
171
  </th>
172
  <td>
173
- <input type="text" id="apipassword" name="apipassword" size="60" value="<?php echo esc_attr($values['apipassword'])?>" />
174
  </td>
175
  </tr>
176
  <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
@@ -238,12 +238,12 @@
238
  ?>
239
  <span id="pmpro_paypalexpress_checkout" <?php if(($gateway != "paypalexpress" && $gateway != "paypalstandard") || !$pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
240
  <input type="hidden" name="submit-checkout" value="1" />
241
- <input type="image" value="<?php _e('Check Out with PayPal', 'paid-memberships-pro' );?> &raquo;" src="<?php echo apply_filters("pmpro_paypal_button_image", "https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-medium.png");?>" />
242
  </span>
243
 
244
  <span id="pmpro_submit_span" <?php if(($gateway == "paypalexpress" || $gateway == "paypalstandard") && $pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
245
  <input type="hidden" name="submit-checkout" value="1" />
246
- <input type="submit" class="<?php echo pmpro_get_element_class( 'pmpro_btn pmpro_btn-submit-checkout', 'pmpro_btn-submit-checkout' ); ?>" value="<?php if($pmpro_requirebilling) { _e('Submit and Check Out', 'paid-memberships-pro' ); } else { _e('Submit and Confirm', 'paid-memberships-pro' );}?> &raquo;" />
247
  </span>
248
  <?php
249
 
@@ -372,7 +372,8 @@
372
  'notify_url' => add_query_arg( 'action', 'ipnhandler', admin_url("admin-ajax.php") ),
373
  'src' => '1',
374
  'sra' => '1',
375
- 'bn' => PAYPAL_BN_CODE
 
376
  );
377
 
378
  //trial?
170
  <label for="apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
171
  </th>
172
  <td>
173
+ <input type="text" id="apipassword" name="apipassword" size="60" value="<?php echo esc_attr($values['apipassword'])?>" autocomplete="off" class="regular-text code pmpro-admin-secure-key" />
174
  </td>
175
  </tr>
176
  <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
238
  ?>
239
  <span id="pmpro_paypalexpress_checkout" <?php if(($gateway != "paypalexpress" && $gateway != "paypalstandard") || !$pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
240
  <input type="hidden" name="submit-checkout" value="1" />
241
+ <input type="image" id="pmpro_btn-submit-paypalstandard" class="<?php echo pmpro_get_element_class( 'pmpro_btn-submit-checkout' ); ?>" value="<?php _e('Check Out with PayPal', 'paid-memberships-pro' );?> &raquo;" src="<?php echo apply_filters("pmpro_paypal_button_image", "https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-medium.png");?>" />
242
  </span>
243
 
244
  <span id="pmpro_submit_span" <?php if(($gateway == "paypalexpress" || $gateway == "paypalstandard") && $pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
245
  <input type="hidden" name="submit-checkout" value="1" />
246
+ <input type="submit" id="pmpro_btn-submit" class="<?php echo pmpro_get_element_class( 'pmpro_btn pmpro_btn-submit-checkout', 'pmpro_btn-submit-checkout' ); ?>" value="<?php if($pmpro_requirebilling) { _e('Submit and Check Out', 'paid-memberships-pro' ); } else { _e('Submit and Confirm', 'paid-memberships-pro' );}?> &raquo;" />
247
  </span>
248
  <?php
249
 
372
  'notify_url' => add_query_arg( 'action', 'ipnhandler', admin_url("admin-ajax.php") ),
373
  'src' => '1',
374
  'sra' => '1',
375
+ 'bn' => PAYPAL_BN_CODE,
376
+ 'MAXFAILEDPAYMENTS' => 1
377
  );
378
 
379
  //trial?
classes/gateways/class.pmprogateway_stripe.php CHANGED
@@ -3,14 +3,17 @@
3
  use Stripe\Customer as Stripe_Customer;
4
  use Stripe\Invoice as Stripe_Invoice;
5
  use Stripe\Plan as Stripe_Plan;
 
 
6
  use Stripe\Charge as Stripe_Charge;
7
  use Stripe\PaymentIntent as Stripe_PaymentIntent;
8
  use Stripe\SetupIntent as Stripe_SetupIntent;
9
- use Stripe\Source as Stripe_Source;
10
  use Stripe\PaymentMethod as Stripe_PaymentMethod;
11
  use Stripe\Subscription as Stripe_Subscription;
 
12
  use Stripe\WebhookEndpoint as Stripe_Webhook;
13
  use Stripe\StripeClient as Stripe_Client; // Used for deleting webhook as of 2.4
 
14
 
15
  define( "PMPRO_STRIPE_API_VERSION", "2020-03-02" );
16
 
@@ -48,66 +51,30 @@ class PMProGateway_stripe extends PMProGateway {
48
 
49
  if ( true === $this->dependencies() ) {
50
  $this->loadStripeLibrary();
51
- Stripe\Stripe::setApiKey( pmpro_getOption( "stripe_secretkey" ) );
52
  Stripe\Stripe::setAPIVersion( PMPRO_STRIPE_API_VERSION );
 
 
 
 
 
 
53
  self::$is_loaded = true;
54
  }
55
 
56
  return $this->gateway;
57
  }
58
 
59
- /**
60
- * Warn if required extensions aren't loaded.
61
- *
62
- * @return bool
63
- * @since 1.8.6.8.1
64
- * @since 1.8.13.6 - Add json dependency
65
- */
66
- public static function dependencies() {
67
- global $msg, $msgt, $pmpro_stripe_error;
68
-
69
- if ( version_compare( PHP_VERSION, '5.3.29', '<' ) ) {
70
-
71
- $pmpro_stripe_error = true;
72
- $msg = - 1;
73
- $msgt = sprintf( __( "The Stripe Gateway requires PHP 5.3.29 or greater. We recommend upgrading to PHP %s or greater. Ask your host to upgrade.", "paid-memberships-pro" ), PMPRO_PHP_MIN_VERSION );
74
-
75
- if ( ! is_admin() ) {
76
- pmpro_setMessage( $msgt, "pmpro_error" );
77
- }
78
-
79
- return false;
80
- }
81
-
82
- $modules = array( 'curl', 'mbstring', 'json' );
83
-
84
- foreach ( $modules as $module ) {
85
- if ( ! extension_loaded( $module ) ) {
86
- $pmpro_stripe_error = true;
87
- $msg = - 1;
88
- $msgt = sprintf( __( "The %s gateway depends on the %s PHP extension. Please enable it, or ask your hosting provider to enable it.", 'paid-memberships-pro' ), 'Stripe', $module );
89
-
90
- //throw error on checkout page
91
- if ( ! is_admin() ) {
92
- pmpro_setMessage( $msgt, 'pmpro_error' );
93
- }
94
-
95
- return false;
96
- }
97
- }
98
-
99
- self::$is_loaded = true;
100
-
101
- return true;
102
- }
103
-
104
  /**
105
  * Load the Stripe API library.
106
  *
107
  * @since 1.8
108
  * Moved into a method in version 1.8 so we only load it when needed.
109
  */
110
- function loadStripeLibrary() {
111
  //load Stripe library if it hasn't been loaded already (usually by another plugin using Stripe)
112
  if ( ! class_exists( "Stripe\Stripe" ) ) {
113
  require_once( PMPRO_DIR . "/includes/lib/Stripe/init.php" );
@@ -119,7 +86,7 @@ class PMProGateway_stripe extends PMProGateway {
119
  *
120
  * @since 1.8
121
  */
122
- static function init() {
123
  //make sure Stripe is a gateway option
124
  add_filter( 'pmpro_gateways', array( 'PMProGateway_stripe', 'pmpro_gateways' ) );
125
 
@@ -140,7 +107,12 @@ class PMProGateway_stripe extends PMProGateway {
140
  //old global RE showing billing address or not
141
  global $pmpro_stripe_lite;
142
  $pmpro_stripe_lite = apply_filters( "pmpro_stripe_lite", ! pmpro_getOption( "stripe_billingaddress" ) ); //default is oposite of the stripe_billingaddress setting
143
- add_filter( 'pmpro_required_billing_fields', array( 'PMProGateway_stripe', 'pmpro_required_billing_fields' ) );
 
 
 
 
 
144
 
145
  //updates cron
146
  add_action( 'pmpro_cron_stripe_subscription_updates', array(
@@ -151,6 +123,7 @@ class PMProGateway_stripe extends PMProGateway {
151
  //AJAX services for creating/disabling webhooks
152
  add_action( 'wp_ajax_pmpro_stripe_create_webhook', array( 'PMProGateway_stripe', 'wp_ajax_pmpro_stripe_create_webhook' ) );
153
  add_action( 'wp_ajax_pmpro_stripe_delete_webhook', array( 'PMProGateway_stripe', 'wp_ajax_pmpro_stripe_delete_webhook' ) );
 
154
 
155
  /*
156
  Filter pmpro_next_payment to get actual value
@@ -197,7 +170,13 @@ class PMProGateway_stripe extends PMProGateway {
197
  ) );
198
  }
199
 
 
200
  add_action( 'init', array( 'PMProGateway_stripe', 'clear_saved_subscriptions' ) );
 
 
 
 
 
201
  }
202
 
203
  /**
@@ -232,7 +211,7 @@ class PMProGateway_stripe extends PMProGateway {
232
  *
233
  * @since 1.8
234
  */
235
- static function pmpro_gateways( $gateways ) {
236
  if ( empty( $gateways['stripe'] ) ) {
237
  $gateways['stripe'] = __( 'Stripe', 'paid-memberships-pro' );
238
  }
@@ -245,20 +224,27 @@ class PMProGateway_stripe extends PMProGateway {
245
  *
246
  * @since 1.8
247
  */
248
- static function getGatewayOptions() {
249
  $options = array(
250
  'sslseal',
251
  'nuclear_HTTPS',
252
  'gateway_environment',
253
  'stripe_secretkey',
254
  'stripe_publishablekey',
 
 
 
 
 
 
255
  'stripe_webhook',
256
  'stripe_billingaddress',
257
  'currency',
258
  'use_ssl',
259
  'tax_state',
260
  'tax_rate',
261
- 'accepted_credit_cards'
 
262
  );
263
 
264
  return $options;
@@ -269,7 +255,7 @@ class PMProGateway_stripe extends PMProGateway {
269
  *
270
  * @since 1.8
271
  */
272
- static function pmpro_payment_options( $options ) {
273
  //get stripe options
274
  $stripe_options = self::getGatewayOptions();
275
 
@@ -284,120 +270,197 @@ class PMProGateway_stripe extends PMProGateway {
284
  *
285
  * @since 1.8
286
  */
287
- static function pmpro_payment_option_fields( $values, $gateway ) {
288
-
289
- if ( ! empty( $values['stripe_publishablekey'] ) && ! empty( $values['stripe_secretkey'] ) ) {
290
-
291
- // Check if webhook is enabled or not.
292
- $webhook = self::get_webhook_ids( $values['stripe_secretkey'] );
293
 
294
- if ( ! $webhook ) {
295
- $stripe = new PMProGateway_stripe;
296
- $webhook = $stripe::does_webhook_exist();
297
- }
298
-
299
- $required_update = false;
300
- // Check to see if events are missing.
301
- if ( is_array( $webhook ) ) {
302
 
303
- if ( $webhook['webhook_id'] == false ) {
304
- $required_update = true;
305
- }
306
 
307
- if ( isset( $webhook['enabled_events'] ) ) {
 
308
  $events = self::check_missing_webhook_events( $webhook['enabled_events'] );
309
-
310
  if ( $events ) {
311
- $required_update = true;
312
- } else {
313
- $required_update = false;
314
- self::update_webhook_ids( $webhook['webhook_id'], $values['stripe_secretkey'] );
315
- pmpro_setOption( 'stripe_webhook', 1 );
316
- $values['stripe_webhook'] = 1; // Checkbox option.
317
  }
318
  }
319
 
320
- } else if ( ! empty( $webhook ) && ! pmpro_getOption( 'stripe_webhook', true ) ) {
321
- pmpro_setOption( 'stripe_webhook', 1 ); // Checkbox option.
322
- $values['stripe_webhook'] = 1;
323
- } else {
324
- $require_update = true;
325
  }
326
 
327
- }
328
-
329
  ?>
330
- <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
331
- <th><?php _e( 'Stripe API Version', 'paid-memberships-pro' ); ?>:</th>
332
- <td><code><?php echo PMPRO_STRIPE_API_VERSION; ?></code></td>
333
- </tr>
334
- <tr class="pmpro_settings_divider gateway gateway_stripe"
335
- <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
336
- <td colspan="2">
337
  <hr />
338
- <h2><?php _e( 'Stripe Settings', 'paid-memberships-pro' ); ?></h2>
339
- </td>
340
- </tr>
341
- <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
342
- <th scope="row" valign="top">
343
- <label for="stripe_publishablekey"><?php _e( 'Publishable Key', 'paid-memberships-pro' ); ?>:</label>
344
- </th>
345
- <td>
346
- <input type="text" id="stripe_publishablekey" name="stripe_publishablekey" value="<?php echo esc_attr( $values['stripe_publishablekey'] ) ?>" class="regular-text code" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  <?php
348
  $public_key_prefix = substr( $values['stripe_publishablekey'], 0, 3 );
349
  if ( ! empty( $values['stripe_publishablekey'] ) && $public_key_prefix != 'pk_' ) {
350
  ?>
351
- <p class="pmpro_red"><strong><?php _e( 'Your Publishable Key appears incorrect.', 'paid-memberships-pro' ); ?></strong></p>
352
  <?php
353
  }
354
  ?>
355
- </td>
356
- </tr>
357
- <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
358
- <th scope="row" valign="top">
359
- <label for="stripe_secretkey"><?php _e( 'Secret Key', 'paid-memberships-pro' ); ?>:</label>
360
- </th>
361
- <td>
362
- <input type="text" id="stripe_secretkey" name="stripe_secretkey" value="<?php echo esc_attr( $values['stripe_secretkey'] ) ?>" class="regular-text code" />
363
- </td>
364
- </tr>
365
- <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
366
- <th scope="row" valign="top">
367
- <label><?php _e( 'Webhook', 'paid-memberships-pro' ); ?>:</label>
368
- </th>
369
- <td>
370
- <?php if ( self::does_webhook_exist() ) { ?>
371
  <button type="button" id="pmpro_stripe_create_webhook" class="button button-secondary" style="display: none;"><span class="dashicons dashicons-update-alt"></span> <?php _e( 'Create Webhook' ,'paid-memberships-pro' ); ?></button>
372
- <div class="notice notice-success inline">
373
- <p id="pmpro_stripe_webhook_notice">Your webhook is enabled. <a id="pmpro_stripe_delete_webhook" href="#">Disable Webhook</a></p>
374
- </div>
375
- <?php } else { ?>
376
- <button type="button" id="pmpro_stripe_create_webhook" class="button button-secondary"><span class="dashicons dashicons-update-alt"></span> <?php _e( 'Create Webhook' ,'paid-memberships-pro' ); ?></button>
377
- <div class="notice error inline">
378
- <p id="pmpro_stripe_webhook_notice"><?php _e('A webhook in Stripe is required to process recurring payments, manage failed payments, and synchronize cancellations.', 'paid-memberships-pro' );?></p>
379
- </div>
380
- <?php } ?>
381
- <p class="description"><?php esc_html_e( 'Webhook URL', 'paid-memberships-pro' ); ?>:
382
- <code><?php echo self::get_site_webhook_url(); ?></code></p>
383
- </td>
384
- </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
386
- <th scope="row" valign="top">
387
- <label for="stripe_billingaddress"><?php _e( 'Show Billing Address Fields', 'paid-memberships-pro' ); ?>
388
- :</label>
389
- </th>
390
- <td>
391
- <select id="stripe_billingaddress" name="stripe_billingaddress">
392
- <option value="0"
393
- <?php if ( empty( $values['stripe_billingaddress'] ) ) { ?>selected="selected"<?php } ?>><?php _e( 'No', 'paid-memberships-pro' ); ?></option>
394
- <option value="1"
395
- <?php if ( ! empty( $values['stripe_billingaddress'] ) ) { ?>selected="selected"<?php } ?>><?php _e( 'Yes', 'paid-memberships-pro' ); ?></option>
396
- </select>
 
 
 
397
  <p class="description"><?php _e( "Stripe doesn't require billing address fields. Choose 'No' to hide them on the checkout page.<br /><strong>If No, make sure you disable address verification in the Stripe dashboard settings.</strong>", 'paid-memberships-pro' ); ?></p>
398
- </td>
399
- </tr>
400
- <?php if ( ! function_exists( 'pmproappe_pmpro_valid_gateways' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  $allowed_appe_html = array (
402
  'a' => array (
403
  'href' => array(),
@@ -417,48 +480,43 @@ class PMProGateway_stripe extends PMProGateway {
417
  /**
418
  * AJAX callback to create webhooks.
419
  */
420
- static function wp_ajax_pmpro_stripe_create_webhook() {
421
  $secretkey = sanitize_text_field( $_REQUEST['secretkey'] );
422
 
423
  $stripe = new PMProGateway_stripe();
424
  Stripe\Stripe::setApiKey( $secretkey );
425
 
426
- $r = $stripe::update_webhook_events();
427
-
428
- if ( empty( $r ) ) {
 
429
  $r = array(
430
  'success' => false,
431
  'notice' => 'error',
432
- 'message' => __( 'Webhook creation failed. You might already have a webhook set up.', 'paid-memberships-pro' ),
433
- 'response' => $r
434
  );
435
  } else {
436
- if ( is_wp_error( $r ) ) {
437
- $r = array(
438
- 'success' => false,
439
- 'notice' => 'error',
440
- 'message' => $r->get_error_message(),
441
- 'response' => $r
442
- );
443
- } else {
444
- $r = array(
445
- 'success' => true,
446
- 'notice' => 'notice-success',
447
- 'message' => __( 'Your webhook is enabled.', 'paid-memberships-pro' ),
448
- 'response' => $r
449
- );
450
- }
451
  }
452
 
453
- echo json_encode( $r );
454
-
455
- exit;
 
 
 
456
  }
457
 
458
  /**
459
  * AJAX callback to disable webhooks.
460
  */
461
- static function wp_ajax_pmpro_stripe_delete_webhook() {
462
  $secretkey = sanitize_text_field( $_REQUEST['secretkey'] );
463
 
464
  $stripe = new PMProGateway_stripe();
@@ -466,43 +524,48 @@ class PMProGateway_stripe extends PMProGateway {
466
 
467
  $webhook = self::does_webhook_exist();
468
 
469
- if ( empty( $webhook ) ) {
470
- $r = array(
471
- 'success' => true,
472
- 'notice' => 'error',
473
- 'message' => __( 'A webhook in Stripe is required to process recurring payments, manage failed payments, and synchronize cancellations.', 'paid-memberships-pro' )
474
- );
475
- } else {
476
- $r = $stripe::delete_webhook( $webhook, $secretkey );
477
-
478
- if ( is_wp_error( $r ) ) {
479
  $r = array(
480
  'success' => false,
481
  'notice' => 'error',
482
- 'message' => $r->get_error_message(),
483
- 'response' => $r
484
  );
485
- } else {
486
- if ( ! empty( $r['deleted'] ) && $r['deleted'] == true ) {
487
- $r = array(
488
- 'success' => true,
489
- 'notice' => 'error',
490
- 'message' => __( 'A webhook in Stripe is required to process recurring payments, manage failed payments, and synchronize cancellations.', 'paid-memberships-pro' ),
491
- 'response' => $r
492
- );
493
- } else {
494
- $r = array(
495
- 'success' => false,
496
- 'notice' => 'error',
497
- 'message' => __( 'There was an error deleting the webhook.', 'paid-memberships-pro' ),
498
- 'response' => $r
499
- );
500
- }
 
 
 
 
 
 
 
501
  }
502
  }
503
 
504
  echo json_encode( $r );
505
-
506
  exit;
507
  }
508
 
@@ -511,9 +574,8 @@ class PMProGateway_stripe extends PMProGateway {
511
  *
512
  * @since 1.8
513
  */
514
- static function pmpro_checkout_after_preheader( $order ) {
515
-
516
- global $gateway, $pmpro_level, $current_user, $pmpro_requirebilling, $pmpro_pages;
517
 
518
  $default_gateway = pmpro_getOption( "gateway" );
519
 
@@ -522,22 +584,26 @@ class PMProGateway_stripe extends PMProGateway {
522
  wp_enqueue_script( "stripe", "https://js.stripe.com/v3/", array(), null );
523
 
524
  if ( ! function_exists( 'pmpro_stripe_javascript' ) ) {
525
-
526
  $localize_vars = array(
527
- 'publishableKey' => pmpro_getOption( 'stripe_publishablekey' ),
 
528
  'verifyAddress' => apply_filters( 'pmpro_stripe_verify_address', pmpro_getOption( 'stripe_billingaddress' ) ),
529
  'ajaxUrl' => admin_url( "admin-ajax.php" ),
530
  'msgAuthenticationValidated' => __( 'Verification steps confirmed. Your payment is processing.', 'paid-memberships-pro' ),
531
  'pmpro_require_billing' => $pmpro_requirebilling,
 
 
 
 
 
532
  );
533
 
534
  if ( ! empty( $order ) ) {
535
- if ( ! empty( $order->Gateway->payment_intent ) ) {
536
- $localize_vars['paymentIntent'] = $order->Gateway->payment_intent;
537
  }
538
- if ( ! empty( $order->Gateway->setup_intent ) ) {
539
- $localize_vars['setupIntent'] = $order->Gateway->setup_intent;
540
- $localize_vars['subscription'] = $order->Gateway->subscription;
541
  }
542
  }
543
 
@@ -555,7 +621,7 @@ class PMProGateway_stripe extends PMProGateway {
555
  * Don't require the CVV.
556
  * Don't require address fields if they are set to hide.
557
  */
558
- static function pmpro_required_billing_fields( $fields ) {
559
  global $pmpro_stripe_lite, $current_user, $bemail, $bconfirmemail;
560
 
561
  //CVV is not required if set that way at Stripe. The Stripe JS will require it if it is required.
@@ -589,389 +655,84 @@ class PMProGateway_stripe extends PMProGateway {
589
 
590
  return $fields;
591
  }
592
-
593
  /**
594
- * Get the webhook ids stored locally in wp_options.
595
  *
596
- * @since 2.4.1
597
  */
598
- static function get_webhook_ids( $secret_key = null ) {
599
- $webhook_ids = pmpro_getOption( 'stripe_webhook_ids' );
600
-
601
- // Need to check in case its stored using the old option.
602
- if ( empty( $webhook_ids ) ) {
603
- $webhook_id = pmpro_getOption( 'stripe_webhook_id' );
604
- if ( ! empty( $webhook_id ) ) {
605
- // We store ids with the cooresponding secret key now.
606
- // Assume this webhook is for the currently selected environment.
607
- $secret_key = pmpro_getOption( 'stripe_secretkey' );
608
- $webhook_ids = array( $secret_key => $webhook_id );
609
- delete_option( 'pmpro_stripe_webhook_id' );
610
- update_option( 'pmpro_stripe_webhook_ids', $webhook_ids );
611
- }
612
  }
613
-
614
- // If secret key is 'true', then load the current secret key.
615
- if ( $secret_key === true ) {
616
- $secret_key = pmpro_getOption( 'stripe_secretkey' );
617
-
618
- // No key, then there will be no webhook.
619
- if ( empty( $secret_key ) ) {
620
- return false;
621
- }
622
  }
623
-
624
- // If a secret key was passed in, return just the id for that key.
625
- if ( ! empty( $secret_key ) ) {
626
- $secret_key_hash = wp_hash( $secret_key );
627
- if ( isset( $webhook_ids[$secret_key_hash] ) ) {
628
- return $webhook_ids[$secret_key_hash];
629
- } else {
630
- return false;
631
- }
632
  }
633
-
634
- if ( empty( $webhook_ids ) ) {
635
- $webhook_ids = array();
 
636
  }
637
-
638
- return $webhook_ids;
 
 
 
 
 
 
 
 
 
 
 
 
639
  }
640
-
641
  /**
642
- * Update webhook ids.
643
  *
644
- * @since 2.4.1
645
  */
646
- static function update_webhook_ids( $webhook_id, $secret_key = null ) {
647
- if ( empty( $secret_key ) ) {
648
- $secret_key = pmpro_getOption( 'stripe_secretkey' );
649
- }
650
-
651
- if ( empty( $secret_key ) ) {
652
- return false;
653
- }
654
-
655
- // Hash the secret key so it's not left behind in the DB.
656
- $secret_key_hash = wp_hash( $secret_key );
657
-
658
- $webhook_ids = self::get_webhook_ids();
659
-
660
- if ( ! empty( $webhook_id ) ) {
661
- $webhook_ids[$secret_key_hash] = $webhook_id;
662
- } else {
663
- unset( $webhook_ids[$secret_key_hash] );
664
  }
665
-
666
- update_option( 'pmpro_stripe_webhook_ids', $webhook_ids );
667
- return true;
668
  }
669
 
670
  /**
671
- * Get available webhooks
672
- *
673
- * @since 2.4
674
  */
675
- static function get_webhooks( $limit = 10 ) {
676
- if ( ! class_exists( 'Stripe\WebhookEndpoint' ) ) {
677
- return false;
 
678
  }
679
 
680
- try {
681
- $webhooks = Stripe_Webhook::all( [ 'limit' => apply_filters( 'pmpro_stripe_webhook_retrieve_limit', $limit ) ] );
682
- } catch (\Throwable $th) {
683
- $webhooks = $th->getMessage();
684
- } catch (\Exception $e) {
685
- $webhooks = $e->getMessage();
686
- }
687
-
688
- return $webhooks;
689
  }
690
 
691
  /**
692
- * Get current webhook URL for website to compare.
693
- *
694
- * @since 2.4
695
  */
696
- static function get_site_webhook_url() {
697
- return admin_url( 'admin-ajax.php' ) . '?action=stripe_webhook';
698
- }
699
-
700
- /**
701
- * List of current enabled events required for PMPro to work.
702
- *
703
- * @since 2.4
704
- */
705
- static function webhook_events() {
706
- return apply_filters( 'pmpro_stripe_webhook_events', array(
707
- 'invoice.payment_succeeded',
708
- 'invoice.payment_action_required',
709
- 'customer.subscription.deleted',
710
- 'charge.failed'
711
- ) );
712
- }
713
-
714
- /**
715
- * Create webhook with relevant events
716
- *
717
- * @since 2.4
718
- */
719
- static function create_webhook() {
720
- try {
721
- $create = Stripe_Webhook::create([
722
- 'url' => self::get_site_webhook_url(),
723
- 'enabled_events' => self::webhook_events(),
724
- 'api_version' => PMPRO_STRIPE_API_VERSION
725
- ]);
726
-
727
- if ( $create ) {
728
- self::update_webhook_ids( $create->id );
729
- return $create->id;
730
- }
731
- } catch (\Throwable $th) {
732
- //throw $th;
733
- return new WP_Error( 'error', $th->getMessage() );
734
- } catch (\Exception $e) {
735
- //throw $th;
736
- return new WP_Error( 'error', $e->getMessage() );
737
- }
738
-
739
- }
740
-
741
- /**
742
- * See if a webhook is registered with Stripe.
743
- *
744
- * @since 2.4
745
- */
746
- static function does_webhook_exist() {
747
- $saved_webhook = self::get_webhook_ids( true );
748
- if ( $saved_webhook ) {
749
- return $saved_webhook;
750
- }
751
-
752
- $webhooks = self::get_webhooks();
753
- $webhook_id = false;
754
- if ( ! empty( $webhooks ) && ! empty( $webhooks['data'] ) ) {
755
-
756
- $pmpro_webhook_url = self::get_site_webhook_url();
757
-
758
- foreach( $webhooks as $webhook ) {
759
- if ( $webhook->url == $pmpro_webhook_url ) {
760
- $webhook_id = $webhook->id;
761
- $webhook_events = $webhook->enabled_events;
762
- continue;
763
- }
764
- }
765
- } else {
766
- $webhook_id = false; // make sure it's false if none are found.
767
- }
768
-
769
- if ( $webhook_id ) {
770
- $webhook_data = array();
771
- $webhook_data['webhook_id'] = $webhook_id;
772
- $webhook_data['enabled_events'] = $webhook_events;
773
- return $webhook_data;
774
- } else {
775
- return false;
776
- }
777
- }
778
-
779
- /**
780
- * Get a list of events that are missing between the created existing webhook and required webhook events for Paid Memberships Pro.
781
- *
782
- * @since 2.4
783
- */
784
- static function check_missing_webhook_events( $webhook_events ) {
785
-
786
- // Get required events
787
- $pmpro_webhook_events = self::webhook_events();
788
- $event_missing = false;
789
-
790
- // No missing events if webhook event is "All Events" selected.
791
- if ( is_array( $webhook_events ) && $webhook_events[0] === '*' ) {
792
- return false;
793
- }
794
-
795
- foreach( $pmpro_webhook_events as $event ) {
796
- if ( ! in_array( $event, $webhook_events ) ) {
797
- $event_missing = true;
798
- }
799
- }
800
-
801
- if ( $event_missing ) {
802
- $events = array_unique( array_merge( $pmpro_webhook_events, $webhook_events ) );
803
- // Force reset of indexes for Stripe.
804
- $events = array_values( $events );
805
- } else {
806
- $events = false;
807
- }
808
-
809
- return $events;
810
- }
811
-
812
- /**
813
- * Update required webhook enabled events.
814
- *
815
- * @since 2.4
816
- */
817
- static function update_webhook_events() {
818
-
819
- // Also checks database to see if it's been saved.
820
- $webhook = self::does_webhook_exist();
821
-
822
- if ( empty( $webhook ) ) {
823
- $create = self::create_webhook();
824
- return $create;
825
- }
826
-
827
- // Bail if no enabled events for a webhook are passed through.
828
- if ( ! isset( $webhook['enabled_events'] ) ) {
829
- return;
830
- }
831
-
832
- $events = self::check_missing_webhook_events( $webhook['enabled_events'] );
833
-
834
- if ( $events ) {
835
-
836
- try {
837
- $update = Stripe_Webhook::update(
838
- $webhook['webhook_id'],
839
- ['enabled_events' => $events ]
840
- );
841
-
842
- if ( $update ) {
843
- self:update_webhook_ids( $webhook['webhook_id'] );
844
- return $update;
845
- }
846
- } catch (\Throwable $th) {
847
- //throw $th;
848
- return new WP_Error( 'error', $th->getMessage() );
849
- } catch (\Exception $e) {
850
- //throw $th;
851
- return new WP_Error( 'error', $e->getMessage() );
852
- }
853
-
854
- } else {
855
- self::update_webhook_ids( $webhook['webhook_id'] );
856
- }
857
-
858
- }
859
-
860
- /**
861
- * Delete an existing webhook.
862
- *
863
- * @since 2.4
864
- */
865
- function delete_webhook( $webhook_id, $secretkey = false ) {
866
- if ( empty( $secretkey ) ) {
867
- $secretkey = pmpro_getOption( "stripe_secretkey" );
868
- }
869
-
870
- try {
871
- $stripe = new Stripe_Client( $secretkey );
872
- $delete = $stripe->webhookEndpoints->delete( $webhook_id, [] );
873
- self::update_webhook_ids( '', $secretkey );
874
- } catch (\Throwable $th) {
875
- self::update_webhook_ids( '', $secretkey );
876
- return new WP_Error( 'error', $th->getMessage() );
877
- } catch (\Exception $e) {
878
- self::update_webhook_ids( '', $secretkey );
879
- return new WP_Error( 'error', $e->getMessage() );
880
- }
881
-
882
- return $delete;
883
- }
884
-
885
- /**
886
- * Filtering orders at checkout.
887
- *
888
- * @since 1.8
889
- */
890
- static function pmpro_checkout_order( $morder ) {
891
-
892
- // Create a code for the order.
893
- if ( empty( $morder->code ) ) {
894
- $morder->code = $morder->getRandomCode();
895
- }
896
-
897
- // Add the PaymentIntent ID to the order.
898
- if ( ! empty ( $_REQUEST['payment_intent_id'] ) ) {
899
- $morder->payment_intent_id = sanitize_text_field( $_REQUEST['payment_intent_id'] );
900
- }
901
-
902
- // Add the SetupIntent ID to the order.
903
- if ( ! empty ( $_REQUEST['setup_intent_id'] ) ) {
904
- $morder->setup_intent_id = sanitize_text_field( $_REQUEST['setup_intent_id'] );
905
- }
906
-
907
- // Add the Subscription ID to the order.
908
- if ( ! empty ( $_REQUEST['subscription_id'] ) ) {
909
- $morder->subscription_transaction_id = sanitize_text_field( $_REQUEST['subscription_id'] );
910
- }
911
-
912
- // Add the PaymentMethod ID to the order.
913
- if ( ! empty ( $_REQUEST['payment_method_id'] ) ) {
914
- $morder->payment_method_id = sanitize_text_field( $_REQUEST['payment_method_id'] );
915
- }
916
-
917
- // Add the Customer ID to the order.
918
- if ( empty( $morder->customer_id ) ) {
919
-
920
- }
921
- if ( ! empty ( $_REQUEST['customer_id'] ) ) {
922
- $morder->customer_id = sanitize_text_field( $_REQUEST['customer_id'] );
923
- }
924
-
925
- //stripe lite code to get name from other sources if available
926
- global $pmpro_stripe_lite, $current_user;
927
- if ( ! empty( $pmpro_stripe_lite ) && empty( $morder->FirstName ) && empty( $morder->LastName ) ) {
928
- if ( ! empty( $current_user->ID ) ) {
929
- $morder->FirstName = get_user_meta( $current_user->ID, "first_name", true );
930
- $morder->LastName = get_user_meta( $current_user->ID, "last_name", true );
931
- } elseif ( ! empty( $_REQUEST['first_name'] ) && ! empty( $_REQUEST['last_name'] ) ) {
932
- $morder->FirstName = sanitize_text_field( $_REQUEST['first_name'] );
933
- $morder->LastName = sanitize_text_field( $_REQUEST['last_name'] );
934
- }
935
- }
936
-
937
- return $morder;
938
- }
939
-
940
- /**
941
- * Code to run after checkout
942
- *
943
- * @since 1.8
944
- */
945
- static function pmpro_after_checkout( $user_id, $morder ) {
946
- global $gateway;
947
-
948
- if ( $gateway == "stripe" ) {
949
- if ( self::$is_loaded && ! empty( $morder ) && ! empty( $morder->Gateway ) && ! empty( $morder->Gateway->customer ) && ! empty( $morder->Gateway->customer->id ) ) {
950
- update_user_meta( $user_id, "pmpro_stripe_customerid", $morder->Gateway->customer->id );
951
- }
952
- }
953
- }
954
-
955
- /**
956
- * Check settings if billing address should be shown.
957
- * @since 1.8
958
- */
959
- static function pmpro_include_billing_address_fields( $include ) {
960
- //check settings RE showing billing address
961
- if ( ! pmpro_getOption( "stripe_billingaddress" ) ) {
962
- $include = false;
963
- }
964
-
965
- return $include;
966
- }
967
-
968
- /**
969
- * Use our own payment fields at checkout. (Remove the name attributes.)
970
- * @since 1.8
971
- */
972
- static function pmpro_include_payment_information_fields( $include ) {
973
- //global vars
974
- global $pmpro_requirebilling, $pmpro_show_discount_code, $discount_code, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear;
975
 
976
  //get accepted credit cards
977
  $pmpro_accepted_credit_cards = pmpro_getOption( "accepted_credit_cards" );
@@ -990,6 +751,15 @@ class PMProGateway_stripe extends PMProGateway {
990
  <?php if ( ! empty( $sslseal ) ) { ?>
991
  <div class="<?php echo pmpro_get_element_class( 'pmpro_checkout-fields-display-seal' ); ?>">
992
  <?php } ?>
 
 
 
 
 
 
 
 
 
993
  <div class="pmpro_checkout-fields<?php if ( ! empty( $sslseal ) ) { ?> pmpro_checkout-fields-leftcol<?php } ?>">
994
  <?php
995
  $pmpro_include_cardtype_field = apply_filters( 'pmpro_include_cardtype_field', false );
@@ -1026,7 +796,7 @@ class PMProGateway_stripe extends PMProGateway {
1026
  <?php if ( $pmpro_show_discount_code ) { ?>
1027
  <div class="<?php echo pmpro_get_element_class( 'pmpro_checkout-field pmpro_payment-discount-code', 'pmpro_payment-discount-code' ); ?>">
1028
  <label for="discount_code"><?php _e( 'Discount Code', 'paid-memberships-pro' ); ?></label>
1029
- <input class="<?php echo pmpro_get_element_class( 'input', 'discount_code' ); ?>"
1030
  id="discount_code" name="discount_code" type="text" size="10"
1031
  value="<?php echo esc_attr( $discount_code ) ?>"/>
1032
  <input type="button" id="discount_code_button" name="discount_code_button"
@@ -1051,18 +821,9 @@ class PMProGateway_stripe extends PMProGateway {
1051
  *
1052
  * @since 1.8
1053
  */
1054
- static function user_profile_fields( $user ) {
1055
  global $wpdb, $current_user, $pmpro_currency_symbol;
1056
 
1057
- $cycles = array(
1058
- __( 'Day(s)', 'paid-memberships-pro' ) => 'Day',
1059
- __( 'Week(s)', 'paid-memberships-pro' ) => 'Week',
1060
- __( 'Month(s)', 'paid-memberships-pro' ) => 'Month',
1061
- __( 'Year(s)', 'paid-memberships-pro' ) => 'Year'
1062
- );
1063
- $current_year = date_i18n( "Y" );
1064
- $current_month = date_i18n( "m" );
1065
-
1066
  //make sure the current user has privileges
1067
  $membership_level_capability = apply_filters( "pmpro_edit_member_capability", "manage_options" );
1068
  if ( ! current_user_can( $membership_level_capability ) ) {
@@ -1075,223 +836,44 @@ class PMProGateway_stripe extends PMProGateway {
1075
  return false;
1076
  }
1077
 
1078
- //check that user has a current subscription at Stripe
1079
- $last_order = new MemberOrder();
1080
- $last_order->getLastMemberOrder( $user->ID );
1081
-
1082
- //assume no sub to start
1083
- $sub = false;
1084
 
1085
- //check that gateway is Stripe
1086
- if ( $last_order->gateway == "stripe" && self::$is_loaded ) {
1087
- //is there a customer?
1088
- $sub = $last_order->Gateway->getSubscription( $last_order );
 
 
 
 
1089
  }
1090
-
1091
- $customer_id = $user->pmpro_stripe_customerid;
1092
-
1093
- if ( empty( $sub ) ) {
1094
- //make sure we delete stripe updates
1095
- update_user_meta( $user->ID, "pmpro_stripe_updates", array() );
1096
-
1097
- //if the last order has a sub id, let the admin know there is no sub at Stripe
1098
- if ( ! empty( $last_order ) && $last_order->gateway == "stripe" && ! empty( $last_order->subscription_transaction_id ) && strpos( $last_order->subscription_transaction_id, "sub_" ) !== false ) {
1099
- ?>
1100
- <p><?php printf( __( '%1$sNote:%2$s Subscription %3$s%4$s%5$s could not be found at Stripe. It may have been deleted.', 'paid-memberships-pro' ), '<strong>', '</strong>', '<strong>', esc_attr( $last_order->subscription_transaction_id ), '</strong>' ); ?></p>
1101
- <?php
1102
- }
1103
- } elseif ( true === self::$is_loaded ) {
1104
- ?>
1105
- <h3><?php _e( "Subscription Updates", 'paid-memberships-pro' ); ?></h3>
1106
- <p>
1107
- <?php
1108
- if ( empty( $_REQUEST['user_id'] ) ) {
1109
- _e( "Subscription updates, allow you to change the member's subscription values at predefined times. Be sure to click Update Profile after making changes.", 'paid-memberships-pro' );
1110
- } else {
1111
- _e( "Subscription updates, allow you to change the member's subscription values at predefined times. Be sure to click Update User after making changes.", 'paid-memberships-pro' );
1112
- }
1113
- ?>
1114
- </p>
1115
- <table class="form-table">
1116
- <tr>
1117
- <th><label for="membership_level"><?php _e( "Update", 'paid-memberships-pro' ); ?></label></th>
1118
- <td id="updates_td">
1119
- <?php
1120
- $old_updates = $user->pmpro_stripe_updates;
1121
- if ( is_array( $old_updates ) ) {
1122
- $updates = array_merge(
1123
- array(
1124
- array(
1125
- 'template' => true,
1126
- 'when' => 'now',
1127
- 'date_month' => '',
1128
- 'date_day' => '',
1129
- 'date_year' => '',
1130
- 'billing_amount' => '',
1131
- 'cycle_number' => '',
1132
- 'cycle_period' => 'Month'
1133
- )
1134
- ),
1135
- $old_updates
1136
- );
1137
- } else {
1138
- $updates = array(
1139
- array(
1140
- 'template' => true,
1141
- 'when' => 'now',
1142
- 'date_month' => '',
1143
- 'date_day' => '',
1144
- 'date_year' => '',
1145
- 'billing_amount' => '',
1146
- 'cycle_number' => '',
1147
- 'cycle_period' => 'Month'
1148
- )
1149
- );
1150
- }
1151
-
1152
- foreach ( $updates as $update ) {
1153
- ?>
1154
- <div class="updates_update"
1155
- <?php if ( ! empty( $update['template'] ) ) { ?>style="display: none;"<?php } ?>>
1156
- <select class="updates_when" name="updates_when[]">
1157
- <option value="now" <?php selected( $update['when'], "now" ); ?>>Now</option>
1158
- <option value="payment" <?php selected( $update['when'], "payment" ); ?>>After
1159
- Next Payment
1160
- </option>
1161
- <option value="date" <?php selected( $update['when'], "date" ); ?>>On Date
1162
- </option>
1163
- </select>
1164
- <span class="updates_date"
1165
- <?php if ( $update['when'] != "date" ) { ?>style="display: none;"<?php } ?>>
1166
- <select name="updates_date_month[]">
1167
- <?php
1168
- for ( $i = 1; $i < 13; $i ++ ) {
1169
- ?>
1170
- <option value="<?php echo str_pad( $i, 2, "0", STR_PAD_LEFT ); ?>"
1171
- <?php if ( ! empty( $update['date_month'] ) && $update['date_month'] == $i ) { ?>selected="selected"<?php } ?>>
1172
- <?php echo date_i18n( "M", strtotime( $i . "/15/" . $current_year ) ); ?>
1173
- </option>
1174
- <?php
1175
- }
1176
- ?>
1177
- </select>
1178
- <input name="updates_date_day[]" type="text" size="2"
1179
- value="<?php if ( ! empty( $update['date_day'] ) ) {
1180
- echo esc_attr( $update['date_day'] );
1181
- } ?>"/>
1182
- <input name="updates_date_year[]" type="text" size="4"
1183
- value="<?php if ( ! empty( $update['date_year'] ) ) {
1184
- echo esc_attr( $update['date_year'] );
1185
- } ?>"/>
1186
- </span>
1187
- <span class="updates_billing"
1188
- <?php if ( $update['when'] == "now" ) { ?>style="display: none;"<?php } ?>>
1189
- <?php echo $pmpro_currency_symbol ?><input name="updates_billing_amount[]" type="text"
1190
- size="10"
1191
- value="<?php echo esc_attr( $update['billing_amount'] ); ?>"/>
1192
- <small><?php _e( 'per', 'paid-memberships-pro' ); ?></small>
1193
- <input name="updates_cycle_number[]" type="text" size="5"
1194
- value="<?php echo esc_attr( $update['cycle_number'] ); ?>"/>
1195
- <select name="updates_cycle_period[]">
1196
- <?php
1197
- foreach ( $cycles as $name => $value ) {
1198
- echo "<option value='$value'";
1199
- if ( ! empty( $update['cycle_period'] ) && $update['cycle_period'] == $value ) {
1200
- echo " selected='selected'";
1201
- }
1202
- echo ">$name</option>";
1203
- }
1204
- ?>
1205
- </select>
1206
- </span>
1207
- <span>
1208
- <a class="updates_remove" href="javascript:void(0);">Remove</a>
1209
- </span>
1210
- </div>
1211
- <?php
1212
- }
1213
- ?>
1214
- <p><a id="updates_new_update" href="javascript:void(0);">+ New Update</a></p>
1215
- </td>
1216
- </tr>
1217
- </table>
1218
- <script>
1219
- <!--
1220
- jQuery(document).ready(function () {
1221
- //function to update dropdowns/etc based on when field
1222
- function updateSubscriptionUpdateFields(when) {
1223
- if (jQuery(when).val() == 'date')
1224
- jQuery(when).parent().children('.updates_date').show();
1225
- else
1226
- jQuery(when).parent().children('.updates_date').hide();
1227
-
1228
- if (jQuery(when).val() == 'no')
1229
- jQuery(when).parent().children('.updates_billing').hide();
1230
- else
1231
- jQuery(when).parent().children('.updates_billing').show();
1232
- }
1233
-
1234
- //and update on page load
1235
- jQuery('.updates_when').each(function () {
1236
- if (jQuery(this).parent().css('display') != 'none') updateSubscriptionUpdateFields(this);
1237
- });
1238
-
1239
- //add a new update when clicking to
1240
- var num_updates_divs = <?php echo count( $updates );?>;
1241
- jQuery('#updates_new_update').click(function () {
1242
- //get updates
1243
- updates = jQuery('.updates_update').toArray();
1244
-
1245
- //clone the first one
1246
- new_div = jQuery(updates[0]).clone();
1247
-
1248
- //append
1249
- new_div.insertBefore('#updates_new_update');
1250
-
1251
- //update events
1252
- addUpdateEvents()
1253
-
1254
- //unhide it
1255
- new_div.show();
1256
- updateSubscriptionUpdateFields(new_div.children('.updates_when'));
1257
- });
1258
-
1259
- function addUpdateEvents() {
1260
- //update when when changes
1261
- jQuery('.updates_when').change(function () {
1262
- updateSubscriptionUpdateFields(this);
1263
- });
1264
-
1265
- //remove updates when clicking
1266
- jQuery('.updates_remove').click(function () {
1267
- jQuery(this).parent().parent().remove();
1268
- });
1269
- }
1270
-
1271
- addUpdateEvents();
1272
- });
1273
- -->
1274
- </script>
1275
- <?php
1276
- }
1277
- }
1278
 
1279
  /**
1280
- * Process fields from the edit user page
 
1281
  *
1282
- * @since 1.8
1283
  */
1284
  static function user_profile_fields_save( $user_id ) {
1285
  global $wpdb;
1286
-
1287
  //check capabilities
1288
  $membership_level_capability = apply_filters( "pmpro_edit_member_capability", "manage_options" );
1289
  if ( ! current_user_can( $membership_level_capability ) ) {
1290
  return false;
1291
  }
1292
 
1293
- //make sure some value was passed
 
 
 
 
 
1294
  if ( ! isset( $_POST['updates_when'] ) || ! is_array( $_POST['updates_when'] ) ) {
 
 
1295
  return;
1296
  }
1297
 
@@ -1299,8 +881,8 @@ class PMProGateway_stripe extends PMProGateway {
1299
  $updates = array();
1300
  $next_on_date_update = "";
1301
 
1302
- //build array of updates (we skip the first because it's the template field for the JavaScript
1303
- for ( $i = 1; $i < count( $_POST['updates_when'] ); $i ++ ) {
1304
  $update = array();
1305
 
1306
  //all updates have these values
@@ -1327,7 +909,7 @@ class PMProGateway_stripe extends PMProGateway {
1327
 
1328
  //if when is now, update the subscription
1329
  if ( $update['when'] == "now" ) {
1330
- PMProGateway_stripe::updateSubscription( $update, $user_id );
1331
 
1332
  continue;
1333
  } elseif ( $update['when'] == 'date' ) {
@@ -1352,27 +934,36 @@ class PMProGateway_stripe extends PMProGateway {
1352
  /**
1353
  * Cron activation for subscription updates.
1354
  *
 
 
 
1355
  * @since 1.8
1356
  */
1357
- static function pmpro_activation() {
1358
  pmpro_maybe_schedule_event( time(), 'daily', 'pmpro_cron_stripe_subscription_updates' );
1359
  }
1360
 
1361
  /**
1362
  * Cron deactivation for subscription updates.
1363
  *
 
 
 
1364
  * @since 1.8
1365
  */
1366
- static function pmpro_deactivation() {
1367
  wp_clear_scheduled_hook( 'pmpro_cron_stripe_subscription_updates' );
1368
  }
1369
 
1370
  /**
1371
  * Cron job for subscription updates.
1372
  *
 
 
 
1373
  * @since 1.8
1374
  */
1375
- static function pmpro_cron_stripe_subscription_updates() {
1376
  global $wpdb;
1377
 
1378
  //get all updates for today (or before today)
@@ -1409,7 +1000,7 @@ class PMProGateway_stripe extends PMProGateway {
1409
  if ( $ud['when'] == 'date' &&
1410
  $ud['date_year'] . "-" . $ud['date_month'] . "-" . $ud['date_day'] <= date_i18n( "Y-m-d", current_time( 'timestamp' ) )
1411
  ) {
1412
- PMProGateway_stripe::updateSubscription( $ud, $user_id );
1413
 
1414
  //remove update from list
1415
  unset( $user_updates[ $key ] );
@@ -1439,7 +1030,7 @@ class PMProGateway_stripe extends PMProGateway {
1439
  * because of an expired credit card/etc and a user checks out to renew their subscription
1440
  * instead of updating their billing information via the billing info page.
1441
  */
1442
- static function pmpro_checkout_before_processing() {
1443
  global $wpdb, $current_user;
1444
 
1445
  // we're only worried about cases where the user is logged in
@@ -1483,7 +1074,7 @@ class PMProGateway_stripe extends PMProGateway {
1483
 
1484
  //so let's cancel the user's susbcription
1485
  if ( ! empty( $last_order ) && ! empty( $last_order->subscription_transaction_id ) ) {
1486
- $subscription = $last_order->Gateway->getSubscription( $last_order );
1487
  if ( ! empty( $subscription ) ) {
1488
  $last_order->Gateway->cancelSubscriptionAtGateway( $subscription, true );
1489
 
@@ -1514,413 +1105,544 @@ class PMProGateway_stripe extends PMProGateway {
1514
  }
1515
 
1516
  /**
1517
- * Process checkout and decide if a charge and or subscribe is needed
1518
- * Updated in v2.1 to work with Stripe v3 payment intents.
1519
- * @since 1.4
1520
  */
1521
- function process( &$order ) {
1522
- $steps = array(
1523
- 'set_customer',
1524
- 'set_payment_method',
1525
- 'attach_payment_method_to_customer',
1526
- 'process_charges',
1527
- 'process_subscriptions',
1528
- );
1529
 
1530
- foreach ( $steps as $key => $step ) {
1531
- do_action( "pmpro_process_order_before_{$step}", $order );
1532
- $this->$step( $order );
1533
- do_action( "pmpro_process_order_after_{$step}", $order );
1534
- if ( ! empty( $order->error ) ) {
1535
- return false;
 
 
 
 
 
 
 
 
 
 
 
1536
  }
1537
  }
1538
 
1539
- $this->clean_up( $order );
1540
- $order->status = 'success';
1541
- $order->saveOrder();
1542
-
1543
- return true;
1544
  }
1545
 
1546
- /**
1547
- * Make a one-time charge with Stripe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1548
  *
1549
- * @since 1.4
1550
  */
1551
- function charge( &$order ) {
1552
- global $pmpro_currency, $pmpro_currencies;
1553
- $currency_unit_multiplier = 100; //ie 100 cents per USD
 
 
1554
 
1555
- //account for zero-decimal currencies like the Japanese Yen
1556
- if ( is_array( $pmpro_currencies[ $pmpro_currency ] ) && isset( $pmpro_currencies[ $pmpro_currency ]['decimals'] ) && $pmpro_currencies[ $pmpro_currency ]['decimals'] == 0 ) {
1557
- $currency_unit_multiplier = 1;
1558
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1559
 
1560
- //create a code for the order
1561
- if ( empty( $order->code ) ) {
1562
- $order->code = $order->getRandomCode();
 
 
 
1563
  }
1564
 
1565
- //what amount to charge?
1566
- $amount = $order->InitialPayment;
 
 
 
 
 
 
 
1567
 
1568
- //tax
1569
- $order->subtotal = $amount;
1570
- $tax = $order->getTax( true );
1571
- $amount = pmpro_round_price( (float) $order->subtotal + (float) $tax );
 
 
 
1572
 
1573
- //create a customer
1574
- $result = $this->getCustomer( $order );
 
 
 
 
 
1575
 
1576
- if ( empty( $result ) ) {
1577
- //failed to create customer
1578
  return false;
1579
  }
1580
 
1581
- //charge
1582
- try {
1583
- $params = array(
1584
- "amount" => $amount * $currency_unit_multiplier, # amount in cents, again
1585
- "currency" => strtolower( $pmpro_currency ),
1586
- "customer" => $this->customer->id,
1587
- "description" => apply_filters( 'pmpro_stripe_order_description', "Order #" . $order->code . ", " . trim( $order->FirstName . " " . $order->LastName ) . " (" . $order->Email . ")", $order )
1588
- );
1589
- /**
1590
- * Filter params used to create the Stripe charge.
1591
- *
1592
- * @since 2.4.4
1593
- *
1594
- * @param array $params Array of params sent to Stripe.
1595
- * @param object $order Order object for this checkout.
1596
- */
1597
- $params = apply_filters( 'pmpro_stripe_charge_params', $params, $order );
1598
- $response = Stripe_Charge::create( $params );
1599
- } catch ( \Throwable $e ) {
1600
- //$order->status = "error";
1601
- $order->errorcode = true;
1602
- $order->error = "Error: " . $e->getMessage();
1603
- $order->shorterror = $order->error;
1604
 
1605
- return false;
1606
- } catch ( \Exception $e ) {
1607
- //$order->status = "error";
1608
- $order->errorcode = true;
1609
- $order->error = "Error: " . $e->getMessage();
1610
- $order->shorterror = $order->error;
1611
 
1612
- return false;
1613
- }
1614
 
1615
- if ( empty( $response["failure_message"] ) ) {
1616
- //successful charge
1617
- $order->payment_transaction_id = $response["id"];
1618
- $order->updateStatus( "success" );
1619
- $order->saveOrder();
1620
 
1621
- return true;
 
 
 
 
1622
  } else {
1623
- //$order->status = "error";
1624
- $order->errorcode = true;
1625
- $order->error = $response['failure_message'];
1626
- $order->shorterror = $response['failure_message'];
1627
-
1628
- return false;
1629
  }
1630
  }
1631
 
1632
  /**
1633
- * Get a Stripe customer object.
1634
  *
1635
- * If $this->customer is set, it returns it.
1636
- * It first checks if the order has a subscription_transaction_id. If so, that's the customer id.
1637
- * If not, it checks for a user_id on the order and searches for a customer id in the user meta.
1638
- * If a customer id is found, it checks for a customer through the Stripe API.
1639
- * If a customer is found and there is a stripeToken on the order passed, it will update the customer.
1640
- * If no customer is found and there is a stripeToken on the order passed, it will create a customer.
1641
- *
1642
- * @return Stripe_Customer|false
1643
- * @since 1.4
1644
  */
1645
- function getCustomer( &$order = false, $force = false ) {
1646
- global $current_user;
 
 
1647
 
1648
- //already have it?
1649
- if ( ! empty( $this->customer ) && ! $force ) {
1650
- return $this->customer;
 
 
 
 
 
 
 
1651
  }
1652
 
1653
- // Is it already on the order?
1654
- if ( ! empty( $order->customer_id ) ) {
1655
- $customer_id = $order->customer_id;
 
 
 
 
 
 
 
 
 
 
 
1656
  }
 
1657
 
1658
- //figure out user_id and user
1659
- if ( ! empty( $order->user_id ) ) {
1660
- $user_id = $order->user_id;
1661
- }
 
 
 
 
 
 
1662
 
1663
- //if no id passed, check the current user
1664
- if ( empty( $user_id ) && ! empty( $current_user->ID ) ) {
1665
- $user_id = $current_user->ID;
1666
- }
1667
 
1668
- if ( ! empty( $user_id ) ) {
1669
- $user = get_userdata( $user_id );
1670
- } else {
1671
- $user = null;
1672
- }
1673
 
1674
- //transaction id?
1675
- if ( ! empty( $order->subscription_transaction_id ) && strpos( $order->subscription_transaction_id, "cus_" ) !== false ) {
1676
- $customer_id = $order->subscription_transaction_id;
1677
- } else {
1678
- //try based on user id
1679
- if ( ! empty( $user_id ) ) {
1680
- $customer_id = get_user_meta( $user_id, "pmpro_stripe_customerid", true );
1681
  }
1682
 
1683
- //look up by transaction id
1684
- if ( empty( $customer_id ) && ! empty( $user_id ) ) {//user id from this order or the user's last stripe order
1685
- if ( ! empty( $order->payment_transaction_id ) ) {
1686
- $payment_transaction_id = $order->payment_transaction_id;
1687
- } else {
1688
- //find the user's last stripe order
1689
- $last_order = new MemberOrder();
1690
- $last_order->getLastMemberOrder( $user_id, array(
1691
- 'success',
1692
- 'cancelled'
1693
- ), null, 'stripe', $order->Gateway->gateway_environment );
1694
- if ( ! empty( $last_order->payment_transaction_id ) ) {
1695
- $payment_transaction_id = $last_order->payment_transaction_id;
1696
- }
1697
- }
1698
-
1699
- //we have a transaction id to look up
1700
- if ( ! empty( $payment_transaction_id ) ) {
1701
- if ( strpos( $payment_transaction_id, "ch_" ) !== false ) {
1702
- //charge, look it up
1703
- try {
1704
- $charge = Stripe_Charge::retrieve( $payment_transaction_id );
1705
- } catch ( \Throwable $e ) {
1706
- $order->error = sprintf( __( 'Error: %s', 'paid-memberships-pro' ), $e->getMessage() );
1707
-
1708
- return false;
1709
- } catch ( \Exception $e ) {
1710
- $order->error = sprintf( __( 'Error: %s', 'paid-memberships-pro' ), $e->getMessage() );
1711
 
1712
- return false;
1713
- }
1714
 
1715
- if ( ! empty( $charge ) && ! empty( $charge->customer ) ) {
1716
- $customer_id = $charge->customer;
1717
- }
1718
- } else if ( strpos( $payment_transaction_id, "in_" ) !== false ) {
1719
- //invoice look it up
1720
- try {
1721
- $invoice = Stripe_Invoice::retrieve( $payment_transaction_id );
1722
- } catch ( \Throwable $e ) {
1723
- $order->error = sprintf( __( 'Error: %s', 'paid-memberships-pro' ), $e->getMessage() );
1724
-
1725
- return false;
1726
- } catch ( \Exception $e ) {
1727
- $order->error = sprintf( __( 'Error: %s', 'paid-memberships-pro' ), $e->getMessage() );
1728
-
1729
- return false;
1730
- }
1731
 
1732
- if ( ! empty( $invoice ) && ! empty( $invoice->customer ) ) {
1733
- $customer_id = $invoice->customer;
1734
- }
1735
- }
1736
  }
1737
 
1738
- //if we found it, save to user meta for future reference
1739
- if ( ! empty( $customer_id ) ) {
1740
- update_user_meta( $user_id, "pmpro_stripe_customerid", $customer_id );
1741
- }
1742
  }
1743
  }
1744
 
1745
- //get name and email values from order in case we update
1746
- if ( ! empty( $order->FirstName ) && ! empty( $order->LastName ) ) {
1747
- $name = trim( $order->FirstName . " " . $order->LastName );
1748
- } elseif ( ! empty( $order->FirstName ) ) {
1749
- $name = $order->FirstName;
1750
- } elseif ( ! empty( $order->LastName ) ) {
1751
- $name = $order->LastName;
1752
- }
1753
 
1754
- if ( empty( $name ) && ! empty( $user->ID ) ) {
1755
- $name = trim( $user->first_name . " " . $user->last_name );
1756
 
1757
- //still empty?
1758
- if ( empty( $name ) ) {
1759
- $name = $user->user_login;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1760
  }
1761
- } elseif ( empty( $name ) ) {
1762
- $name = "No Name";
1763
- }
1764
-
1765
- if ( ! empty( $order->Email ) ) {
1766
- $email = $order->Email;
1767
  } else {
1768
- $email = "";
1769
- }
 
 
 
 
 
 
 
 
 
 
 
 
1770
 
1771
- if ( empty( $email ) && ! empty( $user->ID ) && ! empty( $user->user_email ) ) {
1772
- $email = $user->user_email;
1773
- } elseif ( empty( $email ) ) {
1774
- $email = "No Email";
1775
- }
 
 
 
 
 
 
1776
 
1777
- //check for an existing stripe customer
1778
- if ( ! empty( $customer_id ) ) {
1779
- try {
1780
- $this->customer = Stripe_Customer::retrieve( $customer_id );
1781
- // Update description.
1782
- if ( ! empty( $order->payment_method_id ) ) {
1783
- $this->customer->description = $name . " (" . $email . ")";
1784
- $this->customer->email = $email;
1785
- $this->customer->save();
1786
  }
1787
 
1788
- if ( ! empty( $user_id ) ) {
1789
- //user logged in/etc
1790
- update_user_meta( $user_id, "pmpro_stripe_customerid", $this->customer->id );
1791
- } else {
1792
- //user not registered yet, queue it up
1793
- global $pmpro_stripe_customer_id;
1794
- $pmpro_stripe_customer_id = $this->customer->id;
1795
- if ( ! function_exists( 'pmpro_user_register_stripe_customerid' ) ) {
1796
- function pmpro_user_register_stripe_customerid( $user_id ) {
1797
- global $pmpro_stripe_customer_id;
1798
- update_user_meta( $user_id, "pmpro_stripe_customerid", $pmpro_stripe_customer_id );
1799
- }
1800
 
1801
- add_action( "user_register", "pmpro_user_register_stripe_customerid" );
1802
- }
 
 
 
 
 
 
 
 
1803
  }
1804
 
1805
- return $this->customer;
1806
- } catch ( \Throwable $e ) {
1807
- //assume no customer found
1808
- } catch ( \Exception $e ) {
1809
- //assume no customer found
1810
  }
1811
- }
1812
 
1813
- //no customer id, create one
1814
- if ( ! empty( $order->payment_method_id ) ) {
1815
- try {
1816
- $this->customer = Stripe_Customer::create( array(
1817
- "description" => $name . " (" . $email . ")",
1818
- "email" => $order->Email,
1819
- ) );
1820
- } catch ( \Stripe\Error $e ) {
1821
- $order->error = __( "Error creating customer record with Stripe:", 'paid-memberships-pro' ) . " " . $e->getMessage();
1822
- $order->shorterror = $order->error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1823
 
1824
- return false;
1825
- } catch ( \Throwable $e ) {
1826
- $order->error = __( "Error creating customer record with Stripe:", 'paid-memberships-pro' ) . " " . $e->getMessage();
1827
- $order->shorterror = $order->error;
 
 
 
 
 
 
 
1828
 
1829
- return false;
1830
- } catch ( \Exception $e ) {
1831
- $order->error = __( "Error creating customer record with Stripe:", 'paid-memberships-pro' ) . " " . $e->getMessage();
1832
- $order->shorterror = $order->error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1833
 
1834
- return false;
 
 
 
 
 
 
 
 
 
 
 
1835
  }
1836
 
1837
- if ( ! empty( $user_id ) ) {
1838
- //user logged in/etc
1839
- update_user_meta( $user_id, "pmpro_stripe_customerid", $this->customer->id );
1840
- } else {
1841
- //user not registered yet, queue it up
1842
- global $pmpro_stripe_customer_id;
1843
- $pmpro_stripe_customer_id = $this->customer->id;
1844
- if ( ! function_exists( 'pmpro_user_register_stripe_customerid' ) ) {
1845
- function pmpro_user_register_stripe_customerid( $user_id ) {
1846
- global $pmpro_stripe_customer_id;
1847
- update_user_meta( $user_id, "pmpro_stripe_customerid", $pmpro_stripe_customer_id );
1848
- }
 
1849
 
1850
- add_action( "user_register", "pmpro_user_register_stripe_customerid" );
 
 
 
 
 
 
 
 
 
 
1851
  }
1852
  }
1853
 
1854
- return apply_filters( 'pmpro_stripe_create_customer', $this->customer );
 
 
1855
  }
1856
 
1857
- return false;
1858
  }
1859
 
1860
  /**
1861
- * Get a Stripe subscription from a PMPro order
1862
  *
1863
- * @since 1.8
 
 
 
1864
  */
1865
- function getSubscription( &$order ) {
1866
- global $wpdb;
1867
-
1868
- //no order?
1869
- if ( empty( $order ) || empty( $order->code ) ) {
1870
- return false;
1871
- }
1872
-
1873
- $result = $this->getCustomer( $order, true ); //force so we don't get a cached sub for someone else
1874
-
1875
- //no customer?
1876
- if ( empty( $result ) ) {
1877
- return false;
1878
- }
1879
 
1880
- //no subscriptions?
1881
- if ( empty( $this->customer->subscriptions ) ) {
1882
  return false;
1883
  }
1884
 
1885
- //is there a subscription transaction id pointing to a sub?
1886
- if ( ! empty( $order->subscription_transaction_id ) && strpos( $order->subscription_transaction_id, "sub_" ) !== false ) {
1887
- try {
1888
- $sub = $this->customer->subscriptions->retrieve( $order->subscription_transaction_id );
1889
- } catch ( \Throwable $e ) {
1890
- $order->error = __( "Error getting subscription with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
1891
- $order->shorterror = $order->error;
1892
-
1893
- return false;
1894
- } catch ( \Exception $e ) {
1895
- $order->error = __( "Error getting subscription with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
1896
- $order->shorterror = $order->error;
1897
-
1898
- return false;
1899
- }
1900
 
1901
- return $sub;
 
 
 
 
1902
  }
1903
 
1904
- //find subscription based on customer id and order/plan id
1905
- $subscriptions = $this->customer->subscriptions->all();
 
 
 
1906
 
1907
- //no subscriptions
1908
- if ( empty( $subscriptions ) || empty( $subscriptions->data ) ) {
1909
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1910
  }
1911
 
1912
- //we really want to test against the order codes of all orders with the same subscription_transaction_id (customer id)
1913
- $codes = $wpdb->get_col( "SELECT code FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . esc_sql( $order->user_id ) . "' AND subscription_transaction_id = '" . esc_sql( $order->subscription_transaction_id ) . "' AND status NOT IN('refunded', 'review', 'token', 'error')" );
 
 
 
 
 
 
 
 
1914
 
1915
- //find the one for this order
1916
- foreach ( $subscriptions->data as $sub ) {
1917
- if ( in_array( $sub->plan->id, $codes ) ) {
1918
- return $sub;
1919
- }
 
 
1920
  }
1921
-
1922
- //didn't find anything yet
1923
- return false;
1924
  }
1925
 
1926
  /**
@@ -1928,8 +1650,8 @@ class PMProGateway_stripe extends PMProGateway {
1928
  *
1929
  * @since 2.3
1930
  */
1931
- function getSubscriptionStatus( &$order ) {
1932
- $subscription = $this->getSubscription( $order );
1933
 
1934
  if ( ! empty( $subscription ) ) {
1935
  return $subscription->status;
@@ -1939,222 +1661,1338 @@ class PMProGateway_stripe extends PMProGateway {
1939
  }
1940
 
1941
  /**
1942
- * Create a new subscription with Stripe
1943
  *
1944
  * @since 1.4
1945
  */
1946
- function subscribe( &$order, $checkout = true ) {
1947
- global $pmpro_currency, $pmpro_currencies;
1948
-
1949
- $currency_unit_multiplier = 100; //ie 100 cents per USD
1950
-
1951
- //account for zero-decimal currencies like the Japanese Yen
1952
- if ( is_array( $pmpro_currencies[ $pmpro_currency ] ) && isset( $pmpro_currencies[ $pmpro_currency ]['decimals'] ) && $pmpro_currencies[ $pmpro_currency ]['decimals'] == 0 ) {
1953
- $currency_unit_multiplier = 1;
1954
  }
1955
 
1956
- //create a code for the order
1957
- if ( empty( $order->code ) ) {
1958
- $order->code = $order->getRandomCode();
 
 
 
1959
  }
1960
 
1961
- //filter order before subscription. use with care.
1962
- $order = apply_filters( "pmpro_subscribe_order", $order, $this );
1963
-
1964
- //figure out the user
1965
- if ( ! empty( $order->user_id ) ) {
1966
- $user_id = $order->user_id;
1967
- } else {
1968
- global $current_user;
1969
- $user_id = $current_user->ID;
1970
  }
1971
 
1972
- //set up customer
1973
-
1974
- $result = $this->getCustomer( $order );
1975
- if ( empty( $result ) ) {
1976
- return false; //error retrieving customer
1977
  }
1978
 
1979
- // set subscription id to custom id
1980
-
1981
- $order->subscription_transaction_id = $this->customer['id']; //transaction id is the customer id, we save it in user meta later too
1982
 
1983
- //figure out the amounts
1984
- $amount = $order->PaymentAmount;
1985
- $amount_tax = $order->getTaxForPrice( $amount );
1986
- $amount = pmpro_round_price( (float) $amount + (float) $amount_tax );
 
 
 
1987
 
1988
- /*
1989
- There are two parts to the trial. Part 1 is simply the delay until the first payment
1990
- since we are doing the first payment as a separate transaction.
1991
- The second part is the actual "trial" set by the admin.
1992
 
1993
- Stripe only supports Year or Month for billing periods, but we account for Days and Weeks just in case.
1994
- */
1995
- //figure out the trial length (first payment handled by initial charge)
1996
- if ( $order->BillingPeriod == "Year" ) {
1997
- $trial_period_days = $order->BillingFrequency * 365; //annual
1998
- } elseif ( $order->BillingPeriod == "Day" ) {
1999
- $trial_period_days = $order->BillingFrequency * 1; //daily
2000
- } elseif ( $order->BillingPeriod == "Week" ) {
2001
- $trial_period_days = $order->BillingFrequency * 7; //weekly
2002
- } else {
2003
- $trial_period_days = $order->BillingFrequency * 30; //assume monthly
2004
  }
2005
 
2006
- //convert to a profile start date
2007
- $order->ProfileStartDate = date_i18n( "Y-m-d", strtotime( "+ " . $trial_period_days . " Day", current_time( "timestamp" ) ) ) . "T0:0:0";
2008
 
2009
- //filter the start date
2010
- $order->ProfileStartDate = apply_filters( "pmpro_profile_start_date", $order->ProfileStartDate, $order );
 
2011
 
2012
- //convert back to days
2013
- $trial_period_days = ceil( abs( strtotime( date_i18n( "Y-m-d" ), current_time( "timestamp" ) ) - strtotime( $order->ProfileStartDate, current_time( "timestamp" ) ) ) / 86400 );
 
 
 
 
 
2014
 
2015
- //for free trials, just push the start date of the subscription back
2016
- if ( ! empty( $order->TrialBillingCycles ) && $order->TrialAmount == 0 ) {
2017
- $trialOccurrences = (int) $order->TrialBillingCycles;
2018
- if ( $order->BillingPeriod == "Year" ) {
2019
- $trial_period_days = $trial_period_days + ( 365 * $order->BillingFrequency * $trialOccurrences ); //annual
2020
- } elseif ( $order->BillingPeriod == "Day" ) {
2021
- $trial_period_days = $trial_period_days + ( 1 * $order->BillingFrequency * $trialOccurrences ); //daily
2022
- } elseif ( $order->BillingPeriod == "Week" ) {
2023
- $trial_period_days = $trial_period_days + ( 7 * $order->BillingFrequency * $trialOccurrences ); //weekly
2024
- } else {
2025
- $trial_period_days = $trial_period_days + ( 30 * $order->BillingFrequency * $trialOccurrences ); //assume monthly
2026
  }
2027
- } elseif ( ! empty( $order->TrialBillingCycles ) ) {
2028
- /*
2029
- Let's set the subscription to the trial and give the user an "update" to change the sub later to full price (since v2.0)
2030
 
2031
- This will force TrialBillingCycles > 1 to act as if they were 1
 
2032
  */
2033
- $new_user_updates = array();
2034
- $new_user_updates[] = array(
2035
- 'when' => 'payment',
2036
- 'billing_amount' => $order->PaymentAmount,
2037
- 'cycle_period' => $order->BillingPeriod,
2038
- 'cycle_number' => $order->BillingFrequency
2039
- );
2040
-
2041
- //now amount to equal the trial #s
2042
- $amount = $order->TrialAmount;
2043
- $amount_tax = $order->getTaxForPrice( $amount );
2044
- $amount = pmpro_round_price( (float) $amount + (float) $amount_tax );
2045
- }
2046
-
2047
- //create a plan
2048
- try {
2049
- $plan = array(
2050
- "amount" => $amount * $currency_unit_multiplier,
2051
- "interval_count" => $order->BillingFrequency,
2052
- "interval" => strtolower( $order->BillingPeriod ),
2053
- "trial_period_days" => $trial_period_days,
2054
- 'product' => array( 'name' => $order->membership_name . " for order " . $order->code ),
2055
- "currency" => strtolower( $pmpro_currency ),
2056
- "id" => $order->code
2057
- );
2058
-
2059
- $plan = Stripe_Plan::create( apply_filters( 'pmpro_stripe_create_plan_array', $plan ) );
2060
- } catch ( \Throwable $e ) {
2061
- $order->error = __( "Error creating plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
2062
- $order->shorterror = $order->error;
2063
 
2064
- return false;
2065
- } catch ( \Exception $e ) {
2066
- $order->error = __( "Error creating plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
2067
  $order->shorterror = $order->error;
2068
 
2069
- return false;
2070
  }
 
2071
 
2072
- // before subscribing, let's clear out the updates so we don't trigger any during sub
2073
- if ( ! empty( $user_id ) ) {
2074
- $old_user_updates = get_user_meta( $user_id, "pmpro_stripe_updates", true );
2075
- update_user_meta( $user_id, "pmpro_stripe_updates", array() );
2076
- }
2077
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2078
 
2079
- if ( empty( $order->subscription_transaction_id ) && ! empty( $this->customer['id'] ) ) {
2080
- $order->subscription_transaction_id = $this->customer['id'];
 
 
 
 
 
 
2081
  }
2082
 
2083
- // subscribe to the plan
2084
- try {
2085
- $subscription = array( "plan" => $order->code );
2086
- $result = $this->customer->subscriptions->create( apply_filters( 'pmpro_stripe_create_subscription_array', $subscription ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2087
  } catch ( \Throwable $e ) {
2088
- //try to delete the plan
2089
- $plan->delete();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2090
 
2091
- //give the user any old updates back
2092
- if ( ! empty( $user_id ) ) {
2093
- update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2094
  }
 
 
 
 
 
 
 
 
 
2095
 
2096
- //return error
2097
- $order->error = __( "Error subscribing customer to plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
2098
- $order->shorterror = $order->error;
 
 
 
 
 
 
2099
 
2100
- return false;
2101
- } catch ( \Exception $e ) {
2102
- //try to delete the plan
2103
- $plan->delete();
2104
 
2105
- //give the user any old updates back
2106
- if ( ! empty( $user_id ) ) {
2107
- update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
 
 
 
 
 
 
 
 
 
 
 
 
2108
  }
 
 
 
2109
 
2110
- //return error
2111
- $order->error = __( "Error subscribing customer to plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
2112
- $order->shorterror = $order->error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2113
 
 
 
2114
  return false;
 
 
 
 
 
 
 
 
2115
  }
2116
 
2117
- // delete the plan
2118
- $plan = Stripe_Plan::retrieve( $order->code );
2119
- $plan->delete();
2120
 
2121
- //if we got this far, we're all good
2122
- $order->status = "success";
2123
- $order->subscription_transaction_id = $result['id'];
 
 
 
 
 
 
2124
 
2125
- //save new updates if this is at checkout
2126
- if ( $checkout ) {
2127
- //empty out updates unless set above
2128
- if ( empty( $new_user_updates ) ) {
2129
- $new_user_updates = array();
2130
- }
2131
 
2132
- //update user meta
2133
- if ( ! empty( $user_id ) ) {
2134
- update_user_meta( $user_id, "pmpro_stripe_updates", $new_user_updates );
2135
- } else {
2136
- //need to remember the user updates to save later
2137
- global $pmpro_stripe_updates;
2138
- $pmpro_stripe_updates = $new_user_updates;
2139
- function pmpro_user_register_stripe_updates( $user_id ) {
2140
- global $pmpro_stripe_updates;
2141
- update_user_meta( $user_id, "pmpro_stripe_updates", $pmpro_stripe_updates );
2142
- }
2143
 
2144
- add_action( "user_register", "pmpro_user_register_stripe_updates" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2145
  }
2146
- } else {
2147
- //give them their old updates back
2148
- update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
2149
  }
 
 
2150
 
2151
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2152
  }
2153
 
2154
  /**
2155
  * Helper method to save the subscription ID to make sure the membership doesn't get cancelled by the webhook
 
 
2156
  */
2157
- static function ignoreCancelWebhookForThisSubscription( $subscription_id, $user_id = null ) {
 
 
2158
  if ( empty( $user_id ) ) {
2159
  global $current_user;
2160
  $user_id = $current_user->ID;
@@ -2174,9 +3012,14 @@ class PMProGateway_stripe extends PMProGateway {
2174
  }
2175
 
2176
  /**
2177
- * Helper method to process a Stripe subscription update
 
 
 
 
2178
  */
2179
  static function updateSubscription( $update, $user_id ) {
 
2180
  global $wpdb;
2181
 
2182
  //get level for user
@@ -2186,9 +3029,9 @@ class PMProGateway_stripe extends PMProGateway {
2186
  $last_order = new MemberOrder();
2187
  $last_order->getLastMemberOrder( $user_id );
2188
  $last_order->setGateway( 'stripe' );
2189
- $last_order->Gateway->getCustomer( $last_order );
2190
 
2191
- $subscription = $last_order->Gateway->getSubscription( $last_order );
2192
 
2193
  if ( ! empty( $subscription ) ) {
2194
  $end_timestamp = $subscription->current_period_end;
@@ -2236,7 +3079,8 @@ class PMProGateway_stripe extends PMProGateway {
2236
  }, 10, 2 );
2237
 
2238
  //update subscription
2239
- $update_order->Gateway->set_customer( $update_order, true );
 
2240
  $update_order->Gateway->process_subscriptions( $update_order );
2241
 
2242
  //update membership
@@ -2260,46 +3104,27 @@ class PMProGateway_stripe extends PMProGateway {
2260
  }
2261
 
2262
  /**
2263
- * Helper method to update the customer info via getCustomer
2264
  *
2265
- * @since 1.4
 
 
 
 
2266
  */
2267
- function update( &$order ) {
2268
-
2269
- $steps = array(
2270
- 'set_customer',
2271
- 'set_payment_method',
2272
- 'attach_payment_method_to_customer',
2273
- 'update_payment_method_for_subscriptions',
2274
- );
2275
-
2276
- foreach ( $steps as $key => $step ) {
2277
- do_action( "pmpro_update_billing_before_{$step}", $order );
2278
- $this->$step( $order );
2279
- do_action( "pmpro_update_billing_after_{$step}", $order );
2280
- if ( ! empty( $order->error ) ) {
2281
- return false;
2282
- }
2283
- }
2284
-
2285
- return true;
2286
 
2287
- }
2288
-
2289
- /**
2290
- * Update the payment method for a subscription.
2291
- */
2292
- function update_payment_method_for_subscriptions( &$order ) {
2293
  // get customer
2294
- $this->getCustomer( $order );
2295
 
2296
- if ( empty( $this->customer ) ) {
2297
  return false;
2298
  }
2299
 
2300
  // get all subscriptions
2301
- if ( ! empty( $this->customer->subscriptions ) ) {
2302
- $subscriptions = $this->customer->subscriptions->all();
2303
  }
2304
 
2305
  foreach( $subscriptions as $subscription ) {
@@ -2309,77 +3134,28 @@ class PMProGateway_stripe extends PMProGateway {
2309
  }
2310
 
2311
  // check if we have a related order for it
2312
- $one_order = new MemberOrder();
2313
- $one_order->getLastMemberOrderBySubscriptionTransactionID( $subscription->id );
2314
- if ( empty( $one_order ) || empty( $one_order->id ) ) {
2315
- continue;
2316
- }
2317
-
2318
- // update the payment method
2319
- $subscription->default_payment_method = $this->customer->invoice_settings->default_payment_method;
2320
- $subscription->save();
2321
- }
2322
- }
2323
-
2324
- /**
2325
- * Cancel a subscription at Stripe
2326
- *
2327
- * @since 1.4
2328
- */
2329
- function cancel( &$order, $update_status = true ) {
2330
- global $pmpro_stripe_event;
2331
-
2332
- //no matter what happens below, we're going to cancel the order in our system
2333
- if ( $update_status ) {
2334
- $order->updateStatus( "cancelled" );
2335
- }
2336
-
2337
- //require a subscription id
2338
- if ( empty( $order->subscription_transaction_id ) ) {
2339
- return false;
2340
- }
2341
-
2342
- //find the customer
2343
- $result = $this->getCustomer( $order );
2344
-
2345
- if ( ! empty( $result ) ) {
2346
- //find subscription with this order code
2347
- $subscription = $this->getSubscription( $order );
2348
-
2349
- if ( ! empty( $subscription )
2350
- && ( empty( $pmpro_stripe_event ) || empty( $pmpro_stripe_event->type ) || $pmpro_stripe_event->type != 'customer.subscription.deleted' ) ) {
2351
- if ( $this->cancelSubscriptionAtGateway( $subscription ) ) {
2352
- //we're okay, going to return true later
2353
- } else {
2354
- $order->error = __( "Could not cancel old subscription.", 'paid-memberships-pro' );
2355
- $order->shorterror = $order->error;
2356
-
2357
- return false;
2358
- }
2359
- }
2360
-
2361
- /*
2362
- Clear updates for this user. (But not if checking out, we would have already done that.)
2363
- */
2364
- if ( empty( $_REQUEST['submit-checkout'] ) ) {
2365
- update_user_meta( $order->user_id, "pmpro_stripe_updates", array() );
2366
  }
2367
-
2368
- return true;
2369
- } else {
2370
- $order->error = __( "Could not find the customer.", 'paid-memberships-pro' );
2371
- $order->shorterror = $order->error;
2372
-
2373
- return false; //no customer found
2374
  }
 
2375
  }
2376
 
2377
  /**
2378
  * Helper method to cancel a subscription at Stripe and also clear up any upaid invoices.
2379
  *
2380
  * @since 1.8
 
2381
  */
2382
- function cancelSubscriptionAtGateway( $subscription, $preserve_local_membership = false ) {
 
 
2383
  // Check if a valid sub.
2384
  if ( empty( $subscription ) || empty( $subscription->id ) ) {
2385
  return false;
@@ -2403,10 +3179,10 @@ class PMProGateway_stripe extends PMProGateway {
2403
  }
2404
 
2405
  // Okay have an order, so get customer so we can cancel invoices too
2406
- $this->getCustomer( $order );
2407
 
2408
  // Get open invoices.
2409
- $invoices = Stripe_Invoice::all(['customer' => $this->customer->id, 'status' => 'open']);
2410
 
2411
  // Found it, cancel it.
2412
  try {
@@ -2421,7 +3197,7 @@ class PMProGateway_stripe extends PMProGateway {
2421
 
2422
  // Sometimes we don't want to cancel the local membership when Stripe sends its webhook.
2423
  if ( $preserve_local_membership ) {
2424
- PMProGateway_stripe::ignoreCancelWebhookForThisSubscription( $subscription->id, $order->user_id );
2425
  }
2426
 
2427
  // Cancel
@@ -2436,580 +3212,1196 @@ class PMProGateway_stripe extends PMProGateway {
2436
  }
2437
 
2438
  /**
2439
- * Filter pmpro_next_payment to get date via API if possible
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2440
  *
2441
- * @since 1.8.6
 
 
2442
  */
2443
- static function pmpro_next_payment( $timestamp, $user_id, $order_status ) {
2444
- //find the last order for this user
2445
- if ( ! empty( $user_id ) ) {
2446
- //get last order
2447
- $order = new MemberOrder();
2448
- $order->getLastMemberOrder( $user_id, $order_status );
 
 
 
 
 
 
 
 
 
 
2449
 
2450
- //check if this is a Stripe order with a subscription transaction id
2451
- if ( ! empty( $order->id ) && ! empty( $order->subscription_transaction_id ) && $order->gateway == "stripe" ) {
2452
- //get the subscription and return the current_period end or false
2453
- $subscription = $order->Gateway->getSubscription( $order );
2454
 
2455
- if ( ! empty( $subscription ) ) {
2456
- $customer = $order->Gateway->getCustomer();
2457
- if ( ! $customer->delinquent && ! empty ( $subscription->current_period_end ) ) {
2458
- $offset = get_option( 'gmt_offset' );
2459
- $timestamp = $subscription->current_period_end + ( $offset * 3600 );
2460
- } elseif ( $customer->delinquent && ! empty( $subscription->current_period_start ) ) {
2461
- $offset = get_option( 'gmt_offset' );
2462
- $timestamp = $subscription->current_period_start + ( $offset * 3600 );
2463
- } else {
2464
- $timestamp = $false; // shouldn't really get here
2465
- }
2466
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2467
  }
2468
  }
 
 
2469
 
2470
- return $timestamp;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2471
  }
2472
 
2473
  /**
2474
- * Refund a payment or invoice
2475
  *
2476
- * @param object &$order Related PMPro order object.
2477
- * @param string $transaction_id Payment or Invoice id to void.
2478
  *
2479
- * @return bool True or false if the void worked
2480
  */
2481
- function void( &$order, $transaction_id = null ) {
2482
- //stripe doesn't differentiate between voids and refunds, so let's just pass on to the refund function
2483
- return $this->refund( $order, $transaction_id );
 
 
2484
  }
2485
 
2486
  /**
2487
- * Refund a payment or invoice
2488
  *
2489
- * @param object &$order Related PMPro order object.
2490
- * @param string $transaction_id Payment or invoice id to void.
2491
  *
2492
- * @return bool True or false if the refund worked.
 
2493
  */
2494
- function refund( &$order, $transaction_id = null ) {
2495
- //default to using the payment id from the order
2496
- if ( empty( $transaction_id ) && ! empty( $order->payment_transaction_id ) ) {
2497
- $transaction_id = $order->payment_transaction_id;
2498
- }
2499
 
2500
- //need a transaction id
2501
- if ( empty( $transaction_id ) ) {
2502
- return false;
2503
  }
2504
 
2505
- //if an invoice ID is passed, get the charge/payment id
2506
- if ( strpos( $transaction_id, "in_" ) !== false ) {
2507
- $invoice = Stripe_Invoice::retrieve( $transaction_id );
2508
 
2509
- if ( ! empty( $invoice ) && ! empty( $invoice->charge ) ) {
2510
- $transaction_id = $invoice->charge;
2511
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2512
  }
 
 
 
 
 
 
 
 
 
2513
 
2514
- //get the charge
2515
- try {
2516
- $charge = Stripe_Charge::retrieve( $transaction_id );
2517
- } catch ( \Throwable $e ) {
2518
- $charge = false;
2519
- } catch ( \Exception $e ) {
2520
- $charge = false;
 
 
 
 
 
 
 
 
 
 
 
2521
  }
2522
 
2523
- //can't find the charge?
2524
- if ( empty( $charge ) ) {
2525
- $order->status = "error";
2526
- $order->errorcode = "";
2527
- $order->error = "";
2528
- $order->shorterror = "";
2529
 
 
2530
  return false;
2531
  }
2532
 
2533
- //attempt refund
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2534
  try {
2535
- $refund = $charge->refund();
 
 
 
2536
  } catch ( \Throwable $e ) {
2537
- $order->errorcode = true;
2538
- $order->error = __( "Error: ", 'paid-memberships-pro' ) . $e->getMessage();
2539
- $order->shorterror = $order->error;
2540
-
2541
  return false;
2542
  } catch ( \Exception $e ) {
2543
- $order->errorcode = true;
2544
- $order->error = __( "Error: ", 'paid-memberships-pro' ) . $e->getMessage();
2545
- $order->shorterror = $order->error;
2546
-
2547
  return false;
2548
  }
2549
 
2550
- if ( $refund->status == "succeeded" ) {
2551
- $order->status = "refunded";
2552
- $order->saveOrder();
2553
 
 
 
 
 
 
 
 
 
 
 
2554
  return true;
2555
- } else {
2556
- $order->status = "error";
2557
- $order->errorcode = true;
2558
- $order->error = sprintf( __( "Error: Unkown error while refunding charge #%s", 'paid-memberships-pro' ), $transaction_id );
2559
- $order->shorterror = $order->error;
2560
 
2561
- return false;
 
 
 
2562
  }
2563
- }
2564
 
2565
- function set_payment_method( &$order, $force = false ) {
2566
- if ( ! empty( $this->payment_method ) && ! $force ) {
2567
- return true;
 
 
2568
  }
 
 
2569
 
2570
- $payment_method = $this->get_payment_method( $order );
 
 
 
 
 
 
 
 
2571
 
2572
- if ( empty( $payment_method ) ) {
2573
  return false;
2574
  }
2575
 
2576
- $this->payment_method = $payment_method;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2577
 
2578
  return true;
2579
  }
2580
 
2581
- function get_payment_method( &$order ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2582
 
2583
- if ( ! empty( $order->payment_method_id ) ) {
2584
- try {
2585
- $payment_method = Stripe_PaymentMethod::retrieve( $order->payment_method_id );
2586
- } catch ( Stripe\Error\Base $e ) {
2587
- $order->error = $e->getMessage();
2588
- return false;
2589
- } catch ( \Throwable $e ) {
2590
- $order->error = $e->getMessage();
2591
- return false;
2592
- } catch ( \Exception $e ) {
2593
- $order->error = $e->getMessage();
 
 
 
2594
  return false;
2595
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2596
  }
2597
 
2598
- if ( empty( $payment_method ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2599
  return false;
2600
  }
2601
 
2602
- return $payment_method;
2603
  }
2604
 
2605
- function set_customer( &$order, $force = false ) {
2606
- if ( ! empty( $this->customer ) && ! $force ) {
2607
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
2608
  }
2609
- $this->getCustomer( $order );
2610
- }
2611
 
2612
- function attach_payment_method_to_customer( &$order ) {
 
2613
 
2614
- if ( ! empty( $this->customer->invoice_settings->default_payment_method ) &&
2615
- $this->customer->invoice_settings->default_payment_method === $this->payment_method->id ) {
2616
- return true;
 
 
 
 
 
 
 
2617
  }
2618
 
 
2619
  try {
2620
- $this->payment_method->attach( [ 'customer' => $this->customer->id ] );
2621
- $this->customer->invoice_settings->default_payment_method = $this->payment_method->id;
2622
- $this->customer->save();
2623
- } catch ( Stripe\Error\Base $e ) {
2624
- $order->error = $e->getMessage();
2625
- return false;
 
 
 
 
 
 
 
 
 
 
 
2626
  } catch ( \Throwable $e ) {
2627
- $order->error = $e->getMessage();
 
 
 
 
2628
  return false;
2629
  } catch ( \Exception $e ) {
2630
- $order->error = $e->getMessage();
 
 
 
 
2631
  return false;
2632
  }
2633
 
2634
- return true;
2635
- }
2636
-
2637
- function process_charges( &$order ) {
 
2638
 
2639
- if ( 0 == floatval( $order->InitialPayment ) ) {
2640
  return true;
2641
- }
2642
-
2643
- $this->set_payment_intent( $order );
2644
- $this->confirm_payment_intent( $order );
2645
-
2646
- if ( ! empty( $order->error ) ) {
2647
- $order->error = $order->error;
2648
 
2649
  return false;
2650
  }
 
2651
 
2652
- return true;
 
 
 
 
 
 
 
 
 
 
2653
  }
2654
 
2655
- function set_payment_intent( &$order, $force = false ) {
 
 
 
 
 
 
 
 
2656
 
2657
- if ( ! empty( $this->payment_intent ) && ! $force ) {
2658
- return true;
 
2659
  }
2660
 
2661
- $payment_intent = $this->get_payment_intent( $order );
2662
 
2663
- if ( empty( $payment_intent ) ) {
 
2664
  return false;
2665
  }
2666
 
2667
- $this->payment_intent = $payment_intent;
2668
-
2669
- return true;
2670
- }
2671
-
2672
- function get_payment_intent( &$order ) {
2673
 
2674
- if ( ! empty( $order->payment_intent_id ) ) {
 
2675
  try {
2676
- $payment_intent = Stripe_PaymentIntent::retrieve( $order->payment_intent_id );
2677
- } catch ( Stripe\Error\Base $e ) {
2678
- $order->error = $e->getMessage();
2679
- return false;
2680
  } catch ( \Throwable $e ) {
2681
- $order->error = $e->getMessage();
 
 
2682
  return false;
2683
  } catch ( \Exception $e ) {
2684
- $order->error = $e->getMessage();
 
 
2685
  return false;
2686
  }
2687
- }
2688
 
2689
- if ( empty( $payment_intent ) ) {
2690
- $payment_intent = $this->create_payment_intent( $order );
2691
  }
2692
 
2693
- if ( empty( $payment_intent ) ) {
 
 
 
 
2694
  return false;
2695
  }
2696
 
2697
- return $payment_intent;
 
 
 
 
 
 
 
 
 
 
 
2698
  }
2699
 
2700
- function create_payment_intent( &$order ) {
 
 
 
 
 
 
 
 
 
 
 
2701
 
2702
- global $pmpro_currencies, $pmpro_currency;
 
 
 
2703
 
2704
- // Account for zero-decimal currencies like the Japanese Yen
2705
- $currency_unit_multiplier = 100; //ie 100 cents per USD
2706
- if ( is_array( $pmpro_currencies[ $pmpro_currency ] ) && isset( $pmpro_currencies[ $pmpro_currency ]['decimals'] ) && $pmpro_currencies[ $pmpro_currency ]['decimals'] == 0 ) {
2707
- $currency_unit_multiplier = 1;
 
 
 
 
 
2708
  }
2709
 
2710
- $amount = $order->InitialPayment;
2711
- $order->subtotal = $amount;
2712
- $tax = $order->getTax( true );
2713
 
2714
- $amount = pmpro_round_price( (float) $order->subtotal + (float) $tax );
 
 
 
 
2715
 
2716
- $params = array(
2717
- 'customer' => $this->customer->id,
2718
- 'payment_method' => $this->payment_method->id,
2719
- 'amount' => $amount * $currency_unit_multiplier,
2720
- 'currency' => $pmpro_currency,
2721
- 'confirmation_method' => 'manual',
2722
- 'description' => apply_filters( 'pmpro_stripe_order_description', "Order #" . $order->code . ", " . trim( $order->FirstName . " " . $order->LastName ) . " (" . $order->Email . ")", $order ),
2723
- 'setup_future_usage' => 'off_session',
2724
- );
2725
 
2726
- /**
2727
- * Filter params used to create the payment intent.
2728
- *
2729
- * @since 2.4.1
2730
- *
2731
- * @param array $params Array of params sent to Stripe.
2732
- * @param object $order Order object for this checkout.
2733
- */
2734
- $params = apply_filters( 'pmpro_stripe_payment_intent_params', $params, $order );
 
 
2735
 
 
2736
  try {
2737
- $payment_intent = Stripe_PaymentIntent::create( $params );
2738
- } catch ( Stripe\Error\Base $e ) {
2739
- $order->error = $e->getMessage();
2740
- return false;
 
 
 
 
 
 
 
2741
  } catch ( \Throwable $e ) {
2742
- $order->error = $e->getMessage();
 
 
2743
  return false;
2744
  } catch ( \Exception $e ) {
2745
- $order->error = $e->getMessage();
2746
- return false;
2747
- }
2748
-
2749
- return $payment_intent;
2750
- }
2751
-
2752
- function process_subscriptions( &$order ) {
2753
 
2754
- if ( ! pmpro_isLevelRecurring( $order->membership_level ) ) {
2755
- return true;
2756
  }
2757
 
2758
- //before subscribing, let's clear out the updates so we don't trigger any during sub
2759
  if ( ! empty( $user_id ) ) {
2760
  $old_user_updates = get_user_meta( $user_id, "pmpro_stripe_updates", true );
2761
  update_user_meta( $user_id, "pmpro_stripe_updates", array() );
2762
  }
2763
 
2764
- $this->set_setup_intent( $order );
2765
- $this->confirm_setup_intent( $order );
2766
 
2767
- if ( ! empty( $order->error ) ) {
2768
- $order->error = $order->error;
 
 
 
 
 
 
 
 
 
2769
 
2770
  //give the user any old updates back
2771
  if ( ! empty( $user_id ) ) {
2772
  update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
2773
  }
2774
 
 
 
 
 
2775
  return false;
2776
- }
 
 
2777
 
2778
- //save new updates if this is at checkout
2779
- //empty out updates unless set above
2780
- if ( empty( $new_user_updates ) ) {
2781
- $new_user_updates = array();
 
 
 
 
 
 
2782
  }
2783
 
2784
- //update user meta
2785
- if ( ! empty( $user_id ) ) {
2786
- update_user_meta( $user_id, "pmpro_stripe_updates", $new_user_updates );
2787
- } else {
2788
- //need to remember the user updates to save later
2789
- global $pmpro_stripe_updates;
2790
- $pmpro_stripe_updates = $new_user_updates;
2791
-
2792
- if( ! function_exists( 'pmpro_user_register_stripe_updates' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
2793
  function pmpro_user_register_stripe_updates( $user_id ) {
2794
  global $pmpro_stripe_updates;
2795
- update_user_meta( $user_id, 'pmpro_stripe_updates', $pmpro_stripe_updates );
2796
  }
2797
- add_action( 'user_register', 'pmpro_user_register_stripe_updates' );
 
2798
  }
 
 
 
2799
  }
2800
 
2801
  return true;
2802
  }
2803
 
2804
- function create_plan( &$order ) {
2805
-
2806
- global $pmpro_currencies, $pmpro_currency;
2807
-
2808
- //figure out the amounts
2809
- $amount = $order->PaymentAmount;
2810
- $amount_tax = $order->getTaxForPrice( $amount );
2811
- $amount = pmpro_round_price( (float) $amount + (float) $amount_tax );
 
 
 
 
 
 
 
2812
 
2813
- // Account for zero-decimal currencies like the Japanese Yen
2814
- $currency_unit_multiplier = 100; //ie 100 cents per USD
2815
- if ( is_array( $pmpro_currencies[ $pmpro_currency ] ) && isset( $pmpro_currencies[ $pmpro_currency ]['decimals'] ) && $pmpro_currencies[ $pmpro_currency ]['decimals'] == 0 ) {
2816
- $currency_unit_multiplier = 1;
 
 
 
 
 
 
 
 
 
 
 
2817
  }
2818
 
2819
- /*
2820
- Figure out the trial length (first payment handled by initial charge)
2821
-
2822
- There are two parts to the trial. Part 1 is simply the delay until the first payment
2823
- since we are doing the first payment as a separate transaction.
2824
- The second part is the actual "trial" set by the admin.
2825
-
2826
- Stripe only supports Year or Month for billing periods, but we account for Days and Weeks just in case.
2827
- */
2828
- if ( $order->BillingPeriod == "Year" ) {
2829
- $trial_period_days = $order->BillingFrequency * 365; //annual
2830
- } elseif ( $order->BillingPeriod == "Day" ) {
2831
- $trial_period_days = $order->BillingFrequency * 1; //daily
2832
- } elseif ( $order->BillingPeriod == "Week" ) {
2833
- $trial_period_days = $order->BillingFrequency * 7; //weekly
2834
- } else {
2835
- $trial_period_days = $order->BillingFrequency * 30; //assume monthly
2836
  }
2837
 
2838
- //convert to a profile start date
2839
- $order->ProfileStartDate = date_i18n( "Y-m-d", strtotime( "+ " . $trial_period_days . " Day", current_time( "timestamp" ) ) ) . "T0:0:0";
2840
-
2841
- //filter the start date
2842
- $order->ProfileStartDate = apply_filters( "pmpro_profile_start_date", $order->ProfileStartDate, $order );
2843
-
2844
- //convert back to days
2845
- $trial_period_days = ceil( abs( strtotime( date_i18n( "Y-m-d" ), current_time( "timestamp" ) ) - strtotime( $order->ProfileStartDate, current_time( "timestamp" ) ) ) / 86400 );
2846
 
2847
- //for free trials, just push the start date of the subscription back
2848
- if ( ! empty( $order->TrialBillingCycles ) && $order->TrialAmount == 0 ) {
2849
- $trialOccurrences = (int) $order->TrialBillingCycles;
2850
- if ( $order->BillingPeriod == "Year" ) {
2851
- $trial_period_days = $trial_period_days + ( 365 * $order->BillingFrequency * $trialOccurrences ); //annual
2852
- } elseif ( $order->BillingPeriod == "Day" ) {
2853
- $trial_period_days = $trial_period_days + ( 1 * $order->BillingFrequency * $trialOccurrences ); //daily
2854
- } elseif ( $order->BillingPeriod == "Week" ) {
2855
- $trial_period_days = $trial_period_days + ( 7 * $order->BillingFrequency * $trialOccurrences ); //weekly
2856
- } else {
2857
- $trial_period_days = $trial_period_days + ( 30 * $order->BillingFrequency * $trialOccurrences ); //assume monthly
2858
  }
2859
- } elseif ( ! empty( $order->TrialBillingCycles ) ) {
2860
-
2861
  }
2862
 
2863
- // Save $trial_period_days to order for now too.
2864
- $order->TrialPeriodDays = $trial_period_days;
2865
-
2866
- //create a plan
2867
  try {
2868
- $plan = array(
2869
- "amount" => $amount * $currency_unit_multiplier,
2870
- "interval_count" => $order->BillingFrequency,
2871
- "interval" => strtolower( $order->BillingPeriod ),
2872
- "trial_period_days" => $trial_period_days,
2873
- 'product' => array( 'name' => $order->membership_name . " for order " . $order->code ),
2874
- "currency" => strtolower( $pmpro_currency ),
2875
- "id" => $order->code
2876
- );
2877
- $order->plan = Stripe_Plan::create( apply_filters( 'pmpro_stripe_create_plan_array', $plan ) );
2878
- } catch ( Stripe\Error\Base $e ) {
2879
- $order->error = $e->getMessage();
2880
-
2881
- return false;
2882
  } catch ( \Throwable $e ) {
2883
- $order->error = $e->getMessage();
2884
-
2885
- return false;
2886
  } catch ( \Exception $e ) {
2887
- $order->error = $e->getMessage();
2888
-
2889
- return false;
2890
  }
2891
 
2892
- return $order->plan;
2893
- }
2894
-
2895
- function create_subscription( &$order ) {
 
 
2896
 
2897
- //subscribe to the plan
2898
- try {
2899
- $params = array(
2900
- 'customer' => $this->customer->id,
2901
- 'default_payment_method' => $this->payment_method,
2902
- 'items' => array(
2903
- array( 'plan' => $order->code ),
2904
- ),
2905
- 'trial_period_days' => $order->TrialPeriodDays,
2906
- 'expand' => array(
2907
- 'pending_setup_intent.payment_method',
2908
- ),
2909
- );
2910
- $order->subscription = Stripe_Subscription::create( $params );
2911
- } catch ( Stripe\Error\Base $e ) {
2912
- $order->error = $e->getMessage();
2913
- return false;
2914
- } catch ( \Throwable $e ) {
2915
- $order->error = $e->getMessage();
2916
- return false;
2917
- } catch ( \Exception $e ) {
2918
- $order->error = $e->getMessage();
2919
  return false;
2920
  }
2921
 
2922
- return $order->subscription;
2923
-
2924
- }
2925
-
2926
- function delete_plan( &$order ) {
2927
  try {
2928
- $order->plan->delete();
2929
- } catch ( Stripe\Error\Base $e ) {
2930
- $order->error = $e->getMessage();
2931
-
2932
- return false;
2933
  } catch ( \Throwable $e ) {
2934
- $order->error = $e->getMessage();
 
 
2935
 
2936
  return false;
2937
  } catch ( \Exception $e ) {
2938
- $order->error = $e->getMessage();
 
 
2939
 
2940
  return false;
2941
  }
2942
 
2943
- return true;
2944
- }
2945
-
2946
- function get_setup_intent( &$order ) {
2947
-
2948
- if ( ! empty( $order->setup_intent_id ) ) {
2949
- try {
2950
- $setup_intent = Stripe_SetupIntent::retrieve( $order->setup_intent_id );
2951
- } catch ( Stripe\Error\Base $e ) {
2952
- $order->error = $e->getMessage();
2953
- return false;
2954
- } catch ( \Throwable $e ) {
2955
- $order->error = $e->getMessage();
2956
- return false;
2957
- } catch ( \Exception $e ) {
2958
- $order->error = $e->getMessage();
2959
- return false;
2960
- }
2961
- }
2962
 
2963
- if ( empty( $setup_intent ) ) {
2964
- $setup_intent = $this->create_setup_intent( $order );
2965
- }
 
 
 
2966
 
2967
- if ( empty( $setup_intent ) ) {
2968
  return false;
2969
  }
2970
-
2971
- return $setup_intent;
2972
  }
2973
 
2974
- function set_setup_intent( &$order, $force = false ) {
2975
-
2976
- if ( ! empty( $this->setup_intent ) && ! $force ) {
 
 
 
2977
  return true;
2978
  }
2979
 
2980
- $setup_intent = $this->get_setup_intent( $order );
2981
 
2982
- if ( empty( $setup_intent ) ) {
2983
  return false;
2984
  }
2985
 
2986
- $this->setup_intent = $setup_intent;
2987
 
2988
  return true;
2989
  }
2990
 
2991
- function create_setup_intent( &$order ) {
2992
-
2993
- $this->create_plan( $order );
2994
- $this->subscription = $this->create_subscription( $order );
2995
- $this->delete_plan( $order );
2996
-
2997
- if ( ! empty( $order->error ) || empty( $this->subscription->pending_setup_intent ) ) {
2998
- return false;
2999
  }
3000
-
3001
- return $this->subscription->pending_setup_intent;
 
3002
  }
3003
 
3004
- function confirm_payment_intent( &$order ) {
 
 
 
 
 
 
 
 
 
 
3005
 
3006
  try {
3007
- $params = array(
3008
- 'expand' => array(
3009
- 'payment_method',
3010
- ),
3011
- );
3012
- $this->payment_intent->confirm( $params );
3013
  } catch ( Stripe\Error\Base $e ) {
3014
  $order->error = $e->getMessage();
3015
  return false;
@@ -3021,40 +4413,76 @@ class PMProGateway_stripe extends PMProGateway {
3021
  return false;
3022
  }
3023
 
3024
- if ( 'requires_action' == $this->payment_intent->status ) {
3025
- $order->errorcode = true;
3026
- $order->error = __( 'Customer authentication is required to complete this transaction. Please complete the verification steps issued by your payment provider.', 'paid-memberships-pro' );
3027
- $order->error_type = 'pmpro_alert';
 
 
 
 
 
 
 
 
 
3028
 
 
3029
  return false;
3030
  }
3031
 
 
 
3032
  return true;
3033
  }
3034
 
3035
- function confirm_setup_intent( &$order ) {
 
 
 
 
 
 
 
 
 
 
 
 
3036
 
3037
- if ( empty( $this->setup_intent ) ) {
3038
- return true;
3039
- }
 
3040
 
3041
- if ( 'requires_action' === $this->setup_intent->status ) {
3042
- $order->errorcode = true;
3043
- $order->error = __( 'Customer authentication is required to finish setting up your subscription. Please complete the verification steps issued by your payment provider.', 'paid-memberships-pro' );
 
 
 
 
 
 
 
 
 
 
 
3044
 
3045
  return false;
3046
- }
 
3047
 
3048
- }
 
 
3049
 
3050
- function clean_up( &$order ) {
3051
- if ( ! empty( $this->payment_intent ) && 'succeeded' == $this->payment_intent->status ) {
3052
- $order->payment_transaction_id = $this->payment_intent->charges->data[0]->id;
3053
  }
3054
 
3055
- if ( empty( $order->subscription_transaction_id ) && ! empty( $this->subscription ) ) {
3056
- $order->subscription_transaction_id = $this->subscription->id;
3057
- }
3058
  }
 
3059
 
3060
  }
3
  use Stripe\Customer as Stripe_Customer;
4
  use Stripe\Invoice as Stripe_Invoice;
5
  use Stripe\Plan as Stripe_Plan;
6
+ use Stripe\Product as Stripe_Product;
7
+ use Stripe\Price as Stripe_Price;
8
  use Stripe\Charge as Stripe_Charge;
9
  use Stripe\PaymentIntent as Stripe_PaymentIntent;
10
  use Stripe\SetupIntent as Stripe_SetupIntent;
 
11
  use Stripe\PaymentMethod as Stripe_PaymentMethod;
12
  use Stripe\Subscription as Stripe_Subscription;
13
+ use Stripe\ApplePayDomain as Stripe_ApplePayDomain;
14
  use Stripe\WebhookEndpoint as Stripe_Webhook;
15
  use Stripe\StripeClient as Stripe_Client; // Used for deleting webhook as of 2.4
16
+ use Stripe\Account as Stripe_Account;
17
 
18
  define( "PMPRO_STRIPE_API_VERSION", "2020-03-02" );
19
 
51
 
52
  if ( true === $this->dependencies() ) {
53
  $this->loadStripeLibrary();
54
+ Stripe\Stripe::setApiKey( self::get_secretkey() );
55
  Stripe\Stripe::setAPIVersion( PMPRO_STRIPE_API_VERSION );
56
+ Stripe\Stripe::setAppInfo(
57
+ 'WordPress Paid Memberships Pro',
58
+ PMPRO_VERSION,
59
+ 'https://www.paidmembershipspro.com',
60
+ 'pp_partner_DKlIQ5DD7SFW3A'
61
+ );
62
  self::$is_loaded = true;
63
  }
64
 
65
  return $this->gateway;
66
  }
67
 
68
+ /****************************************
69
+ ************ STATIC METHODS ************
70
+ ****************************************/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /**
72
  * Load the Stripe API library.
73
  *
74
  * @since 1.8
75
  * Moved into a method in version 1.8 so we only load it when needed.
76
  */
77
+ public static function loadStripeLibrary() {
78
  //load Stripe library if it hasn't been loaded already (usually by another plugin using Stripe)
79
  if ( ! class_exists( "Stripe\Stripe" ) ) {
80
  require_once( PMPRO_DIR . "/includes/lib/Stripe/init.php" );
86
  *
87
  * @since 1.8
88
  */
89
+ public static function init() {
90
  //make sure Stripe is a gateway option
91
  add_filter( 'pmpro_gateways', array( 'PMProGateway_stripe', 'pmpro_gateways' ) );
92
 
107
  //old global RE showing billing address or not
108
  global $pmpro_stripe_lite;
109
  $pmpro_stripe_lite = apply_filters( "pmpro_stripe_lite", ! pmpro_getOption( "stripe_billingaddress" ) ); //default is oposite of the stripe_billingaddress setting
110
+
111
+ $gateway = pmpro_getGateway();
112
+ if($gateway == "stripe")
113
+ {
114
+ add_filter( 'pmpro_required_billing_fields', array( 'PMProGateway_stripe', 'pmpro_required_billing_fields' ) );
115
+ }
116
 
117
  //updates cron
118
  add_action( 'pmpro_cron_stripe_subscription_updates', array(
123
  //AJAX services for creating/disabling webhooks
124
  add_action( 'wp_ajax_pmpro_stripe_create_webhook', array( 'PMProGateway_stripe', 'wp_ajax_pmpro_stripe_create_webhook' ) );
125
  add_action( 'wp_ajax_pmpro_stripe_delete_webhook', array( 'PMProGateway_stripe', 'wp_ajax_pmpro_stripe_delete_webhook' ) );
126
+ add_action( 'wp_ajax_pmpro_stripe_rebuild_webhook', array( 'PMProGateway_stripe', 'wp_ajax_pmpro_stripe_rebuild_webhook' ) );
127
 
128
  /*
129
  Filter pmpro_next_payment to get actual value
170
  ) );
171
  }
172
 
173
+ add_action( 'pmpro_payment_option_fields', array( 'PMProGateway_stripe', 'pmpro_set_up_apple_pay' ), 10, 2 );
174
  add_action( 'init', array( 'PMProGateway_stripe', 'clear_saved_subscriptions' ) );
175
+
176
+ // Stripe Connect functions.
177
+ add_action( 'admin_init', array( 'PMProGateway_stripe', 'stripe_connect_save_options' ) );
178
+ add_action( 'admin_notices', array( 'PMProGateway_stripe', 'stripe_connect_show_errors' ) );
179
+ add_action( 'admin_notices', array( 'PMProGateway_stripe', 'stripe_connect_deauthorize' ) );
180
  }
181
 
182
  /**
211
  *
212
  * @since 1.8
213
  */
214
+ public static function pmpro_gateways( $gateways ) {
215
  if ( empty( $gateways['stripe'] ) ) {
216
  $gateways['stripe'] = __( 'Stripe', 'paid-memberships-pro' );
217
  }
224
  *
225
  * @since 1.8
226
  */
227
+ public static function getGatewayOptions() {
228
  $options = array(
229
  'sslseal',
230
  'nuclear_HTTPS',
231
  'gateway_environment',
232
  'stripe_secretkey',
233
  'stripe_publishablekey',
234
+ 'live_stripe_connect_user_id',
235
+ 'live_stripe_connect_secretkey',
236
+ 'live_stripe_connect_publishablekey',
237
+ 'sandbox_stripe_connect_user_id',
238
+ 'sandbox_stripe_connect_secretkey',
239
+ 'sandbox_stripe_connect_publishablekey',
240
  'stripe_webhook',
241
  'stripe_billingaddress',
242
  'currency',
243
  'use_ssl',
244
  'tax_state',
245
  'tax_rate',
246
+ 'accepted_credit_cards',
247
+ 'stripe_payment_request_button',
248
  );
249
 
250
  return $options;
255
  *
256
  * @since 1.8
257
  */
258
+ public static function pmpro_payment_options( $options ) {
259
  //get stripe options
260
  $stripe_options = self::getGatewayOptions();
261
 
270
  *
271
  * @since 1.8
272
  */
273
+ public static function pmpro_payment_option_fields( $values, $gateway ) {
274
+ $stripe = new PMProGateway_stripe();
 
 
 
 
275
 
276
+ // Show connect fields.
277
+ $stripe->show_connect_payment_option_fields( true, $values, $gateway ); // Show live connect fields.
278
+ $stripe->show_connect_payment_option_fields( false, $values, $gateway ); // Show sandbox connect fields.
 
 
 
 
 
279
 
280
+ if ( self::using_legacy_keys() ) {
281
+ // Check if webhook is enabled or not.
282
+ $webhook = self::does_webhook_exist();
283
 
284
+ // Check to see if events are missing.
285
+ if ( is_array( $webhook ) && isset( $webhook['enabled_events'] ) ) {
286
  $events = self::check_missing_webhook_events( $webhook['enabled_events'] );
 
287
  if ( $events ) {
288
+ self::update_webhook_events();
 
 
 
 
 
289
  }
290
  }
291
 
292
+ // Break the country cache in case new credentials were saved.
293
+ delete_transient( 'pmpro_stripe_account_country' );
 
 
 
294
  }
295
 
 
 
296
  ?>
297
+ <tr class="pmpro_settings_divider gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
298
+ <td colspan="2">
 
 
 
 
 
299
  <hr />
300
+ <h2 class="pmpro_stripe_legacy_keys" <?php if( ! self::show_legacy_keys_settings() ) {?>style="display: none;"<?php }?>><?php esc_html_e( 'Stripe API Settings (Legacy)', 'paid-memberships-pro' ); ?></h2>
301
+ <?php if( ! self::show_legacy_keys_settings() ) {?>
302
+ <p>
303
+ <?php esc_html_e( 'Having trouble connecting through the button above or otherwise need to use your own API keys?', 'paid-memberships-pro' );?>
304
+ <a id="pmpro_stripe_legacy_keys_toggle" href="javascript:void(0);"><?php esc_html_e( 'Click here to use the legacy API settings.', 'paid-memberships-pro' );?></a>
305
+ </p>
306
+ <script>
307
+ // Toggle to show the Stripe legacy keys settings.
308
+ jQuery(document).ready(function(){
309
+ jQuery('#pmpro_stripe_legacy_keys_toggle').click(function(e){
310
+ var btn = jQuery('#pmpro_stripe_legacy_keys_toggle');
311
+ var div = btn.closest('.pmpro_settings_divider');
312
+ btn.parent().remove();
313
+ jQuery('.pmpro_stripe_legacy_keys').show();
314
+ jQuery('.pmpro_stripe_legacy_keys').addClass('gateway_stripe');
315
+ jQuery('#stripe_publishablekey').focus();
316
+ });
317
+ });
318
+ </script>
319
+ <?php } ?>
320
+ </td>
321
+ </tr>
322
+ <tr class="gateway pmpro_stripe_legacy_keys <?php if ( self::show_legacy_keys_settings() ) { echo 'gateway_stripe'; } ?>" <?php if ( $gateway != "stripe" || ! self::show_legacy_keys_settings() ) { ?>style="display: none;"<?php } ?>>
323
+ <th scope="row" valign="top">
324
+ <label for="stripe_publishablekey"><?php _e( 'Publishable Key', 'paid-memberships-pro' ); ?>:</label>
325
+ </th>
326
+ <td>
327
+ <input type="text" id="stripe_publishablekey" name="stripe_publishablekey" value="<?php echo esc_attr( $values['stripe_publishablekey'] ) ?>" class="regular-text code" />
328
  <?php
329
  $public_key_prefix = substr( $values['stripe_publishablekey'], 0, 3 );
330
  if ( ! empty( $values['stripe_publishablekey'] ) && $public_key_prefix != 'pk_' ) {
331
  ?>
332
+ <p class="pmpro_red"><strong><?php _e( 'Your Publishable Key appears incorrect.', 'paid-memberships-pro' ); ?></strong></p>
333
  <?php
334
  }
335
  ?>
336
+ </td>
337
+ </tr>
338
+ <tr class="gateway pmpro_stripe_legacy_keys <?php if ( self::show_legacy_keys_settings() ) { echo 'gateway_stripe'; } ?>" <?php if ( $gateway != "stripe" || ! self::show_legacy_keys_settings() ) { ?>style="display: none;"<?php } ?>>
339
+ <th scope="row" valign="top">
340
+ <label for="stripe_secretkey"><?php _e( 'Secret Key', 'paid-memberships-pro' ); ?>:</label>
341
+ </th>
342
+ <td>
343
+ <input type="text" id="stripe_secretkey" name="stripe_secretkey" value="<?php echo esc_attr( $values['stripe_secretkey'] ) ?>" autocomplete="off" class="regular-text code pmpro-admin-secure-key" />
344
+ </td>
345
+ </tr>
346
+ <tr class="gateway pmpro_stripe_legacy_keys <?php if ( self::show_legacy_keys_settings() ) { echo 'gateway_stripe'; } ?>" <?php if ( $gateway != "stripe" || ! self::show_legacy_keys_settings() ) { ?>style="display: none;"<?php } ?>>
347
+ <th scope="row" valign="top">
348
+ <label><?php esc_html_e( 'Webhook', 'paid-memberships-pro' ); ?>:</label>
349
+ </th>
350
+ <td>
351
+ <?php if ( ! empty( $webhook ) && is_array( $webhook ) && self::show_legacy_keys_settings()) { ?>
352
  <button type="button" id="pmpro_stripe_create_webhook" class="button button-secondary" style="display: none;"><span class="dashicons dashicons-update-alt"></span> <?php _e( 'Create Webhook' ,'paid-memberships-pro' ); ?></button>
353
+ <?php
354
+ if ( 'disabled' === $webhook['status'] ) {
355
+ // Check webhook status.
356
+ ?>
357
+ <div class="notice error inline">
358
+ <p id="pmpro_stripe_webhook_notice" class="pmpro_stripe_webhook_notice"><?php _e( 'A webhook is set up in Stripe, but it is disabled.', 'paid-memberships-pro' ); ?> <a id="pmpro_stripe_rebuild_webhook" href="#">Rebuild Webhook</a></p>
359
+ </div>
360
+ <?php
361
+ } elseif ( $webhook['api_version'] < PMPRO_STRIPE_API_VERSION ) {
362
+ // Check webhook API version.
363
+ ?>
364
+ <div class="notice error inline">
365
+ <p id="pmpro_stripe_webhook_notice" class="pmpro_stripe_webhook_notice"><?php _e( 'A webhook is set up in Stripe, but it is using an old API version.', 'paid-memberships-pro' ); ?> <a id="pmpro_stripe_rebuild_webhook" href="#"><?php _e( 'Rebuild Webhook', 'paid-memberships-pro' ); ?></a></p>
366
+ </div>
367
+ <?php
368
+ } else {
369
+ ?>
370
+ <div class="notice notice-success inline">
371
+ <p id="pmpro_stripe_webhook_notice" class="pmpro_stripe_webhook_notice"><?php _e( 'Your webhook is enabled.', 'paid-memberships-pro' ); ?> <a id="pmpro_stripe_delete_webhook" href="#"><?php _e( 'Disable Webhook', 'paid-memberships-pro' ); ?></a></p>
372
+ </div>
373
+ <?php
374
+ }
375
+ } elseif ( self::show_legacy_keys_settings() ) { ?>
376
+ <button type="button" id="pmpro_stripe_create_webhook" class="button button-secondary"><span class="dashicons dashicons-update-alt"></span> <?php _e( 'Create Webhook' ,'paid-memberships-pro' ); ?></button>
377
+ <div class="notice error inline">
378
+ <p id="pmpro_stripe_webhook_notice" class="pmpro_stripe_webhook_notice"><?php _e('A webhook in Stripe is required to process recurring payments, manage failed payments, and synchronize cancellations.', 'paid-memberships-pro' );?></p>
379
+ </div>
380
+ <?php
381
+ }
382
+ ?>
383
+ <p class="description"><?php esc_html_e( 'Webhook URL', 'paid-memberships-pro' ); ?>:
384
+ <code><?php echo self::get_site_webhook_url(); ?></code></p>
385
+ </td>
386
+ </tr>
387
+ <tr class="pmpro_settings_divider gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
388
+ <td colspan="2">
389
+ <hr />
390
+ <h2><?php esc_html_e( 'Other Stripe Settings', 'paid-memberships-pro' ); ?></h2>
391
+ </td>
392
+ </tr>
393
  <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
394
+ <th><?php _e( 'Stripe API Version', 'paid-memberships-pro' ); ?>:</th>
395
+ <td><code><?php echo PMPRO_STRIPE_API_VERSION; ?></code></td>
396
+ </tr>
397
+ <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
398
+ <th scope="row" valign="top">
399
+ <label for="stripe_billingaddress"><?php _e( 'Show Billing Address Fields', 'paid-memberships-pro' ); ?>:</label>
400
+ </th>
401
+ <td>
402
+ <select id="stripe_billingaddress" name="stripe_billingaddress">
403
+ <option value="0"
404
+ <?php if ( empty( $values['stripe_billingaddress'] ) ) { ?>selected="selected"<?php } ?>><?php _e( 'No', 'paid-memberships-pro' ); ?></option>
405
+ <option value="1"
406
+ <?php if ( ! empty( $values['stripe_billingaddress'] ) ) { ?>selected="selected"<?php } ?>><?php _e( 'Yes', 'paid-memberships-pro' ); ?></option>
407
+ </select>
408
  <p class="description"><?php _e( "Stripe doesn't require billing address fields. Choose 'No' to hide them on the checkout page.<br /><strong>If No, make sure you disable address verification in the Stripe dashboard settings.</strong>", 'paid-memberships-pro' ); ?></p>
409
+ </td>
410
+ </tr>
411
+ <tr class="gateway gateway_stripe" <?php if ( $gateway != "stripe" ) { ?>style="display: none;"<?php } ?>>
412
+ <th scope="row" valign="top">
413
+ <label for="stripe_payment_request_button"><?php _e( 'Enable Payment Request Button', 'paid-memberships-pro' ); ?>:</label>
414
+ </th>
415
+ <td>
416
+ <select id="stripe_payment_request_button" name="stripe_payment_request_button">
417
+ <option value="0"
418
+ <?php if ( empty( $values['stripe_payment_request_button'] ) ) { ?>selected="selected"<?php } ?>><?php _e( 'No', 'paid-memberships-pro' ); ?></option>
419
+ <option value="1"
420
+ <?php if ( ! empty( $values['stripe_payment_request_button'] ) ) { ?>selected="selected"<?php } ?>><?php _e( 'Yes', 'paid-memberships-pro' ); ?></option>
421
+ </select>
422
+ <?php
423
+ $allowed_stripe_payment_button_html = array (
424
+ 'a' => array (
425
+ 'href' => array(),
426
+ 'target' => array(),
427
+ 'title' => array(),
428
+ ),
429
+ );
430
+ ?>
431
+ <p class="description"><?php echo sprintf( wp_kses( __( 'Allow users to pay using Apple Pay, Google Pay, or Microsoft Pay depending on their browser. When enabled, your domain will automatically be registered with Apple and a domain association file will be hosted on your site. <a target="_blank" href="%s" title="More Information about the domain association file for Apple Pay">More Information &raquo;</a>', 'paid-memberships-pro' ), $allowed_stripe_payment_button_html ), 'https://stripe.com/docs/stripe-js/elements/payment-request-button#verifying-your-domain-with-apple-pay' ); ?></p>
432
+ <?php
433
+ if ( ! empty( $values['stripe_payment_request_button'] ) ) {
434
+ // Are there any issues with how the payment request button is set up?
435
+ $payment_request_error = null;
436
+ $allowed_payment_request_error_html = array (
437
+ 'a' => array (
438
+ 'href' => array(),
439
+ 'target' => array(),
440
+ 'title' => array(),
441
+ ),
442
+ );
443
+ if ( empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off" ) {
444
+ $payment_request_error = sprintf( wp_kses( __( 'This webpage is being served over HTTP, but the Stripe Payment Request Button will only work on pages being served over HTTPS. To resolve this, you must <a target="_blank" href="%s" title="Configuring WordPress to Always Use HTTPS/SSL">set up WordPress to always use HTTPS</a>.', 'paid-memberships-pro' ), $allowed_payment_request_error_html ), 'https://www.paidmembershipspro.com/configuring-wordpress-always-use-httpsssl/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=blog&utm_content=configure-https' );
445
+ } elseif ( self::using_legacy_keys() && substr( $values['stripe_publishablekey'], 0, 8 ) !== "pk_live_" && substr( $values['stripe_publishablekey'], 0, 8 ) !== "pk_test_" ) {
446
+ $payment_request_error = sprintf( wp_kses( __( 'It looks like you are using an older Stripe publishable key. In order to use the Payment Request Button feature, you will need to update your API key, which will be prefixed with "pk_live_" or "pk_test_". <a target="_blank" href="%s" title="Stripe Dashboard API Key Settings">Log in to your Stripe Dashboard to roll your publishable key</a>.', 'paid-memberships-pro' ), $allowed_payment_request_error_html ), 'https://dashboard.stripe.com/account/apikeys' );
447
+ } elseif ( self::using_legacy_keys() && substr( $values['stripe_secretkey'], 0, 8 ) !== "sk_live_" && substr( $values['stripe_secretkey'], 0, 8 ) !== "sk_test_" ) {
448
+ $payment_request_error = sprintf( wp_kses( __( 'It looks like you are using an older Stripe secret key. In order to use the Payment Request Button feature, you will need to update your API key, which will be prefixed with "sk_live_" or "sk_test_". <a target="_blank" href="%s" title="Stripe Dashboard API Key Settings">Log in to your Stripe Dashboard to roll your secret key</a>.', 'paid-memberships-pro' ), $allowed_payment_request_error_html ), 'https://dashboard.stripe.com/account/apikeys' );
449
+ } elseif ( ! $stripe->pmpro_does_apple_pay_domain_exist() ) {
450
+ $payment_request_error = sprintf( wp_kses( __( 'Your domain could not be registered with Apple to enable Apple Pay. Please try <a target="_blank" href="%s" title="Apple Pay Settings Page in Stripe">registering your domain manually from the Apple Pay settings page in Stripe</a>.', 'paid-memberships-pro' ), $allowed_payment_request_error_html ), 'https://dashboard.stripe.com/settings/payments/apple_pay' );
451
+ }
452
+ if ( ! empty( $payment_request_error ) ) {
453
+ ?>
454
+ <div class="notice error inline">
455
+ <p id="pmpro_stripe_payment_request_button_notice"><?php echo( $payment_request_error ); ?></p>
456
+ </div>
457
+ <?php
458
+ }
459
+ }
460
+ ?>
461
+ </td>
462
+ </tr>
463
+ <?php if ( ! function_exists( 'pmproappe_pmpro_valid_gateways' ) ) {
464
  $allowed_appe_html = array (
465
  'a' => array (
466
  'href' => array(),
480
  /**
481
  * AJAX callback to create webhooks.
482
  */
483
+ public static function wp_ajax_pmpro_stripe_create_webhook( $silent = false ) {
484
  $secretkey = sanitize_text_field( $_REQUEST['secretkey'] );
485
 
486
  $stripe = new PMProGateway_stripe();
487
  Stripe\Stripe::setApiKey( $secretkey );
488
 
489
+ $update_webhook_response = $stripe::update_webhook_events();
490
+
491
+ if ( empty( $update_webhook_response ) || is_wp_error( $update_webhook_response ) ) {
492
+ $message = empty( $update_webhook_response ) ? __( 'Webhook creation failed. You might already have a webhook set up.', 'paid-memberships-pro' ) : $update_webhook_response->get_error_message();
493
  $r = array(
494
  'success' => false,
495
  'notice' => 'error',
496
+ 'message' => $message,
497
+ 'response' => $update_webhook_response
498
  );
499
  } else {
500
+ $r = array(
501
+ 'success' => true,
502
+ 'notice' => 'notice-success',
503
+ 'message' => __( 'Your webhook is enabled.', 'paid-memberships-pro' ),
504
+ 'response' => $update_webhook_response
505
+ );
 
 
 
 
 
 
 
 
 
506
  }
507
 
508
+ if ( $silent ) {
509
+ return $r;
510
+ } else {
511
+ echo json_encode( $r );
512
+ exit;
513
+ }
514
  }
515
 
516
  /**
517
  * AJAX callback to disable webhooks.
518
  */
519
+ public static function wp_ajax_pmpro_stripe_delete_webhook( $silent = false ) {
520
  $secretkey = sanitize_text_field( $_REQUEST['secretkey'] );
521
 
522
  $stripe = new PMProGateway_stripe();
524
 
525
  $webhook = self::does_webhook_exist();
526
 
527
+ $r = array(
528
+ 'success' => true,
529
+ 'notice' => 'error',
530
+ 'message' => __( 'A webhook in Stripe is required to process recurring payments, manage failed payments, and synchronize cancellations.', 'paid-memberships-pro' )
531
+ );
532
+ if ( ! empty( $webhook ) ) {
533
+ $delete_webhook_response = $stripe::delete_webhook( $webhook, $secretkey );
534
+
535
+ if ( is_wp_error( $delete_webhook_response ) || empty( $delete_webhook_response['deleted'] ) || $delete_webhook_response['deleted'] != true ) {
536
+ $message = is_wp_error( $delete_webhook_response ) ? $delete_webhook_response->get_error_message() : __( 'There was an error deleting the webhook.', 'paid-memberships-pro' );
537
  $r = array(
538
  'success' => false,
539
  'notice' => 'error',
540
+ 'message' => $message,
 
541
  );
542
+ }
543
+ $r['response'] = $delete_webhook_response;
544
+ }
545
+
546
+ if ( $silent ) {
547
+ return $r;
548
+ } else {
549
+ echo json_encode( $r );
550
+ exit;
551
+ }
552
+ }
553
+
554
+ /**
555
+ * AJAX callback to rebuild webhook.
556
+ */
557
+ public static function wp_ajax_pmpro_stripe_rebuild_webhook() {
558
+ // First try to delete the webhook.
559
+ $r = self::wp_ajax_pmpro_stripe_delete_webhook( true ) ;
560
+ if ( $r['success'] ) {
561
+ // Webhook was successfully deleted. Now make a new one.
562
+ $r = self::wp_ajax_pmpro_stripe_create_webhook( true );
563
+ if ( ! $r['success'] ) {
564
+ $r['message'] = __( 'Webhook creation failed. Please refresh and try again.', 'paid-memberships-pro' );
565
  }
566
  }
567
 
568
  echo json_encode( $r );
 
569
  exit;
570
  }
571
 
574
  *
575
  * @since 1.8
576
  */
577
+ public static function pmpro_checkout_after_preheader( $order ) {
578
+ global $gateway, $pmpro_level, $current_user, $pmpro_requirebilling, $pmpro_pages, $pmpro_currency;
 
579
 
580
  $default_gateway = pmpro_getOption( "gateway" );
581
 
584
  wp_enqueue_script( "stripe", "https://js.stripe.com/v3/", array(), null );
585
 
586
  if ( ! function_exists( 'pmpro_stripe_javascript' ) ) {
 
587
  $localize_vars = array(
588
+ 'publishableKey' => self::get_publishablekey(),
589
+ 'user_id' => self::get_connect_user_id(),
590
  'verifyAddress' => apply_filters( 'pmpro_stripe_verify_address', pmpro_getOption( 'stripe_billingaddress' ) ),
591
  'ajaxUrl' => admin_url( "admin-ajax.php" ),
592
  'msgAuthenticationValidated' => __( 'Verification steps confirmed. Your payment is processing.', 'paid-memberships-pro' ),
593
  'pmpro_require_billing' => $pmpro_requirebilling,
594
+ 'restUrl' => get_rest_url(),
595
+ 'siteName' => get_bloginfo( 'name' ),
596
+ 'updatePaymentRequestButton' => apply_filters( 'pmpro_stripe_update_payment_request_button', true ),
597
+ 'currency' => strtolower( $pmpro_currency ),
598
+ 'accountCountry' => self::get_account_country(),
599
  );
600
 
601
  if ( ! empty( $order ) ) {
602
+ if ( ! empty( $order->stripe_payment_intent ) ) {
603
+ $localize_vars['paymentIntent'] = $order->stripe_payment_intent;
604
  }
605
+ if ( ! empty( $order->stripe_setup_intent ) ) {
606
+ $localize_vars['setupIntent'] = $order->stripe_setup_intent;
 
607
  }
608
  }
609
 
621
  * Don't require the CVV.
622
  * Don't require address fields if they are set to hide.
623
  */
624
+ public static function pmpro_required_billing_fields( $fields ) {
625
  global $pmpro_stripe_lite, $current_user, $bemail, $bconfirmemail;
626
 
627
  //CVV is not required if set that way at Stripe. The Stripe JS will require it if it is required.
655
 
656
  return $fields;
657
  }
658
+
659
  /**
660
+ * Filtering orders at checkout.
661
  *
662
+ * @since 1.8
663
  */
664
+ public static function pmpro_checkout_order( $morder ) {
665
+
666
+ // Create a code for the order.
667
+ if ( empty( $morder->code ) ) {
668
+ $morder->code = $morder->getRandomCode();
 
 
 
 
 
 
 
 
 
669
  }
670
+
671
+ // Add the PaymentIntent ID to the order.
672
+ if ( ! empty ( $_REQUEST['payment_intent_id'] ) ) {
673
+ $morder->payment_intent_id = sanitize_text_field( $_REQUEST['payment_intent_id'] );
 
 
 
 
 
674
  }
675
+
676
+ // Add the SetupIntent ID to the order.
677
+ if ( ! empty ( $_REQUEST['setup_intent_id'] ) ) {
678
+ $morder->setup_intent_id = sanitize_text_field( $_REQUEST['setup_intent_id'] );
 
 
 
 
 
679
  }
680
+
681
+ // Add the PaymentMethod ID to the order.
682
+ if ( ! empty ( $_REQUEST['payment_method_id'] ) ) {
683
+ $morder->payment_method_id = sanitize_text_field( $_REQUEST['payment_method_id'] );
684
  }
685
+
686
+ //stripe lite code to get name from other sources if available
687
+ global $pmpro_stripe_lite, $current_user;
688
+ if ( ! empty( $pmpro_stripe_lite ) && empty( $morder->FirstName ) && empty( $morder->LastName ) ) {
689
+ if ( ! empty( $current_user->ID ) ) {
690
+ $morder->FirstName = get_user_meta( $current_user->ID, "first_name", true );
691
+ $morder->LastName = get_user_meta( $current_user->ID, "last_name", true );
692
+ } elseif ( ! empty( $_REQUEST['first_name'] ) && ! empty( $_REQUEST['last_name'] ) ) {
693
+ $morder->FirstName = sanitize_text_field( $_REQUEST['first_name'] );
694
+ $morder->LastName = sanitize_text_field( $_REQUEST['last_name'] );
695
+ }
696
+ }
697
+
698
+ return $morder;
699
  }
700
+
701
  /**
702
+ * Code to run after checkout
703
  *
704
+ * @since 1.8
705
  */
706
+ public static function pmpro_after_checkout( $user_id, $morder ) {
707
+ global $gateway;
708
+
709
+ if ( $gateway == "stripe" ) {
710
+ if ( self::$is_loaded && ! empty( $morder ) && ! empty( $morder->Gateway ) && ! empty( $morder->Gateway->customer ) && ! empty( $morder->Gateway->customer->id ) ) {
711
+ update_user_meta( $user_id, "pmpro_stripe_customerid", $morder->Gateway->customer->id );
712
+ }
 
 
 
 
 
 
 
 
 
 
 
713
  }
 
 
 
714
  }
715
 
716
  /**
717
+ * Check settings if billing address should be shown.
718
+ * @since 1.8
 
719
  */
720
+ public static function pmpro_include_billing_address_fields( $include ) {
721
+ //check settings RE showing billing address
722
+ if ( ! pmpro_getOption( "stripe_billingaddress" ) ) {
723
+ $include = false;
724
  }
725
 
726
+ return $include;
 
 
 
 
 
 
 
 
727
  }
728
 
729
  /**
730
+ * Use our own payment fields at checkout. (Remove the name attributes.)
731
+ * @since 1.8
 
732
  */
733
+ public static function pmpro_include_payment_information_fields( $include ) {
734
+ //global vars
735
+ global $pmpro_requirebilling, $pmpro_show_discount_code, $discount_code, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
736
 
737
  //get accepted credit cards
738
  $pmpro_accepted_credit_cards = pmpro_getOption( "accepted_credit_cards" );
751
  <?php if ( ! empty( $sslseal ) ) { ?>
752
  <div class="<?php echo pmpro_get_element_class( 'pmpro_checkout-fields-display-seal' ); ?>">
753
  <?php } ?>
754
+ <?php
755
+ if ( pmpro_getOption( 'stripe_payment_request_button' ) ) { ?>
756
+ <div class="<?php echo pmpro_get_element_class( 'pmpro_checkout-field pmpro_checkout-field-payment-request-button', 'pmpro_checkout-field-payment-request-button' ); ?>">
757
+ <div id="payment-request-button"><!-- Alternate payment method will be inserted here. --></div>
758
+ <h4 class="<?php echo pmpro_get_element_class( 'pmpro_checkout-field pmpro_payment-credit-card', 'pmpro_payment-credit-card' ); ?>"><?php esc_html_e( 'Pay with Credit Card', 'paid-memberships-pro' ); ?></h4>
759
+ </div>
760
+ <?php
761
+ }
762
+ ?>
763
  <div class="pmpro_checkout-fields<?php if ( ! empty( $sslseal ) ) { ?> pmpro_checkout-fields-leftcol<?php } ?>">
764
  <?php
765
  $pmpro_include_cardtype_field = apply_filters( 'pmpro_include_cardtype_field', false );
796
  <?php if ( $pmpro_show_discount_code ) { ?>
797
  <div class="<?php echo pmpro_get_element_class( 'pmpro_checkout-field pmpro_payment-discount-code', 'pmpro_payment-discount-code' ); ?>">
798
  <label for="discount_code"><?php _e( 'Discount Code', 'paid-memberships-pro' ); ?></label>
799
+ <input class="<?php echo pmpro_get_element_class( 'input pmpro_alter_price', 'discount_code' ); ?>"
800
  id="discount_code" name="discount_code" type="text" size="10"
801
  value="<?php echo esc_attr( $discount_code ) ?>"/>
802
  <input type="button" id="discount_code_button" name="discount_code_button"
821
  *
822
  * @since 1.8
823
  */
824
+ public static function user_profile_fields( $user ) {
825
  global $wpdb, $current_user, $pmpro_currency_symbol;
826
 
 
 
 
 
 
 
 
 
 
827
  //make sure the current user has privileges
828
  $membership_level_capability = apply_filters( "pmpro_edit_member_capability", "manage_options" );
829
  if ( ! current_user_can( $membership_level_capability ) ) {
836
  return false;
837
  }
838
 
839
+ // Get the user's Stripe Customer if they have one.
840
+ $stripe = new PMProGateway_Stripe();
841
+ $customer = $stripe->get_customer_for_user( $user->ID );
 
 
 
842
 
843
+ // Check whether we have a Stripe Customer.
844
+ if ( ! empty( $customer ) ) {
845
+ // Get the link to edit the customer.
846
+ echo '<hr>';
847
+ echo '<a target="_blank" href="' . esc_url( 'https://dashboard.stripe.com/' . ( pmpro_getOption( 'gateway_environment' ) == 'sandbox' ? 'test/' : '' ) . 'customers/' . $customer->id ) . '">' . esc_html__( 'Edit customer in Stripe', 'paid-memberships-pro' ) . '</a>';
848
+ if ( ! empty( $user->pmpro_stripe_updates ) && is_array( $user->pmpro_stripe_updates ) ) {
849
+ $stripe->user_profile_fields_subscription_updates( $user, $customer );
850
+ }
851
  }
852
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
853
 
854
  /**
855
+ * Temporary function to allow users to delete subscription updates.
856
+ * Will be removed once subscription updates are completely deprecated.
857
  *
858
+ * @since 2.7.0.
859
  */
860
  static function user_profile_fields_save( $user_id ) {
861
  global $wpdb;
 
862
  //check capabilities
863
  $membership_level_capability = apply_filters( "pmpro_edit_member_capability", "manage_options" );
864
  if ( ! current_user_can( $membership_level_capability ) ) {
865
  return false;
866
  }
867
 
868
+ //make sure subscription updates were shown.
869
+ if ( ! isset( $_POST['pmpro_subscription_updates_visible'] ) ) {
870
+ return;
871
+ }
872
+
873
+ // Check whether all updates were deleted.
874
  if ( ! isset( $_POST['updates_when'] ) || ! is_array( $_POST['updates_when'] ) ) {
875
+ delete_user_meta( $user_id, 'pmpro_stripe_updates' );
876
+ delete_user_meta( $user_id, 'pmpro_stripe_next_on_date_update' );
877
  return;
878
  }
879
 
881
  $updates = array();
882
  $next_on_date_update = "";
883
 
884
+ //build array of updates
885
+ for ( $i = 0; $i < count( $_POST['updates_when'] ); $i ++ ) {
886
  $update = array();
887
 
888
  //all updates have these values
909
 
910
  //if when is now, update the subscription
911
  if ( $update['when'] == "now" ) {
912
+ self::updateSubscription( $update, $user_id );
913
 
914
  continue;
915
  } elseif ( $update['when'] == 'date' ) {
934
  /**
935
  * Cron activation for subscription updates.
936
  *
937
+ * The subscription updates menu is no longer accessible as of v2.6.
938
+ * This function is staying to process subscription updates that were already queued.
939
+ *
940
  * @since 1.8
941
  */
942
+ public static function pmpro_activation() {
943
  pmpro_maybe_schedule_event( time(), 'daily', 'pmpro_cron_stripe_subscription_updates' );
944
  }
945
 
946
  /**
947
  * Cron deactivation for subscription updates.
948
  *
949
+ * The subscription updates menu is no longer accessible as of v2.6.
950
+ * This function is staying to process subscription updates that were already queued.
951
+ *
952
  * @since 1.8
953
  */
954
+ public static function pmpro_deactivation() {
955
  wp_clear_scheduled_hook( 'pmpro_cron_stripe_subscription_updates' );
956
  }
957
 
958
  /**
959
  * Cron job for subscription updates.
960
  *
961
+ * The subscription updates menu is no longer accessible as of v2.6.
962
+ * This function is staying to process subscription updates that were already queued.
963
+ *
964
  * @since 1.8
965
  */
966
+ public static function pmpro_cron_stripe_subscription_updates() {
967
  global $wpdb;
968
 
969
  //get all updates for today (or before today)
1000
  if ( $ud['when'] == 'date' &&
1001
  $ud['date_year'] . "-" . $ud['date_month'] . "-" . $ud['date_day'] <= date_i18n( "Y-m-d", current_time( 'timestamp' ) )
1002
  ) {
1003
+ self::updateSubscription( $ud, $user_id );
1004
 
1005
  //remove update from list
1006
  unset( $user_updates[ $key ] );
1030
  * because of an expired credit card/etc and a user checks out to renew their subscription
1031
  * instead of updating their billing information via the billing info page.
1032
  */
1033
+ public static function pmpro_checkout_before_processing() {
1034
  global $wpdb, $current_user;
1035
 
1036
  // we're only worried about cases where the user is logged in
1074
 
1075
  //so let's cancel the user's susbcription
1076
  if ( ! empty( $last_order ) && ! empty( $last_order->subscription_transaction_id ) ) {
1077
+ $subscription = $last_order->Gateway->get_subscription( $last_order->subscription_transaction_id );
1078
  if ( ! empty( $subscription ) ) {
1079
  $last_order->Gateway->cancelSubscriptionAtGateway( $subscription, true );
1080
 
1105
  }
1106
 
1107
  /**
1108
+ * Filter pmpro_next_payment to get date via API if possible
1109
+ *
1110
+ * @since 1.8.6
1111
  */
1112
+ public static function pmpro_next_payment( $timestamp, $user_id, $order_status ) {
1113
+ //find the last order for this user
1114
+ if ( ! empty( $user_id ) ) {
1115
+ //get last order
1116
+ $order = new MemberOrder();
1117
+ $order->getLastMemberOrder( $user_id, $order_status );
 
 
1118
 
1119
+ //check if this is a Stripe order with a subscription transaction id
1120
+ if ( ! empty( $order->id ) && ! empty( $order->subscription_transaction_id ) && $order->gateway == "stripe" ) {
1121
+ //get the subscription and return the current_period end or false
1122
+ $subscription = $order->Gateway->get_subscription( $order->subscription_transaction_id );
1123
+
1124
+ if ( ! empty( $subscription ) ) {
1125
+ $customer = $order->Gateway->get_customer_for_user( $user_id );
1126
+ if ( ! $customer->delinquent && ! empty ( $subscription->current_period_end ) ) {
1127
+ $offset = get_option( 'gmt_offset' );
1128
+ $timestamp = $subscription->current_period_end + ( $offset * 3600 );
1129
+ } elseif ( $customer->delinquent && ! empty( $subscription->current_period_start ) ) {
1130
+ $offset = get_option( 'gmt_offset' );
1131
+ $timestamp = $subscription->current_period_start + ( $offset * 3600 );
1132
+ } else {
1133
+ $timestamp = null; // shouldn't really get here
1134
+ }
1135
+ }
1136
  }
1137
  }
1138
 
1139
+ return $timestamp;
 
 
 
 
1140
  }
1141
 
1142
+ public static function pmpro_set_up_apple_pay( $payment_option_values, $gateway ) {
1143
+ // Check that we just saved Stripe settings.
1144
+ if ( $gateway != 'stripe' || empty( $_REQUEST['savesettings'] ) ) {
1145
+ return;
1146
+ }
1147
+
1148
+ // Check that payment request button is enabled.
1149
+ if ( empty( $payment_option_values['stripe_payment_request_button'] ) ) {
1150
+ // We don't want to unregister domain or remove file in case
1151
+ // other plugins are using it.
1152
+ return;
1153
+ }
1154
+
1155
+ // Make sure that Apple Pay is set up.
1156
+ // TODO: Apple Pay API functions don't seem to work with
1157
+ // test API keys. Need to figure this out.
1158
+ $stripe = new PMProGateway_stripe();
1159
+ if ( ! $stripe->pmpro_does_apple_pay_domain_exist() ) {
1160
+ // 1. Make sure domain association file available.
1161
+ flush_rewrite_rules();
1162
+ // 2. Register Domain with Apple.
1163
+ $stripe->pmpro_create_apple_pay_domain();
1164
+ }
1165
+ }
1166
+
1167
+ /**
1168
+ * This function is used to save the parameters returned after successfull connection of Stripe account.
1169
  *
1170
+ * @return void
1171
  */
1172
+ public static function stripe_connect_save_options() {
1173
+ // Is user have permission to edit give setting.
1174
+ if ( ! current_user_can( 'manage_options' ) ) {
1175
+ return;
1176
+ }
1177
 
1178
+ // Be sure only to connect when param present.
1179
+ if ( ! isset( $_REQUEST['pmpro_stripe_connected'] ) || ! isset( $_REQUEST['pmpro_stripe_connected_environment'] ) ) {
1180
+ return false;
1181
  }
1182
+
1183
+ // Change current gateway to Stripe
1184
+ pmpro_setOption( 'gateway', 'stripe' );
1185
+ pmpro_setOption( 'gateway_environment', $_REQUEST['pmpro_stripe_connected_environment'] );
1186
+
1187
+ $error = '';
1188
+ if (
1189
+ 'false' === $_REQUEST['pmpro_stripe_connected']
1190
+ && isset( $_REQUEST['error_message'] )
1191
+ ) {
1192
+ $error = $_REQUEST['error_message'];
1193
+ } elseif (
1194
+ 'false' === $_REQUEST['pmpro_stripe_connected']
1195
+ || ! isset( $_REQUEST['pmpro_stripe_publishable_key'] )
1196
+ || ! isset( $_REQUEST['pmpro_stripe_user_id'] )
1197
+ || ! isset( $_REQUEST['pmpro_stripe_access_token'] )
1198
+ ) {
1199
+ $error = __( 'Invalid response from the Stripe Connect server.', 'paid-memberships-pro' );
1200
+ } else {
1201
+ // Update keys.
1202
+ if ( $_REQUEST['pmpro_stripe_connected_environment'] === 'live' ) {
1203
+ // Update live keys.
1204
+ pmpro_setOption( 'live_stripe_connect_user_id', $_REQUEST['pmpro_stripe_user_id'] );
1205
+ pmpro_setOption( 'live_stripe_connect_secretkey', $_REQUEST['pmpro_stripe_access_token'] );
1206
+ pmpro_setOption( 'live_stripe_connect_publishablekey', $_REQUEST['pmpro_stripe_publishable_key'] );
1207
+ } else {
1208
+ // Update sandbox keys.
1209
+ pmpro_setOption( 'sandbox_stripe_connect_user_id', $_REQUEST['pmpro_stripe_user_id'] );
1210
+ pmpro_setOption( 'sandbox_stripe_connect_secretkey', $_REQUEST['pmpro_stripe_access_token'] );
1211
+ pmpro_setOption( 'sandbox_stripe_connect_publishablekey', $_REQUEST['pmpro_stripe_publishable_key'] );
1212
+ }
1213
+
1214
 
1215
+ // Delete option for user API key.
1216
+ delete_option( 'pmpro_stripe_secretkey' );
1217
+ delete_option( 'pmpro_stripe_publishablekey' );
1218
+
1219
+ wp_redirect( admin_url( 'admin.php?page=pmpro-paymentsettings' ) );
1220
+ exit;
1221
  }
1222
 
1223
+ if ( ! empty( $error ) ) {
1224
+ global $pmpro_stripe_error;
1225
+ $pmpro_stripe_error = sprintf(
1226
+ /* translators: %s Error Message */
1227
+ __( '<strong>Error:</strong> PMPro could not connect to the Stripe API. Reason: %s', 'paid-memberships-pro' ),
1228
+ esc_html( $error )
1229
+ );
1230
+ }
1231
+ }
1232
 
1233
+ public static function stripe_connect_show_errors() {
1234
+ global $pmpro_stripe_error;
1235
+ if ( ! empty( $pmpro_stripe_error ) ) {
1236
+ $class = 'notice notice-error pmpro-stripe-connect-message';
1237
+ printf( '<div class="%1$s"><p>%2$s</p></div>', esc_attr( $class ), $pmpro_stripe_error );
1238
+ }
1239
+ }
1240
 
1241
+ /**
1242
+ * Disconnects user from the Stripe Connected App.
1243
+ */
1244
+ public static function stripe_connect_deauthorize() {
1245
+ if ( ! current_user_can( 'manage_options' ) ) {
1246
+ return;
1247
+ }
1248
 
1249
+ // Be sure only to deauthorize when param present.
1250
+ if ( ! isset( $_REQUEST['pmpro_stripe_disconnected'] ) || ! isset( $_REQUEST['pmpro_stripe_disconnected_environment'] ) ) {
1251
  return false;
1252
  }
1253
 
1254
+ // Show message if NOT disconnected.
1255
+ if (
1256
+ 'false' === $_REQUEST['pmpro_stripe_disconnected']
1257
+ && isset( $_REQUEST['error_code'] )
1258
+ ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1259
 
1260
+ $class = 'notice notice-warning pmpro-stripe-disconnect-message';
1261
+ $message = sprintf(
1262
+ /* translators: %s Error Message */
1263
+ __( '<strong>Error:</strong> PMPro could not disconnect from the Stripe API. Reason: %s', 'paid-memberships-pro' ),
1264
+ esc_html( $_REQUEST['error_message'] )
1265
+ );
1266
 
1267
+ printf( '<div class="%1$s"><p>%2$s</p></div>', esc_attr( $class ), $message );
 
1268
 
1269
+ }
 
 
 
 
1270
 
1271
+ if ( $_REQUEST['pmpro_stripe_disconnected_environment'] === 'live' ) {
1272
+ // Delete live keys.
1273
+ delete_option( 'pmpro_live_stripe_connect_user_id' );
1274
+ delete_option( 'pmpro_live_stripe_connect_secretkey' );
1275
+ delete_option( 'pmpro_live_stripe_connect_publishablekey' );
1276
  } else {
1277
+ // Delete sandbox keys.
1278
+ delete_option( 'pmpro_sandbox_stripe_connect_user_id' );
1279
+ delete_option( 'pmpro_sandbox_stripe_connect_secretkey' );
1280
+ delete_option( 'pmpro_sandbox_stripe_connect_publishablekey' );
 
 
1281
  }
1282
  }
1283
 
1284
  /**
1285
+ * Determine whether the site is using legacy Stripe keys.
1286
  *
1287
+ * @return bool Whether the site is using legacy Stripe keys.
 
 
 
 
 
 
 
 
1288
  */
1289
+ public static function using_legacy_keys() {
1290
+ $r = ! empty( pmpro_getOption( 'stripe_secretkey' ) ) && ! empty( pmpro_getOption( 'stripe_publishablekey' ) );
1291
+ return $r;
1292
+ }
1293
 
1294
+ /**
1295
+ * Determine whether the site has Stripe Connect credentials set based on gateway environment.
1296
+ *
1297
+ * @param null|string $gateway_environment The gateway environment to use, default uses the current saved setting.
1298
+ *
1299
+ * @return bool Whether the site has Stripe Connect credentials set.
1300
+ */
1301
+ public static function has_connect_credentials( $gateway_environment = null ) {
1302
+ if ( empty( $gateway_environment ) ) {
1303
+ $gateway_engvironemnt = pmpro_getOption( 'pmpro_gateway_environment' );
1304
  }
1305
 
1306
+ if ( $gateway_environment === 'live' ) {
1307
+ // Return whether Stripe is connected for live gateway environment.
1308
+ return (
1309
+ pmpro_getOption( 'live_stripe_connect_user_id' ) &&
1310
+ pmpro_getOption( 'live_stripe_connect_secretkey' ) &&
1311
+ pmpro_getOption( 'live_stripe_connect_publishablekey' )
1312
+ );
1313
+ } else {
1314
+ // Return whether Stripe is connected for sandbox gateway environment.
1315
+ return (
1316
+ pmpro_getOption( 'sandbox_stripe_connect_user_id' ) &&
1317
+ pmpro_getOption( 'sandbox_stripe_connect_secretkey' ) &&
1318
+ pmpro_getOption( 'sandbox_stripe_connect_publishablekey' )
1319
+ );
1320
  }
1321
+ }
1322
 
1323
+ /**
1324
+ * Warn if required extensions aren't loaded.
1325
+ *
1326
+ * @return bool
1327
+ * @since 1.8.6.8.1
1328
+ * @since 1.8.13.6 - Add json dependency
1329
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
1330
+ */
1331
+ public static function dependencies() {
1332
+ global $msg, $msgt, $pmpro_stripe_error;
1333
 
1334
+ if ( version_compare( PHP_VERSION, '5.3.29', '<' ) ) {
 
 
 
1335
 
1336
+ $pmpro_stripe_error = true;
1337
+ $msg = - 1;
1338
+ $msgt = sprintf( __( "The Stripe Gateway requires PHP 5.3.29 or greater. We recommend upgrading to PHP %s or greater. Ask your host to upgrade.", "paid-memberships-pro" ), PMPRO_PHP_MIN_VERSION );
 
 
1339
 
1340
+ if ( ! is_admin() ) {
1341
+ pmpro_setMessage( $msgt, "pmpro_error" );
 
 
 
 
 
1342
  }
1343
 
1344
+ return false;
1345
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1346
 
1347
+ $modules = array( 'curl', 'mbstring', 'json' );
 
1348
 
1349
+ foreach ( $modules as $module ) {
1350
+ if ( ! extension_loaded( $module ) ) {
1351
+ $pmpro_stripe_error = true;
1352
+ $msg = - 1;
1353
+ $msgt = sprintf( __( "The %s gateway depends on the %s PHP extension. Please enable it, or ask your hosting provider to enable it.", 'paid-memberships-pro' ), 'Stripe', $module );
 
 
 
 
 
 
 
 
 
 
 
1354
 
1355
+ //throw error on checkout page
1356
+ if ( ! is_admin() ) {
1357
+ pmpro_setMessage( $msgt, 'pmpro_error' );
 
1358
  }
1359
 
1360
+ return false;
 
 
 
1361
  }
1362
  }
1363
 
1364
+ self::$is_loaded = true;
 
 
 
 
 
 
 
1365
 
1366
+ return true;
1367
+ }
1368
 
1369
+ /****************************************
1370
+ ************ PUBLIC METHODS ************
1371
+ ****************************************/
1372
+ /**
1373
+ * Process checkout and decide if a charge and or subscribe is needed
1374
+ * Updated in v2.1 to work with Stripe v3 payment intents.
1375
+ * @since 1.4
1376
+ */
1377
+ public function process( &$order ) {
1378
+ $payment_transaction_id = '';
1379
+ $subscription_transaction_id = '';
1380
+
1381
+ if ( ! empty( $order->setup_intent_id ) ){
1382
+ // The user tried to confirm a setup intent. This means that there was no initial
1383
+ // payment needed for this chekout (otherwise there would be a payment intent instead),
1384
+ // and that the subscription needed authorization before being created.
1385
+ $setup_intent = $this->process_setup_intent( $order->setup_intent_id );
1386
+ if ( is_string( $setup_intent ) ) {
1387
+ $order->error = __( 'Error processing setup intent.', 'paid-memberships-pro' ) . ' ' . $setup_intent;
1388
+ $order->shorterror = $order->error;
1389
+ return false;
1390
  }
1391
+ // Subscription should now be active. Let's get its ID to save to the MemberOrder.
1392
+ $subscription_transaction_id = $setup_intent->metadata->subscription_id;
 
 
 
 
1393
  } else {
1394
+ // User has either just submitted the checkout form or tried to confirm their
1395
+ // payment intent.
1396
+ $customer = null; // This will be used to create the subscription later.
1397
+ if ( ! empty( $order->payment_intent_id ) ) {
1398
+ // User has just tried to confirm their payment intent. We need to make sure that it was
1399
+ // confirmed successfully, and then try to create their subscription if needed.
1400
+ $payment_intent = $this->process_payment_intent( $order->payment_intent_id );
1401
+ if ( is_string( $payment_intent ) ) {
1402
+ $order->error = __( 'Error processing payment intent.', 'paid-memberships-pro' ) . ' ' . $payment_intent;
1403
+ $order->shorterror = $order->error;
1404
+ return false;
1405
+ }
1406
+ // Payment should now be processed.
1407
+ $payment_transaction_id = $payment_intent->charges->data[0]->id;
1408
 
1409
+ // Note the customer so that we can create a subscription if needed..
1410
+ $customer = $payment_intent->customer;
1411
+ } else {
1412
+ // We have not yet tried to process this checkout.
1413
+ // Make sure we have a customer with a payment method.
1414
+ $customer = $this->update_customer_at_checkout( $order );
1415
+ if ( empty( $customer ) ) {
1416
+ // There was an issue creating/updating the Stripe customer.
1417
+ // $order will have an error message, so we don't need to add one.
1418
+ return false;
1419
+ }
1420
 
1421
+ $payment_method = $this->get_payment_method( $order );
1422
+ if ( empty( $payment_method ) ) {
1423
+ // There was an issue getting the payment method.
1424
+ $order->error = __( 'Error retrieving payment method.', 'paid-memberships-pro' );
1425
+ $order->shorterror = $order->error;
1426
+ return false;
 
 
 
1427
  }
1428
 
1429
+ $customer = $this->set_default_payment_method_for_customer( $customer, $payment_method );
1430
+ if ( is_string( $customer ) ) {
1431
+ // There was an issue updating the default payment method.
1432
+ $order->error = __( 'Error updating default payment method.', 'paid-memberships-pro' ) . ' ' . $customer;
1433
+ $order->shorterror = $order->error;
1434
+ return false;
1435
+ }
 
 
 
 
 
1436
 
1437
+ // Save customer in $order for create_payment_intent().
1438
+ // This will likely be removed as we rework payment processing.
1439
+ $order->stripe_customer = $customer;
1440
+
1441
+ // Process the charges.
1442
+ $charges_processed = $this->process_charges( $order );
1443
+ if ( ! empty( $order->error ) ) {
1444
+ // There was an error processing charges.
1445
+ // $order has an error message, so we don't need to add one.
1446
+ return false;
1447
  }
1448
 
1449
+ // If we needed to charge an initial payment, it was successful.
1450
+ if ( ! empty( $order->stripe_payment_intent->charges->data[0]->id ) ) {
1451
+ $payment_transaction_id = $order->stripe_payment_intent->charges->data[0]->id;
1452
+ }
 
1453
  }
 
1454
 
1455
+ // Create a subscription if we need to.
1456
+ if ( pmpro_isLevelRecurring( $order->membership_level ) ) {
1457
+ $subscription = $this->create_subscription_for_customer_from_order( $customer->id, $order );
1458
+ if ( empty( $subscription ) ) {
1459
+ // There was an issue creating the subscription.
1460
+ $order->error = __( 'Error creating subscription for customer.', 'paid-memberships-pro' );
1461
+ $order->shorterror = $order->error;
1462
+ return false;
1463
+ }
1464
+ $order->stripe_subscription = $subscription;
1465
+ $setup_intent = $subscription->pending_setup_intent;
1466
+
1467
+ if ( ! empty( $setup_intent->status ) && 'requires_action' === $setup_intent->status ) {
1468
+ // We will need to reload the page to authenticate, so save the subscription ID in the setup intent
1469
+ // so that we don't lose it.
1470
+ $setup_intent = $this->add_subscription_id_to_setup_intent( $setup_intent, $subscription->id );
1471
+ if ( is_string( $setup_intent ) ) {
1472
+ $order->error = $setup_intent;
1473
+ $order->shorterror = $order->error;
1474
+ return false;
1475
+ }
1476
+ $order->stripe_setup_intent = $setup_intent;
1477
+ $order->errorcode = true;
1478
+ $order->error = __( 'Customer authentication is required to finish setting up your subscription. Please complete the verification steps issued by your payment provider.', 'paid-memberships-pro' );
1479
+
1480
+ return false;
1481
+ }
1482
 
1483
+ // Successfully created a subscription.
1484
+ $subscription_transaction_id = $subscription->id;
1485
+ }
1486
+ }
1487
+ // All charges have been processed and all subscriptions have been created.
1488
+ $order->payment_transaction_id = $payment_transaction_id;
1489
+ $order->subscription_transaction_id = $subscription_transaction_id;
1490
+ $order->status = 'success';
1491
+ $order->saveOrder();
1492
+ return true;
1493
+ }
1494
 
1495
+ /**
1496
+ * Retrieve a Stripe_Customer for a given user.
1497
+ *
1498
+ * @since 2.7.0
1499
+ *
1500
+ * @param int $user_id to get Stripe_Customer for.
1501
+ * @return Stripe_Customer|null
1502
+ */
1503
+ public function get_customer_for_user( $user_id ) {
1504
+ // Pull Stripe customer ID from user meta.
1505
+ $customer_id = get_user_meta( $user_id, 'pmpro_stripe_customerid', true );
1506
+
1507
+ if ( empty( $customer_id ) ) {
1508
+ // Try to figure out the cuseromer ID from their last order.
1509
+ if ( empty ( $order ) ) {
1510
+ $order = new MemberOrder();
1511
+ $order->getLastMemberOrder(
1512
+ $user_id,
1513
+ array(
1514
+ 'success',
1515
+ 'cancelled'
1516
+ ),
1517
+ null,
1518
+ 'stripe',
1519
+ $order->Gateway->gateway_environment
1520
+ );
1521
+ }
1522
 
1523
+ // If we don't have a customer ID yet, get the Customer ID from their subscription.
1524
+ if ( empty( $customer_id ) && ! empty( $order->subscription_transaction_id ) && strpos( $order->subscription_transaction_id, "sub_" ) !== false ) {
1525
+ try {
1526
+ $subscription = Stripe_Subscription::retrieve( $order->subscription_transaction_id );
1527
+ } catch ( \Throwable $e ) {
1528
+ // Assume no customer found.
1529
+ } catch ( \Exception $e ) {
1530
+ // Assume no customer found.
1531
+ }
1532
+ if ( ! empty( $subscription ) && ! empty( $subscription->customer ) ) {
1533
+ $customer_id = $subscription->customer;
1534
+ }
1535
  }
1536
 
1537
+ // If we don't have a customer ID yet, get the Customer ID from their charge.
1538
+ if ( empty( $customer_id ) && ! empty( $order->payment_transaction_id ) && strpos( $order->payment_transaction_id, "ch_" ) !== false ) {
1539
+ try {
1540
+ $charge = Stripe_Charge::retrieve( $order->payment_transaction_id );
1541
+ } catch ( \Throwable $e ) {
1542
+ // Assume no customer found.
1543
+ } catch ( \Exception $e ) {
1544
+ // Assume no customer found.
1545
+ }
1546
+ if ( ! empty( $charge ) && ! empty( $charge->customer ) ) {
1547
+ $customer_id = $charge->customer;
1548
+ }
1549
+ }
1550
 
1551
+ // If we don't have a customer ID yet, get the Customer ID from their invoice.
1552
+ if ( empty( $customer_id ) && ! empty( $order->payment_transaction_id ) && strpos( $order->payment_transaction_id, "in_" ) !== false ) {
1553
+ try {
1554
+ $invoice = Stripe_Invoice::retrieve( $order->payment_transaction_id );
1555
+ } catch ( \Throwable $e ) {
1556
+ // Assume no customer found.
1557
+ } catch ( \Exception $e ) {
1558
+ // Assume no customer found.
1559
+ }
1560
+ if ( ! empty( $invoice ) && ! empty( $invoice->customer ) ) {
1561
+ $customer_id = $invoice->customer;
1562
  }
1563
  }
1564
 
1565
+ if ( ! empty( $customer_id ) ) {
1566
+ update_user_meta( $user_id, "pmpro_stripe_customerid", $customer_id );
1567
+ }
1568
  }
1569
 
1570
+ return empty( $customer_id ) ? null : $this->get_customer( $customer_id );
1571
  }
1572
 
1573
  /**
1574
+ * Create/Update Stripe customer for a user.
1575
  *
1576
+ * @since 2.7.0
1577
+ *
1578
+ * @param int $user_id to create/update Stripe customer for.
1579
+ * @return Stripe_Customer|false
1580
  */
1581
+ public function update_customer_from_user( $user_id ) {
1582
+ $user = get_userdata( $user_id );
 
 
 
 
 
 
 
 
 
 
 
 
1583
 
1584
+ if ( empty( $user->ID ) ) {
1585
+ // User does not exist.
1586
  return false;
1587
  }
1588
 
1589
+ // Get the existing customer from Stripe.
1590
+ $customer = $this->get_customer_for_user( $user_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
1591
 
1592
+ // Get the name for the customer.
1593
+ $name = trim( $user->first_name . " " . $user->last_name );
1594
+ if ( empty( $name ) ) {
1595
+ // In case first and last names aren't set.
1596
+ $name = $user->user_login;
1597
  }
1598
 
1599
+ // Get data to update customer with.
1600
+ $customer_args = array(
1601
+ 'email' => $user->user_email,
1602
+ 'description' => $name . ' (' . $email . ')',
1603
+ );
1604
 
1605
+ // Maybe update billing address for customer.
1606
+ if (
1607
+ ! $this->customer_has_billing_address( $customer ) &&
1608
+ ! empty( $user->pmpro_baddress1 ) &&
1609
+ ! empty( $user->pmpro_bcity ) &&
1610
+ ! empty( $user->pmpro_bstate ) &&
1611
+ ! empty( $user->pmpro_bzipcode ) &&
1612
+ ! empty( $user->pmpro_bcountry )
1613
+ ) {
1614
+ // We have an address in user meta and there is
1615
+ // no address in Stripe. May as well send it.
1616
+ $customer_args['address'] = array(
1617
+ 'city' => $user->pmpro_bcity,
1618
+ 'country' => $user->pmpro_bcountry,
1619
+ 'line1' => $user->pmpro_baddress1,
1620
+ 'line2' => $user->pmpro_baddress2,
1621
+ 'postal_code' => $user->pmpro_bzipcode,
1622
+ 'state' => $user->pmpro_bstate,
1623
+ );
1624
  }
1625
 
1626
+ /**
1627
+ * Change the information that is sent when updating/creating
1628
+ * a Stripe_Customer from a user.
1629
+ *
1630
+ * @since 2.7.0
1631
+ *
1632
+ * @param array $customer_args to be sent.
1633
+ * @param WP_User $user being used to create/update customer.
1634
+ */
1635
+ $customer_args = apply_filters( 'pmpro_stripe_update_customer_from_user', $customer_args, $user );
1636
 
1637
+ // Update the customer.
1638
+ if ( empty( $customer ) ) {
1639
+ // We need to build a new customer.
1640
+ $customer = $this->create_customer( $customer_args );
1641
+ } else {
1642
+ // Update the existing customer.
1643
+ $customer = $this->update_customer( $customer->ID, $customer_args );
1644
  }
1645
+ return is_string( $customer ) ? false : $customer;
 
 
1646
  }
1647
 
1648
  /**
1650
  *
1651
  * @since 2.3
1652
  */
1653
+ public function getSubscriptionStatus( &$order ) {
1654
+ $subscription = $this->get_subscription( $order->subscription_transaction_id );
1655
 
1656
  if ( ! empty( $subscription ) ) {
1657
  return $subscription->status;
1661
  }
1662
 
1663
  /**
1664
+ * Helper method to update the customer info via update_customer_at_checkout
1665
  *
1666
  * @since 1.4
1667
  */
1668
+ public function update( &$order ) {
1669
+ $customer = $this->update_customer_at_checkout( $order );
1670
+ if ( empty( $customer ) ) {
1671
+ // There was an issue creating/updating the Stripe customer.
1672
+ // $order will have an error message, so we don't need to add one.
1673
+ return false;
 
 
1674
  }
1675
 
1676
+ $payment_method = $this->get_payment_method( $order );
1677
+ if ( empty( $payment_method ) ) {
1678
+ // There was an issue getting the payment method.
1679
+ $order->error = __( "Error retrieving payment method.", 'paid-memberships-pro' );
1680
+ $order->shorterror = $order->error;
1681
+ return false;
1682
  }
1683
 
1684
+ $customer = $this->set_default_payment_method_for_customer( $customer, $payment_method );
1685
+ if ( is_string( $customer ) ) {
1686
+ // There was an issue updating the default payment method.
1687
+ $order->error = __( "Error updating default payment method.", 'paid-memberships-pro' ) . " " . $customer;
1688
+ $order->shorterror = $order->error;
1689
+ return false;
 
 
 
1690
  }
1691
 
1692
+ if ( ! $this->update_payment_method_for_subscriptions( $order ) ) {
1693
+ $order->error = __( "Error updating payment method for subscription.", 'paid-memberships-pro' );
1694
+ $order->shorterror = $order->error;
1695
+ return false;
 
1696
  }
1697
 
1698
+ return true;
1699
+ }
 
1700
 
1701
+ /**
1702
+ * Cancel a subscription at Stripe
1703
+ *
1704
+ * @since 1.4
1705
+ */
1706
+ public function cancel( &$order, $update_status = true ) {
1707
+ global $pmpro_stripe_event;
1708
 
1709
+ //no matter what happens below, we're going to cancel the order in our system
1710
+ if ( $update_status ) {
1711
+ $order->updateStatus( "cancelled" );
1712
+ }
1713
 
1714
+ //require a subscription id
1715
+ if ( empty( $order->subscription_transaction_id ) ) {
1716
+ return false;
 
 
 
 
 
 
 
 
1717
  }
1718
 
1719
+ //find the customer
1720
+ $result = $this->update_customer_at_checkout( $order );
1721
 
1722
+ if ( ! empty( $result ) ) {
1723
+ //find subscription with this order code
1724
+ $subscription = $this->get_subscription( $order->subscription_transaction_id );
1725
 
1726
+ if ( ! empty( $subscription )
1727
+ && ( empty( $pmpro_stripe_event ) || empty( $pmpro_stripe_event->type ) || $pmpro_stripe_event->type != 'customer.subscription.deleted' ) ) {
1728
+ if ( $this->cancelSubscriptionAtGateway( $subscription ) ) {
1729
+ //we're okay, going to return true later
1730
+ } else {
1731
+ $order->error = __( "Could not cancel old subscription.", 'paid-memberships-pro' );
1732
+ $order->shorterror = $order->error;
1733
 
1734
+ return false;
1735
+ }
 
 
 
 
 
 
 
 
 
1736
  }
 
 
 
1737
 
1738
+ /*
1739
+ Clear updates for this user. (But not if checking out, we would have already done that.)
1740
  */
1741
+ if ( empty( $_REQUEST['submit-checkout'] ) ) {
1742
+ update_user_meta( $order->user_id, "pmpro_stripe_updates", array() );
1743
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1744
 
1745
+ return true;
1746
+ } else {
1747
+ $order->error = __( "Could not find the customer.", 'paid-memberships-pro' );
1748
  $order->shorterror = $order->error;
1749
 
1750
+ return false; //no customer found
1751
  }
1752
+ }
1753
 
 
 
 
 
 
1754
 
1755
+ /****************************************
1756
+ *********** PRIVATE METHODS ************
1757
+ ****************************************/
1758
+ /**
1759
+ * Shows settings for connecting to Stripe.
1760
+ *
1761
+ * @since 2.7.0.
1762
+ *
1763
+ * @param bool $livemode True if live credentials, false if sandbox.
1764
+ * @param array $values Current settings.
1765
+ * @param string $gateway currently being shown.
1766
+ */
1767
+ private function show_connect_payment_option_fields( $livemode = true, $values, $gateway ) {
1768
+ $gateway_environment = $this->gateway_environment;
1769
+
1770
+ $stripe_legacy_key = $values['stripe_publishablekey'];
1771
+ $stripe_legacy_secret = $values['stripe_secretkey'];
1772
+ $stripe_is_legacy_setup = ( self::using_legacy_keys() && ! empty( $stripe_legacy_key ) && ! empty( $stripe_legacy_secret ) );
1773
 
1774
+ $environment = $livemode ? 'live' : 'sandbox';
1775
+ $environment2 = $livemode ? 'live' : 'test'; // For when 'test' is used instead of 'sandbox'.
1776
+
1777
+ // Determine if the gateway is connected in live mode and set var.
1778
+ if ( self::has_connect_credentials( $environment ) || $stripe_is_legacy_setup ) {
1779
+ $connection_selector = 'pmpro_gateway-mode-connected';
1780
+ } else {
1781
+ $connection_selector = 'pmpro_gateway-mode-not-connected';
1782
  }
1783
 
1784
+ ?>
1785
+ <tr class="pmpro_settings_divider gateway gateway_stripe_<?php echo $environment; ?>"
1786
+ <?php if ( $gateway != "stripe" || $gateway_environment != $environment ) { ?>style="display: none;"<?php } ?>>
1787
+ <td colspan="2">
1788
+ <hr />
1789
+ <h2>
1790
+ <?php esc_html_e( 'Stripe Connect Settings', 'paid-memberships-pro' ); ?>
1791
+ <span class="pmpro_gateway-mode pmpro_gateway-mode-<?php echo $environment2; ?> <?php esc_attr_e( $connection_selector ); ?>">
1792
+ <?php
1793
+ echo ( $livemode ? esc_html__( 'Live Mode:', 'paid-memberships-pro' ) : esc_html__( 'Test Mode:', 'paid-memberships-pro' ) ) . ' ';
1794
+ if ( self::has_connect_credentials( $environment ) ) {
1795
+ esc_html_e( 'Connected', 'paid-memberships-pro' );
1796
+ } elseif( $stripe_is_legacy_setup ) {
1797
+ esc_html_e( 'Connected with Legacy Keys', 'paid-memberships-pro' );
1798
+ } else {
1799
+ esc_html_e( 'Not Connected', 'paid-memberships-pro' );
1800
+ }
1801
+ ?>
1802
+ </span>
1803
+ </h2>
1804
+ <?php if ( self::using_legacy_keys() ) { ?>
1805
+ <div class="notice notice-large notice-warning inline">
1806
+ <p class="pmpro_stripe_webhook_notice">
1807
+ <strong><?php esc_html_e( 'Your site is using legacy API keys to authenticate with Stripe.', 'paid-memberships-pro' ); ?></strong><br />
1808
+ <?php esc_html_e( 'You can continue to use the legacy API keys or choose to upgrade to our new Stripe Connect solution below.', 'paid-memberships-pro' ); ?><br />
1809
+ <?php
1810
+ if ( $livemode ) {
1811
+ esc_html_e( 'Use the "Connect with Stripe" button below to securely authenticate with your Stripe account using Stripe Connect. Log in with the current Stripe account used for this site so that existing subscriptions are not affected by the update.', 'paid-memberships-pro' );
1812
+ } else {
1813
+ esc_html_e( 'Use the "Connect with Stripe" button below to securely authenticate with your Stripe account using Stripe Connect in test mode.', 'paid-memberships-pro' );
1814
+ }
1815
+ ?>
1816
+ <a href="https://www.paidmembershipspro.com/gateway/stripe/switch-legacy-to-connect/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=documentation&utm_content=switch-to-connect" target="_blank"><?php esc_html_e( 'Read the documentation on switching to Stripe Connect &raquo;', 'paid-memberships-pro' ); ?></a>
1817
+ </p>
1818
+ </div>
1819
+ <?php } ?>
1820
+ </td>
1821
+ </tr>
1822
+ <tr class="gateway gateway_stripe_<?php echo $environment; ?>" <?php if ( $gateway != "stripe" || $gateway_environment != $environment ) { ?>style="display: none;"<?php } ?>>
1823
+ <th scope="row" valign="top">
1824
+ <label><?php esc_html_e( 'Stripe Connection:', 'paid-memberships-pro' ); ?></label>
1825
+ </th>
1826
+ <td>
1827
+ <?php
1828
+ $connect_url_base = apply_filters( 'pmpro_stripe_connect_url', 'https://connect.paidmembershipspro.com' );
1829
+ if ( self::has_connect_credentials( $environment ) ) {
1830
+ $connect_url = add_query_arg(
1831
+ array(
1832
+ 'action' => 'disconnect',
1833
+ 'gateway_environment' => $environment2,
1834
+ 'stripe_user_id' => $values[ $environment . '_stripe_connect_user_id'],
1835
+ 'return_url' => rawurlencode( admin_url( 'admin.php?page=pmpro-paymentsettings' ) ),
1836
+ ),
1837
+ $connect_url_base
1838
+ );
1839
+ ?>
1840
+ <a href="<?php echo esc_url_raw( $connect_url ); ?>" class="pmpro-stripe-connect"><span><?php esc_html_e( 'Disconnect From Stripe', 'paid-memberships-pro' ); ?></span></a>
1841
+ <p class="description">
1842
+ <?php
1843
+ if ( $livemode ) {
1844
+ esc_html_e( 'This will disconnect your site from Stripe. Users will not be able to complete membership checkout or update their billing information. Existing subscriptions will not be affected at the gateway, but new recurring orders will not be created in this site.', 'paid-memberships-pro' );
1845
+ } else {
1846
+ esc_html_e( 'This will disconnect your site from Stripe in test mode only.', 'paid-memberships-pro' );
1847
+ }
1848
+ ?>
1849
+ </p>
1850
+ <?php
1851
+ } else {
1852
+ $connect_url = add_query_arg(
1853
+ array(
1854
+ 'action' => 'authorize',
1855
+ 'gateway_environment' => $environment2,
1856
+ 'return_url' => rawurlencode( admin_url( 'admin.php?page=pmpro-paymentsettings' ) ),
1857
+ ),
1858
+ $connect_url_base
1859
+ );
1860
+ ?>
1861
+ <a href="<?php echo esc_url_raw( $connect_url ); ?>" class="pmpro-stripe-connect"><span><?php esc_html_e( 'Connect with Stripe', 'paid-memberships-pro' ); ?></span></a>
1862
+ <?php
1863
+ }
1864
+ ?>
1865
+ <p class="description">
1866
+ <?php
1867
+ if ( pmpro_license_isValid( null, 'plus' ) ) {
1868
+ esc_html_e( 'Note: You have a valid license and are not charged additional platform fees for payment processing.', 'paid-memberships-pro');
1869
+ } else {
1870
+ esc_html_e( 'Note: You are using the free Stripe payment gateway integration. This includes an additional 1% fee for payment processing. This fee is removed by activating a Plus license.', 'paid-memberships-pro');
1871
+ }
1872
+ echo ' <a href="https://www.paidmembershipspro.com/gateway/stripe/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=gateways&utm_content=stripe-fees#tab-fees" target="_blank">' . esc_html( 'Learn More &raquo;', 'paid-memberships-pro' ) . '</a>';
1873
+ ?>
1874
+ </p>
1875
+ <input type='hidden' name='<?php echo $environment; ?>_stripe_connect_user_id' id='<?php echo $environment; ?>_stripe_connect_user_id' value='<?php echo esc_attr( $values[ $environment . '_stripe_connect_user_id'] ) ?>'/>
1876
+ <input type='hidden' name='<?php echo $environment; ?>_stripe_connect_secretkey' id='<?php echo $environment; ?>_stripe_connect_secretkey' value='<?php echo esc_attr( $values[ $environment . '_stripe_connect_secretkey'] ) ?>'/>
1877
+ <input type='hidden' name='<?php echo $environment; ?>_stripe_connect_publishablekey' id='<?php echo $environment; ?>_stripe_connect_publishablekey' value='<?php echo esc_attr( $values[ $environment . '_stripe_connect_publishablekey'] ) ?>'/>
1878
+ </td>
1879
+ </tr>
1880
+ <tr class="gateway gateway_stripe_<?php echo $environment; ?>" <?php if ( $gateway != "stripe" || $gateway_environment != $environment ) { ?>style="display: none;"<?php } ?>>
1881
+ <th scope="row" valign="top">
1882
+ <label><?php esc_html_e( 'Webhook', 'paid-memberships-pro' ); ?>:</label>
1883
+ </th>
1884
+ <td>
1885
+ <?php self::get_last_webhook_date( $environment ); ?>
1886
+ <p class="description"><?php esc_html_e( 'Webhook URL', 'paid-memberships-pro' ); ?>:
1887
+ <code><?php echo esc_html( self::get_site_webhook_url() ); ?></code></p>
1888
+ </td>
1889
+ </tr>
1890
+ <?php
1891
+ }
1892
+
1893
+ /**
1894
+ * Retrieve a Stripe_Customer.
1895
+ *
1896
+ * @since 2.7.0
1897
+ *
1898
+ * @param string $customer_id to retrieve.
1899
+ * @return Stripe_Customer|null
1900
+ */
1901
+ private function get_customer( $customer_id ) {
1902
+ try {
1903
+ $customer = Stripe_Customer::retrieve( $customer_id );
1904
+ return $customer;
1905
  } catch ( \Throwable $e ) {
1906
+ // Assume no customer found.
1907
+ } catch ( \Exception $e ) {
1908
+ // Assume no customer found.
1909
+ }
1910
+ }
1911
+
1912
+ /**
1913
+ * Check whether a given Stripe customer has a billing address set.
1914
+ *
1915
+ * @since 2.7.0
1916
+ *
1917
+ * @param Stripe_Customer $customer to check.
1918
+ * @return bool
1919
+ */
1920
+ private function customer_has_billing_address( $customer ) {
1921
+ return (
1922
+ ! empty( $customer ) &&
1923
+ ! empty( $customer->address->line1 ) &&
1924
+ ! empty( $customer->address->city ) &&
1925
+ ! empty( $customer->address->state ) &&
1926
+ ! empty( $customer->address->postal_code ) &&
1927
+ ! empty( $customer->address->country )
1928
+ );
1929
+ }
1930
+
1931
+ /**
1932
+ * Update a customer in Stripe.
1933
+ *
1934
+ * @since 2.7.0
1935
+ *
1936
+ * @param string $customer_id to update.
1937
+ * @param array $args to update with.
1938
+ * @return Stripe_Customer|string error message.
1939
+ */
1940
+ private function update_customer( $customer_id, $args ) {
1941
+ try {
1942
+ $customer = Stripe_Customer::update( $customer_id, $args );
1943
+ } catch ( \Stripe\Error $e ) {
1944
+ return $e->getMessage();
1945
+ } catch ( \Throwable $e ) {
1946
+ return $e->getMessage();
1947
+ } catch ( \Exception $e ) {
1948
+ return $e->getMessage();
1949
+ }
1950
+ return $customer;
1951
+ }
1952
+
1953
+ /**
1954
+ * Create a new customer in Stripe.
1955
+ *
1956
+ * @since 2.7.0
1957
+ *
1958
+ * @param array $args to update with.
1959
+ * @return Stripe_Customer|string error message.
1960
+ */
1961
+ private function create_customer( $args ) {
1962
+ try {
1963
+ $customer = Stripe_Customer::create( $args );
1964
+ } catch ( \Stripe\Error $e ) {
1965
+ return $e->getMessage();
1966
+ } catch ( \Throwable $e ) {
1967
+ return $e->getMessage();
1968
+ } catch ( \Exception $e ) {
1969
+ return $e->getMessage();
1970
+ }
1971
+ return $customer;
1972
+ }
1973
+
1974
+ /**
1975
+ * Create/Update Stripe customer from MemberOrder.
1976
+ *
1977
+ * Falls back on information in User object if insufficient
1978
+ * information in MemberOrder.
1979
+ *
1980
+ * Should only be called when checkout is being proceesed. Otherwise,
1981
+ * use update_customer_from_user() method.
1982
+ *
1983
+ * @since 2.7.0
1984
+ *
1985
+ * @param MemberOrder $order to create/update Stripe customer for.
1986
+ * @return Stripe_Customer|false
1987
+ */
1988
+ private function update_customer_at_checkout( $order ) {
1989
+ global $current_user;
1990
+
1991
+ // Get user's ID.
1992
+ if ( ! empty( $order->user_id ) ) {
1993
+ $user_id = $order->user_id;
1994
+ }
1995
+ if ( empty( $user_id ) && ! empty( $current_user->ID ) ) {
1996
+ $user_id = $current_user->ID;
1997
+ }
1998
+ $user = empty( $user_id ) ? null : get_userdata( $user_id );
1999
+ $customer = empty( $user_id ) ? null : $this->get_customer_for_user( $user_id );
2000
+
2001
+ // Get customer name.
2002
+ if ( ! empty( $order->FirstName ) && ! empty( $order->LastName ) ) {
2003
+ $name = trim( $order->FirstName . " " . $order->LastName );
2004
+ } elseif ( ! empty( $order->FirstName ) ) {
2005
+ $name = $order->FirstName;
2006
+ } elseif ( ! empty( $order->LastName ) ) {
2007
+ $name = $order->LastName;
2008
+ } elseif ( ! empty( $user->ID ) ) {
2009
+ $name = trim( $user->first_name . " " . $user->last_name );
2010
+ if ( empty( $name ) ) {
2011
+ // In case first and last names aren't set.
2012
+ $name = $user->user_login;
2013
+ }
2014
+ } else {
2015
+ $name = 'No Name';
2016
+ }
2017
+
2018
+ // Get user's email.
2019
+ if ( ! empty( $order->Email ) ) {
2020
+ $email = $order->Email;
2021
+ } elseif ( ! empty( $user->user_email ) ) {
2022
+ $email = $user->user_email;
2023
+ } else {
2024
+ $email = "No Email";
2025
+ }
2026
+
2027
+ // Build data to update customer with.
2028
+ $customer_args = array(
2029
+ 'email' => $email,
2030
+ 'description' => $name . ' (' . $email . ')',
2031
+ );
2032
+
2033
+ // Maybe update billing address for customer.
2034
+ if (
2035
+ ! empty( $order->billing->street ) &&
2036
+ ! empty( $order->billing->city ) &&
2037
+ ! empty( $order->billing->state ) &&
2038
+ ! empty( $order->billing->postal_code ) &&
2039
+ ! empty( $order->billing->country )
2040
+ ) {
2041
+ // We collected a billing address at checkout.
2042
+ // Send it to Stripe.
2043
+ $customer_args['address'] = array(
2044
+ 'city' => $order->billing->city,
2045
+ 'country' => $order->billing->country,
2046
+ 'line1' => $order->billing->street,
2047
+ 'line2' => '',
2048
+ 'postal_code' => $order->billing->zip,
2049
+ 'state' => $order->billing->state,
2050
+ );
2051
+ } elseif (
2052
+ ! $this->customer_has_billing_address( $customer ) &&
2053
+ ! empty( $user->pmpro_baddress1 ) &&
2054
+ ! empty( $user->pmpro_bcity ) &&
2055
+ ! empty( $user->pmpro_bstate ) &&
2056
+ ! empty( $user->pmpro_bzipcode ) &&
2057
+ ! empty( $user->pmpro_bcountry )
2058
+ ) {
2059
+ // We have an address in user meta and there is
2060
+ // no address in Stripe. May as well send it.
2061
+ $customer_args['address'] = array(
2062
+ 'city' => $user->pmpro_bcity,
2063
+ 'country' => $user->pmpro_bcountry,
2064
+ 'line1' => $user->pmpro_baddress1,
2065
+ 'line2' => $user->pmpro_baddress2,
2066
+ 'postal_code' => $user->pmpro_bzipcode,
2067
+ 'state' => $user->pmpro_bstate,
2068
+ );
2069
+ }
2070
+
2071
+ /**
2072
+ * Change the information that is sent when updating/creating
2073
+ * a Stripe_Customer from a MemberOrder.
2074
+ *
2075
+ * @since 2.7.0
2076
+ *
2077
+ * @param array $customer_args to be sent.
2078
+ * @param MemberOrder $order being used to create/update customer.
2079
+ */
2080
+ $customer_args = apply_filters( 'pmpro_stripe_update_customer_at_checkout', $customer_args, $order );
2081
+
2082
+ // Check if we have an existing user.
2083
+ if ( ! empty( $customer ) ) {
2084
+ // User is already a customer in Stripe. Update.
2085
+ $customer = $this->update_customer( $customer->id, $customer_args );
2086
+ if ( is_string( $customer ) ) {
2087
+ // We were not able to create a new user in Stripe.
2088
+ $order->error = __( "Error updating customer record with Stripe.", 'paid-memberships-pro' ) . " " . $customer;
2089
+ $order->shorterror = $order->error;
2090
+ return false;
2091
+ }
2092
+ return $customer;
2093
+ }
2094
+
2095
+ // No customer yet. Need to create one.
2096
+ $customer = $this->create_customer( $customer_args );
2097
+ if ( is_string( $customer ) ) {
2098
+ // We were not able to create a new user in Stripe.
2099
+ $order->error = __( "Error creating customer record with Stripe.", 'paid-memberships-pro' ) . " " . $customer;
2100
+ $order->shorterror = $order->error;
2101
+ return false;
2102
+ }
2103
+
2104
+ // If we don't have a user yet, we need to update their user meta after registration.
2105
+ if ( empty( $user_id ) ) {
2106
+ global $pmpro_stripe_customer_id;
2107
+ $pmpro_stripe_customer_id = $customer->id;
2108
+ if ( ! function_exists( 'pmpro_user_register_stripe_customerid' ) ) {
2109
+ function pmpro_user_register_stripe_customerid( $user_id ) {
2110
+ global $pmpro_stripe_customer_id;
2111
+ update_user_meta( $user_id, "pmpro_stripe_customerid", $pmpro_stripe_customer_id );
2112
+ }
2113
+ add_action( "user_register", "pmpro_user_register_stripe_customerid" );
2114
+ }
2115
+ }
2116
+
2117
+ /**
2118
+ * Fires after a Stripe_Customer is created at checkout.
2119
+ *
2120
+ * @since Unknown
2121
+ * @deprecated 2.7.0. Use pmpro_stripe_update_customer_from_user or pmpro_stripe_update_customer_at_checkout.
2122
+ *
2123
+ * @param array $customer_args to be sent.
2124
+ * @param MemberOrder $order being used to create/update customer.
2125
+ */
2126
+ do_action( 'pmpro_stripe_create_customer', $customer );
2127
+ return $customer;
2128
+ }
2129
+
2130
+ /**
2131
+ * Sets the default Payment Method for a Customer in Stripe.
2132
+ *
2133
+ * @param Stripe_Customer $customer to update default payment method for.
2134
+ * @param Stripe_PaymentMethod $payment_method to set as default.
2135
+ * @return Stripe_Customer|string error message.
2136
+ */
2137
+ private function set_default_payment_method_for_customer( $customer, $payment_method ) {
2138
+ if ( ! empty( $customer->invoice_settings->default_payment_method ) && $customer->invoice_settings->default_payment_method === $payment_method->id ) {
2139
+ // Payment method already correct, no need to update.
2140
+ return $customer;
2141
+ }
2142
+
2143
+ try {
2144
+ $payment_method->attach( [ 'customer' => $customer->id ] );
2145
+ $customer->invoice_settings->default_payment_method = $payment_method->id;
2146
+ $customer->save();
2147
+ } catch ( Stripe\Error\Base $e ) {
2148
+ $order->error = $e->getMessage();
2149
+ return $e->getMessage();
2150
+ } catch ( \Throwable $e ) {
2151
+ $order->error = $e->getMessage();
2152
+ return $e->getMessage();
2153
+ } catch ( \Exception $e ) {
2154
+ $order->error = $e->getMessage();
2155
+ return $e->getMessage();
2156
+ }
2157
+
2158
+ return $customer;
2159
+ }
2160
+
2161
+ /**
2162
+ * Convert a price to a positive integer in cents (or 0 for a free price)
2163
+ * representing how much to charge. This is how Stripe wants us to send price amounts.
2164
+ *
2165
+ * @param float $price to be converted into cents.
2166
+ * @return integer
2167
+ */
2168
+ private function convert_price_to_unit_amount( $price ) {
2169
+ global $pmpro_currencies, $pmpro_currency;
2170
+ $currency_unit_multiplier = 100; // ie 100 cents per USD.
2171
+
2172
+ // Account for zero-decimal currencies like the Japanese Yen.
2173
+ if (
2174
+ is_array( $pmpro_currencies[ $pmpro_currency ] ) &&
2175
+ isset( $pmpro_currencies[ $pmpro_currency ]['decimals'] ) &&
2176
+ $pmpro_currencies[ $pmpro_currency ]['decimals'] == 0
2177
+ ) {
2178
+ $currency_unit_multiplier = 1;
2179
+ }
2180
+
2181
+ return intval( $price * $currency_unit_multiplier );
2182
+ }
2183
+
2184
+ /**
2185
+ * Retrieve a Stripe_Subscription.
2186
+ *
2187
+ * @since 2.7.0
2188
+ *
2189
+ * @param string $subscription_id to retrieve.
2190
+ * @return Stripe_Subscription|null
2191
+ */
2192
+ private function get_subscription( $subscription_id ) {
2193
+ try {
2194
+ $customer = Stripe_Subscription::retrieve( $subscription_id );
2195
+ return $customer;
2196
+ } catch ( \Throwable $e ) {
2197
+ // Assume no subscription found.
2198
+ } catch ( \Exception $e ) {
2199
+ // Assume no subscription found.
2200
+ }
2201
+ }
2202
+
2203
+ /**
2204
+ * Get the Stripe product ID for a given membership level.
2205
+ *
2206
+ * @since 2.7.0
2207
+ *
2208
+ * @param PMPro_Membership_Leve|int $level to get product ID for.
2209
+ * @return string|null
2210
+ */
2211
+ private function get_product_id_for_level( $level ) {
2212
+ if ( ! is_a( $level, 'PMPro_Membership_Level' ) ) {
2213
+ if ( is_numeric( $level ) ) {
2214
+ $level = new PMPro_Membership_Level( $level );
2215
+ }
2216
+ }
2217
+
2218
+ if ( empty( $level->ID ) ) {
2219
+ // We do not have a valid level.
2220
+ return;
2221
+ }
2222
+
2223
+ $gateway_environment = pmpro_getOption( 'gateway_environment' );
2224
+ if ( $gateway_environment === 'sandbox' ) {
2225
+ $stripe_product_id = $level->stripe_product_id_sandbox;
2226
+ } else {
2227
+ $stripe_product_id = $level->stripe_product_id;
2228
+ }
2229
+ if ( empty( $stripe_product_id ) ) {
2230
+ $stripe_product_id = $this->create_product_for_level( $level, $gateway_environment );
2231
+ }
2232
+
2233
+ if ( ! empty( $stripe_product_id ) ) {
2234
+ return $stripe_product_id;
2235
+ }
2236
+ }
2237
+
2238
+ /**
2239
+ * Create a new Stripe product for a given membership level.
2240
+ *
2241
+ * WARNING: Will overwrite old Stripe product set for level if
2242
+ * there is already one set.
2243
+ *
2244
+ * @since 2.7.0
2245
+ *
2246
+ * @param PMPro_Membership_Level|int $level to create product ID for.
2247
+ * @param string $gateway_environment to create product for.
2248
+ * @return string|null ID of new product
2249
+ */
2250
+ private function create_product_for_level( $level, $gateway_environment ) {
2251
+ if ( ! is_a( $level, 'PMPro_Membership_Level' ) ) {
2252
+ if ( is_numeric( $level ) ) {
2253
+ $level = new PMPro_Membership_Level( $level );
2254
+ }
2255
+ }
2256
+
2257
+ if ( empty( $level->ID ) ) {
2258
+ // We do not have a valid level.
2259
+ return;
2260
+ }
2261
+
2262
+ $product_args = array(
2263
+ 'name' => $level->name,
2264
+ );
2265
+ /**
2266
+ * Filter the data sent to Stripe when creating a new product for a membership level.
2267
+ *
2268
+ * @since 2.7.0
2269
+ *
2270
+ * @param array $product_args being sent to Stripe.
2271
+ * @param PMPro_Membership_Level $level that product is being created for.
2272
+ * @param string $gateway_environment being used.
2273
+ */
2274
+ $product_args = apply_filters( 'pmpro_stripe_create_product_for_level', $product_args, $level, $gateway_environment );
2275
+
2276
+ try {
2277
+ $product = Stripe_Product::create( $product_args );
2278
+ if ( ! empty( $product->id ) ) {
2279
+ $meta_name = 'sandbox' === $gateway_environment ? 'stripe_product_id_sandbox' : 'stripe_product_id';
2280
+ update_pmpro_membership_level_meta( $level->ID, $meta_name, $product->id );
2281
+ return $product->id;
2282
+ }
2283
+ } catch (\Throwable $th) {
2284
+ // Could not create product.
2285
+ } catch (\Exception $e) {
2286
+ // Could not create product.
2287
+ }
2288
+ }
2289
+
2290
+ /**
2291
+ * Get a Price for a given product, or create one if it doesn't exist.
2292
+ *
2293
+ * TODO: Add pagination.
2294
+ *
2295
+ * @since 2.7.0
2296
+ *
2297
+ * @param string $product_id to get Price for.
2298
+ * @param float $amount that the Price will charge.
2299
+ * @param string|null $cycle_period for subscription payments.
2300
+ * @param string|null $cycle_number of cycle periods between each subscription payment.
2301
+ *
2302
+ * @return string|null Price ID.
2303
+ */
2304
+ private function get_price_for_product( $product_id, $amount, $cycle_period = null, $cycle_number = null ) {
2305
+ global $pmpro_currency;
2306
+ $currency = pmpro_get_currency();
2307
+
2308
+ $is_recurring = ! empty( $cycle_period ) && ! empty( $cycle_number );
2309
+ $unit_amount = intval( $amount * pow( 10, intval( $currency['decimals'] ) ) );
2310
+ $cycle_period = strtolower( $cycle_period );
2311
+
2312
+ $price_search_args = array(
2313
+ 'product' => $product_id,
2314
+ 'type' => $is_recurring ? 'recurring' : 'one_time',
2315
+ 'currency' => strtolower( $pmpro_currency ),
2316
+ );
2317
+ if ( $is_recurring ) {
2318
+ $price_search_args['recurring'] = array( 'interval' => $cycle_period );
2319
+ }
2320
+
2321
+ try {
2322
+ $prices = Stripe_Price::all( $price_search_args );
2323
+
2324
+ foreach ( $prices as $price ) {
2325
+ // Check whether price is the same. If not, continue.
2326
+ if ( intval( $price->unit_amount ) !== intval( $unit_amount ) ) {
2327
+ continue;
2328
+ }
2329
+ // Check if recurring structure is the same. If not, continue.
2330
+ if ( $is_recurring && ( empty( $price->recurring->interval_count ) || intval( $price->recurring->interval_count ) !== intval( $cycle_number ) ) ) {
2331
+ continue;
2332
+ }
2333
+ return $price->id;
2334
+ }
2335
+ } catch (\Throwable $th) {
2336
+ // There was an error listing prices.
2337
+ return;
2338
+ } catch (\Exception $e) {
2339
+ // There was an error listing prices.
2340
+ return;
2341
+ }
2342
+
2343
+ // Create a new Price.
2344
+ $price_args = array(
2345
+ 'product' => $product_id,
2346
+ 'currency' => strtolower( $pmpro_currency ),
2347
+ 'unit_amount' => $unit_amount
2348
+ );
2349
+ if ( $is_recurring ) {
2350
+ $price_args['recurring'] = array(
2351
+ 'interval' => $cycle_period,
2352
+ 'interval_count' => $cycle_number
2353
+ );
2354
+ }
2355
+
2356
+ try {
2357
+ $price = Stripe_Price::create( $price_args );
2358
+ if ( ! empty( $price->id ) ) {
2359
+ return $price->id;
2360
+ }
2361
+ } catch (\Throwable $th) {
2362
+ // Could not create product.
2363
+ } catch (\Exception $e) {
2364
+ // Could not create product.
2365
+ }
2366
+ }
2367
+
2368
+ /**
2369
+ * Calculate the number of days until the first recurring payment
2370
+ * for a subscription should be charged.
2371
+ *
2372
+ * @since 2.7.0.
2373
+ *
2374
+ * @param MemberOrder $order to calculate trial period days for.
2375
+ * @return int trial period days.
2376
+ */
2377
+ private function calculate_trial_period_days( $order ) {
2378
+ // Use a trial period to set the first recurring payment date.
2379
+ if ( $order->BillingPeriod == "Year" ) {
2380
+ $days_in_billing_period = $order->BillingFrequency * 365; //annual
2381
+ } elseif ( $order->BillingPeriod == "Day" ) {
2382
+ $days_in_billing_period = $order->BillingFrequency * 1; //daily
2383
+ } elseif ( $order->BillingPeriod == "Week" ) {
2384
+ $days_in_billing_period = $order->BillingFrequency * 7; //weekly
2385
+ } else {
2386
+ $days_in_billing_period = $order->BillingFrequency * 30; //assume monthly
2387
+ }
2388
+ $trial_period_days = $order->BillingFrequency * $days_in_billing_period;
2389
+
2390
+ // For free trials, multiply the trial period for each additional free period.
2391
+ if ( ! empty( $order->TrialBillingCycles ) && $order->TrialAmount == 0 ) {
2392
+ $trialOccurrences = (int) $order->TrialBillingCycles;
2393
+ $trial_period_days = $trial_period_days * ( $order->TrialBillingCycles + 1 );
2394
+ }
2395
+
2396
+ //convert to a profile start date
2397
+ $order->ProfileStartDate = date_i18n( "Y-m-d\TH:i:s", strtotime( "+ " . $trial_period_days . " Day", current_time( "timestamp" ) ) );
2398
+
2399
+ //filter the start date
2400
+ $order->ProfileStartDate = apply_filters( "pmpro_profile_start_date", $order->ProfileStartDate, $order );
2401
+
2402
+ //convert back to days
2403
+ $trial_period_days = ceil( abs( strtotime( date_i18n( "Y-m-d\TH:i:s" ), current_time( "timestamp" ) ) - strtotime( $order->ProfileStartDate, current_time( "timestamp" ) ) ) / 86400 );
2404
+ return $trial_period_days;
2405
+ }
2406
+
2407
+ /**
2408
+ * Create a subscription for a customer from an order using a Stripe Price.
2409
+ *
2410
+ * @since 2.7.0.
2411
+ *
2412
+ * @param string $customer_id to create subscription for.
2413
+ * @param MemberOrder $order to pull subscription details from.
2414
+ * @return Stripe_Subscription|bool false if error.
2415
+ */
2416
+ private function create_subscription_for_customer_from_order( $customer_id, $order ) {
2417
+ $subtotal = $order->PaymentAmount;
2418
+ $tax = $order->getTaxForPrice( $subtotal );
2419
+ $amount = pmpro_round_price( (float) $subtotal + (float) $tax );
2420
+
2421
+ // Set up the subscription.
2422
+ $product_id = $this->get_product_id_for_level( $order->membership_id );
2423
+ if ( empty( $product_id ) ) {
2424
+ $order->error = esc_html__( 'Cannot find product for membership level.', 'paid-memberships-pro' );
2425
+ return false;
2426
+ }
2427
+
2428
+ $price = $this->get_price_for_product( $product_id, $amount, $order->BillingPeriod, $order->BillingFrequency );
2429
+ if ( empty( $price ) ) {
2430
+ $order->error = esc_html__( 'Cannot get price.', 'paid-memberships-pro' );
2431
+ return false;
2432
+ }
2433
+
2434
+ $trial_period_days = $this->calculate_trial_period_days( $order );
2435
+
2436
+ try {
2437
+ $subscription_params = array(
2438
+ 'customer' => $customer_id,
2439
+ 'items' => array(
2440
+ array( 'price' => $price ),
2441
+ ),
2442
+ 'trial_period_days' => $trial_period_days,
2443
+ 'expand' => array(
2444
+ 'pending_setup_intent.payment_method',
2445
+ ),
2446
+ );
2447
+ if ( ! self::using_legacy_keys() ) {
2448
+ $params['application_fee_percent'] = self::get_application_fee_percentage();
2449
+ }
2450
+ $subscription_params = apply_filters( 'pmpro_stripe_create_subscription_array', $subscription_params );
2451
+ $subscription = Stripe_Subscription::create( $subscription_params );
2452
+ } catch ( Stripe\Error\Base $e ) {
2453
+ $order->error = $e->getMessage();
2454
+ return false;
2455
+ } catch ( \Throwable $e ) {
2456
+ $order->error = $e->getMessage();
2457
+ return false;
2458
+ } catch ( \Exception $e ) {
2459
+ $order->error = $e->getMessage();
2460
+ return false;
2461
+ }
2462
+ return $subscription;
2463
+ }
2464
+
2465
+ /**
2466
+ * Retrieve a payment intent.
2467
+ *
2468
+ * @since 2.7.0.
2469
+ *
2470
+ * @param string $payment_intent_id to retrieve.
2471
+ * @return Stripe_PaymentIntent|string error.
2472
+ */
2473
+ private function retrieve_payment_intent( $payment_intent_id ) {
2474
+ try {
2475
+ $payment_intent = Stripe_PaymentIntent::retrieve( $payment_intent_id );
2476
+ } catch ( Stripe\Error\Base $e ) {
2477
+ return $e->getMessage();
2478
+ } catch ( \Throwable $e ) {
2479
+ return $e->getMessage();
2480
+ } catch ( \Exception $e ) {
2481
+ return $e->getMessage();
2482
+ }
2483
+ return $payment_intent;
2484
+ }
2485
+
2486
+ /**
2487
+ * Retrieve a setup intent.
2488
+ *
2489
+ * @since 2.7.0.
2490
+ *
2491
+ * @param string $setup_intent_id to retrieve.
2492
+ * @return Stripe_SetupIntent|string error.
2493
+ */
2494
+ private function retrieve_setup_intent( $setup_intent_id ) {
2495
+ try {
2496
+ $setup_intent_args = array(
2497
+ 'id' => $setup_intent_id,
2498
+ 'expand' => array(
2499
+ 'latest_attempt',
2500
+ ),
2501
+ );
2502
+ $setup_intent = Stripe_SetupIntent::retrieve( $setup_intent_args );
2503
+ } catch ( Stripe\Error\Base $e ) {
2504
+ return $e->getMessage();
2505
+ } catch ( \Throwable $e ) {
2506
+ return $e->getMessage();
2507
+ } catch ( \Exception $e ) {
2508
+ return $e->getMessage();
2509
+ }
2510
+ return $setup_intent;
2511
+ }
2512
+
2513
+ /**
2514
+ * Confirm the payment intent after authentication.
2515
+ *
2516
+ * @since 2.7.0.
2517
+ *
2518
+ * @param string $payment_intent_id to confirm.
2519
+ * @return Stripe_PaymentIntent|string error.
2520
+ */
2521
+ private function process_payment_intent( $payment_intent_id ) {
2522
+ // Get the payment intent.
2523
+ $payment_intent = $this->retrieve_payment_intent( $payment_intent_id );
2524
+ if ( is_string( $payment_intent ) ) {
2525
+ // There was an issue retrieving the payment intent.
2526
+ return $payment_intent;
2527
+ }
2528
+
2529
+ // Confirm the payment.
2530
+ try {
2531
+ $params = array(
2532
+ 'expand' => array(
2533
+ 'payment_method',
2534
+ 'customer'
2535
+ ),
2536
+ );
2537
+ $payment_intent->confirm( $params );
2538
+ } catch ( Stripe\Error\Base $e ) {
2539
+ return $e->getMessage();
2540
+ } catch ( \Throwable $e ) {
2541
+ return $e->getMessage();
2542
+ } catch ( \Exception $e ) {
2543
+ return $e->getMessage();
2544
+ }
2545
+
2546
+ // Check that the confirmation was successful.
2547
+ if ( 'requires_action' == $payment_intent->status ) {
2548
+ return __( 'Customer authentication is required to finish setting up your subscription. Please complete the verification steps issued by your payment provider.', 'paid-memberships-pro' );
2549
+ }
2550
+
2551
+ return $payment_intent;
2552
+ }
2553
+
2554
+ /**
2555
+ * Confirm the setup intent after authentication.
2556
+ *
2557
+ * @since 2.7.0.
2558
+ *
2559
+ * @param string $setup_intent_id to confirm.
2560
+ * @return Stripe_SetupIntent|string error.
2561
+ */
2562
+ private function process_setup_intent( $setup_intent_id ) {
2563
+ // Get the setup intent.
2564
+ $setup_intent = $this->retrieve_setup_intent( $setup_intent_id );
2565
+ if ( is_string( $setup_intent ) ) {
2566
+ return $setup_intent;
2567
+ }
2568
+
2569
+ // Make sure that the setup intent was recently confirmed.
2570
+ /**
2571
+ * The time in seconds that a setup intent must be confirmed within.
2572
+ *
2573
+ * @since 2.7.0
2574
+ *
2575
+ * @param int $seconds_to_confirm_setup_intent The time in seconds that a setup intent must be confirmed within.
2576
+ */
2577
+ $setup_intent_timeout = apply_filters( 'pmpro_stripe_setup_intent_timeout', 60 * 10 );
2578
+ $last_setup_attempt_created = $setup_intent->latest_attempt->created;
2579
+ if ( $last_setup_attempt_created < time() - $setup_intent_timeout ) {
2580
+ return __( 'Cannot reuse an old setup intent.', 'paid-memberships-pro' );
2581
+ }
2582
+
2583
+ // Make sure that the confirmation was successful.
2584
+ if ( 'requires_action' === $setup_intent->status ) {
2585
+ return __( 'Customer authentication is required to finish setting up your subscription. Please complete the verification steps issued by your payment provider.', 'paid-memberships-pro' );
2586
+ }
2587
+ return $setup_intent;
2588
+ }
2589
+
2590
+ /**
2591
+ * Add a subscription ID to the metadata of a setup intent.
2592
+ *
2593
+ * @since 2.7.0.
2594
+ *
2595
+ * @param Stripe_SetupIntent $setup_intent The setup intent to add metadata to.
2596
+ * @param string $subscription_id The subscription ID that created this setup intent.
2597
+ *
2598
+ * @return Stripe_SetupIntent|string The setup intent object or an error message string.
2599
+ */
2600
+ private function add_subscription_id_to_setup_intent( $setup_intent, $subscription_id ) {
2601
+ try {
2602
+ $setup_intent = Stripe_SetupIntent::update(
2603
+ $setup_intent->id,
2604
+ array(
2605
+ 'metadata' => array(
2606
+ 'subscription_id' => $subscription_id,
2607
+ ),
2608
+ 'expand' => array(
2609
+ 'payment_method',
2610
+ ),
2611
+ )
2612
+ );
2613
+ } catch ( \Throwable $e ) {
2614
+ return __( "Error adding metadata to setup intent.", 'paid-memberships-pro' );
2615
+ } catch ( \Exception $e ) {
2616
+ return __( "Error adding metadata to setup intent.", 'paid-memberships-pro' );
2617
+ }
2618
+ return $setup_intent;
2619
+ }
2620
+
2621
+ /**
2622
+ * Temporary function to allow users to view and delete subscription updates.
2623
+ * Will be removed once subscription updates are completely deprecated.
2624
+ *
2625
+ * @since 2.7.0.
2626
+ *
2627
+ * @param WP_User $user whose profile is being shown.
2628
+ * @param Stripe_Customer $customer associated with that user.
2629
+ */
2630
+ private function user_profile_fields_subscription_updates( $user, $customer ) {
2631
+ global $pmpro_currency_symbol;
2632
+
2633
+ $subscriptions = $customer->subscriptions->all();
2634
+ if ( empty( $subscriptions ) ) {
2635
+ // User does not have any subscriptions to udpate. Delete all updates.
2636
+ delete_user_meta( $user->ID, 'pmpro_stripe_updates' );
2637
+ return;
2638
+ }
2639
+
2640
+ $cycles = array(
2641
+ __( 'Day(s)', 'paid-memberships-pro' ) => 'Day',
2642
+ __( 'Week(s)', 'paid-memberships-pro' ) => 'Week',
2643
+ __( 'Month(s)', 'paid-memberships-pro' ) => 'Month',
2644
+ __( 'Year(s)', 'paid-memberships-pro' ) => 'Year',
2645
+ );
2646
+
2647
+ $current_year = date_i18n( 'Y' );
2648
+ $current_month = date_i18n( 'm' );
2649
+ ?>
2650
+ <h3><?php esc_html_e( 'Subscription Updates', 'paid-memberships-pro' ); ?></h3>
2651
+ <p><?php esc_html_e( 'Subscription updates will be deprecated in a future version of PMPro, though your existing subscription updates will still trigger as expected. We now instead reccomend updating the subscription directly in Stripe.', 'paid-memberships-pro' ); ?></p>
2652
+ <table class="form-table">
2653
+ <input type='hidden' name='pmpro_subscription_updates_visible' value='1' />
2654
+ <tr>
2655
+ <th><label><?php esc_html_e( 'Update', 'paid-memberships-pro' ); ?></label></th>
2656
+ <td id="updates_td">
2657
+ <?php
2658
+ $updates = $user->pmpro_stripe_updates;
2659
+
2660
+ foreach ( $updates as $update ) {
2661
+ ?>
2662
+ <div class="updates_update">
2663
+ <select class="updates_when" name="updates_when[]" disabled>
2664
+ <option value="now" <?php selected( $update['when'], 'now' ); ?>>Now</option>
2665
+ <option value="payment" <?php selected( $update['when'], 'payment' ); ?>>After
2666
+ Next Payment
2667
+ </option>
2668
+ <option value="date" <?php selected( $update['when'], 'date' ); ?>>On Date
2669
+ </option>
2670
+ </select>
2671
+ <span class="updates_date"
2672
+ <?php if ( $update['when'] != 'date' ) { ?>style="display: none;"<?php } ?>>
2673
+ <select name="updates_date_month[]" disabled>
2674
+ <?php
2675
+ for ( $i = 1; $i < 13; $i ++ ) {
2676
+ ?>
2677
+ <option value="<?php echo str_pad( $i, 2, '0', STR_PAD_LEFT ); ?>"
2678
+ <?php if ( ! empty( $update['date_month'] ) && $update['date_month'] == $i ) { ?>selected="selected"<?php } ?>>
2679
+ <?php echo date_i18n( 'M', strtotime( $i . '/15/' . $current_year ) ); ?>
2680
+ </option>
2681
+ <?php
2682
+ }
2683
+ ?>
2684
+ </select>
2685
+ <input name="updates_date_day[]" type="text" size="2"
2686
+ value="<?php if ( ! empty( $update['date_day'] ) ) {
2687
+ echo esc_attr( $update['date_day'] );
2688
+ } ?>" readonly/>
2689
+ <input name="updates_date_year[]" type="text" size="4"
2690
+ value="<?php if ( ! empty( $update['date_year'] ) ) {
2691
+ echo esc_attr( $update['date_year'] );
2692
+ } ?>" readonly/>
2693
+ </span>
2694
+ <span class="updates_billing"
2695
+ <?php if ( $update['when'] == "now" ) { ?>style="display: none;"<?php } ?>>
2696
+ <?php echo $pmpro_currency_symbol; ?><input name="updates_billing_amount[]" type="text"
2697
+ size="10"
2698
+ value="<?php echo esc_attr( $update['billing_amount'] ); ?>"
2699
+ readonly/>
2700
+ <small><?php esc_html_e( 'per', 'paid-memberships-pro' ); ?></small>
2701
+ <input name="updates_cycle_number[]" type="text" size="5"
2702
+ value="<?php echo esc_attr( $update['cycle_number'] ); ?>" readonly/>
2703
+ <select name="updates_cycle_period[]" disabled>
2704
+ <?php
2705
+ foreach ( $cycles as $name => $value ) {
2706
+ echo "<option value='" . esc_attr( $value ) . "'";
2707
+ if ( ! empty( $update['cycle_period'] ) && $update['cycle_period'] == $value ) {
2708
+ echo " selected='selected'";
2709
+ }
2710
+ echo ">" . esc_html( $name ) . "</option>";
2711
+ }
2712
+ ?>
2713
+ </select>
2714
+ </span>
2715
+ <span>
2716
+ <a class="updates_remove" href="javascript:void(0);"><?php esc_html_e( 'Remove', 'paid-memberships-pro' ); ?></a>
2717
+ </span>
2718
+ </div>
2719
+ <script>
2720
+ jQuery(function () {
2721
+ //remove updates when clicking
2722
+ jQuery('.updates_remove').on('click', function () {
2723
+ jQuery(this).parent().parent().remove();
2724
+ });
2725
+ jQuery('form').on('submit', function () {
2726
+ // Makes sure that disabled select fields are still submitted.
2727
+ jQuery(this).find(':input').prop('disabled', false);
2728
+ });
2729
+ });
2730
+ </script>
2731
+ <?php
2732
+ }
2733
+ ?>
2734
+ </td>
2735
+ </tr>
2736
+ </table>
2737
+ <?php
2738
+ }
2739
+
2740
+
2741
+
2742
+ /****************************************
2743
+ ******* METHODS BECOMING PRIVATE *******
2744
+ ****************************************/
2745
+ /**
2746
+ * Get available webhooks
2747
+ *
2748
+ * @since 2.4
2749
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2750
+ */
2751
+ public static function get_webhooks( $limit = 10 ) {
2752
+ // Show deprecation warning if called publically.
2753
+ pmpro_method_should_be_private( '2.7.0' );
2754
+
2755
+ if ( ! class_exists( 'Stripe\WebhookEndpoint' ) ) {
2756
+ // Load Stripe library.
2757
+ new PMProGateway_stripe();
2758
+ if ( ! class_exists( 'Stripe\WebhookEndpoint' ) ) {
2759
+ // Couldn't load library.
2760
+ return false;
2761
+ }
2762
+ }
2763
+
2764
+ try {
2765
+ $webhooks = Stripe_Webhook::all( [ 'limit' => apply_filters( 'pmpro_stripe_webhook_retrieve_limit', $limit ) ] );
2766
+ } catch (\Throwable $th) {
2767
+ $webhooks = $th->getMessage();
2768
+ } catch (\Exception $e) {
2769
+ $webhooks = $e->getMessage();
2770
+ }
2771
+
2772
+ return $webhooks;
2773
+ }
2774
+
2775
+ /**
2776
+ * Get current webhook URL for website to compare.
2777
+ *
2778
+ * @since 2.4
2779
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2780
+ */
2781
+ public static function get_site_webhook_url() {
2782
+ // Show deprecation warning if called publically.
2783
+ pmpro_method_should_be_private( '2.7.0' );
2784
+ return admin_url( 'admin-ajax.php' ) . '?action=stripe_webhook';
2785
+ }
2786
+
2787
+ /**
2788
+ * List of current enabled events required for PMPro to work.
2789
+ *
2790
+ * @since 2.4
2791
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2792
+ */
2793
+ public static function webhook_events() {
2794
+ // Show deprecation warning if called publically.
2795
+ pmpro_method_should_be_private( '2.7.0' );
2796
+ return apply_filters( 'pmpro_stripe_webhook_events', array(
2797
+ 'invoice.payment_succeeded',
2798
+ 'invoice.payment_action_required',
2799
+ 'customer.subscription.deleted',
2800
+ 'charge.failed'
2801
+ ) );
2802
+ }
2803
 
2804
+ /**
2805
+ * Create webhook with relevant events
2806
+ *
2807
+ * @since 2.4
2808
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2809
+ */
2810
+ public static function create_webhook() {
2811
+ // Show deprecation warning if called publically.
2812
+ pmpro_method_should_be_private( '2.7.0' );
2813
+
2814
+ try {
2815
+ $create = Stripe_Webhook::create([
2816
+ 'url' => self::get_site_webhook_url(),
2817
+ 'enabled_events' => self::webhook_events(),
2818
+ 'api_version' => PMPRO_STRIPE_API_VERSION,
2819
+ ]);
2820
+
2821
+ if ( $create ) {
2822
+ return $create->id;
2823
  }
2824
+ } catch (\Throwable $th) {
2825
+ //throw $th;
2826
+ return new WP_Error( 'error', $th->getMessage() );
2827
+ } catch (\Exception $e) {
2828
+ //throw $th;
2829
+ return new WP_Error( 'error', $e->getMessage() );
2830
+ }
2831
+
2832
+ }
2833
 
2834
+ /**
2835
+ * See if a webhook is registered with Stripe.
2836
+ *
2837
+ * @since 2.4
2838
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2839
+ */
2840
+ public static function does_webhook_exist( $force = false ) {
2841
+ // Show deprecation warning if called publically.
2842
+ pmpro_method_should_be_private( '2.7.0' );
2843
 
2844
+ static $cached_webhook = null;
2845
+ if ( ! empty( $cached_webhook ) && ! $force ) {
2846
+ return $cached_webhook;
2847
+ }
2848
 
2849
+ $webhooks = self::get_webhooks();
2850
+
2851
+ $webhook_id = false;
2852
+ if ( ! empty( $webhooks ) && ! empty( $webhooks['data'] ) ) {
2853
+
2854
+ $pmpro_webhook_url = self::get_site_webhook_url();
2855
+
2856
+ foreach( $webhooks as $webhook ) {
2857
+ if ( $webhook->url == $pmpro_webhook_url ) {
2858
+ $webhook_id = $webhook->id;
2859
+ $webhook_events = $webhook->enabled_events;
2860
+ $webhook_api_version = $webhook->api_version;
2861
+ $webhook_status = $webhook->status;
2862
+ continue;
2863
+ }
2864
  }
2865
+ } else {
2866
+ $webhook_id = false; // make sure it's false if none are found.
2867
+ }
2868
 
2869
+ if ( $webhook_id ) {
2870
+ $webhook_data = array();
2871
+ $webhook_data['webhook_id'] = $webhook_id;
2872
+ $webhook_data['enabled_events'] = $webhook_events;
2873
+ $webhook_data['api_version'] = $webhook_api_version;
2874
+ $webhook_data['status'] = $webhook_status;
2875
+ $cached_webhook = $webhook_data;
2876
+ } else {
2877
+ $cached_webhook = false;
2878
+ }
2879
+ return $cached_webhook;
2880
+ }
2881
+
2882
+ /**
2883
+ * Get a list of events that are missing between the created existing webhook and required webhook events for Paid Memberships Pro.
2884
+ *
2885
+ * @since 2.4
2886
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2887
+ */
2888
+ public static function check_missing_webhook_events( $webhook_events ) {
2889
+ // Show deprecation warning if called publically.
2890
+ pmpro_method_should_be_private( '2.7.0' );
2891
+
2892
+ // Get required events
2893
+ $pmpro_webhook_events = self::webhook_events();
2894
 
2895
+ // No missing events if webhook event is "All Events" selected.
2896
+ if ( is_array( $webhook_events ) && $webhook_events[0] === '*' ) {
2897
  return false;
2898
+ }
2899
+
2900
+ if ( count( array_diff( $pmpro_webhook_events, $webhook_events ) ) ) {
2901
+ $events = array_unique( array_merge( $pmpro_webhook_events, $webhook_events ) );
2902
+ // Force reset of indexes for Stripe.
2903
+ $events = array_values( $events );
2904
+ } else {
2905
+ $events = false;
2906
  }
2907
 
2908
+ return $events;
2909
+ }
 
2910
 
2911
+ /**
2912
+ * Update required webhook enabled events.
2913
+ *
2914
+ * @since 2.4
2915
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2916
+ */
2917
+ public static function update_webhook_events() {
2918
+ // Show deprecation warning if called publically.
2919
+ pmpro_method_should_be_private( '2.7.0' );
2920
 
2921
+ // Also checks database to see if it's been saved.
2922
+ $webhook = self::does_webhook_exist();
 
 
 
 
2923
 
2924
+ if ( empty( $webhook ) ) {
2925
+ $create = self::create_webhook();
2926
+ return $create;
2927
+ }
 
 
 
 
 
 
 
2928
 
2929
+ // Bail if no enabled events for a webhook are passed through.
2930
+ if ( ! isset( $webhook['enabled_events'] ) ) {
2931
+ return;
2932
+ }
2933
+
2934
+ $events = self::check_missing_webhook_events( $webhook['enabled_events'] );
2935
+
2936
+ if ( $events ) {
2937
+
2938
+ try {
2939
+ $update = Stripe_Webhook::update(
2940
+ $webhook['webhook_id'],
2941
+ ['enabled_events' => $events ]
2942
+ );
2943
+
2944
+ if ( $update ) {
2945
+ return $update;
2946
+ }
2947
+ } catch (\Throwable $th) {
2948
+ //throw $th;
2949
+ return new WP_Error( 'error', $th->getMessage() );
2950
+ } catch (\Exception $e) {
2951
+ //throw $th;
2952
+ return new WP_Error( 'error', $e->getMessage() );
2953
  }
2954
+
 
 
2955
  }
2956
+
2957
+ }
2958
 
2959
+ /**
2960
+ * Delete an existing webhook.
2961
+ *
2962
+ * @since 2.4
2963
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2964
+ */
2965
+ public static function delete_webhook( $webhook_id, $secretkey = false ) {
2966
+ // Show deprecation warning if called publically.
2967
+ pmpro_method_should_be_private( '2.7.0' );
2968
+
2969
+ if ( empty( $secretkey ) ) {
2970
+ $secretkey = self::get_secretkey();
2971
+ }
2972
+ if ( is_array( $webhook_id ) ) {
2973
+ $webhook_id = $webhook_id['webhook_id'];
2974
+ }
2975
+
2976
+ try {
2977
+ $stripe = new Stripe_Client( $secretkey );
2978
+ $delete = $stripe->webhookEndpoints->delete( $webhook_id, [] );
2979
+ } catch (\Throwable $th) {
2980
+ return new WP_Error( 'error', $th->getMessage() );
2981
+ } catch (\Exception $e) {
2982
+ return new WP_Error( 'error', $e->getMessage() );
2983
+ }
2984
+
2985
+ return $delete;
2986
  }
2987
 
2988
  /**
2989
  * Helper method to save the subscription ID to make sure the membership doesn't get cancelled by the webhook
2990
+ *
2991
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
2992
  */
2993
+ public static function ignoreCancelWebhookForThisSubscription( $subscription_id, $user_id = null ) {
2994
+ pmpro_method_should_be_private( '2.7.0' );
2995
+
2996
  if ( empty( $user_id ) ) {
2997
  global $current_user;
2998
  $user_id = $current_user->ID;
3012
  }
3013
 
3014
  /**
3015
+ * Helper method to process a Stripe subscription update.
3016
+ *
3017
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
3018
+ *
3019
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3020
  */
3021
  static function updateSubscription( $update, $user_id ) {
3022
+ pmpro_method_should_be_private( '2.7.0' );
3023
  global $wpdb;
3024
 
3025
  //get level for user
3029
  $last_order = new MemberOrder();
3030
  $last_order->getLastMemberOrder( $user_id );
3031
  $last_order->setGateway( 'stripe' );
3032
+ $last_order->Gateway->update_customer_at_checkout( $last_order );
3033
 
3034
+ $subscription = $last_order->Gateway->get_subscription( $last_order->subscription_transaction_id );
3035
 
3036
  if ( ! empty( $subscription ) ) {
3037
  $end_timestamp = $subscription->current_period_end;
3079
  }, 10, 2 );
3080
 
3081
  //update subscription
3082
+ $customer = $update_order->Gateway->update_customer_at_checkout( $update_order, true );
3083
+ $order->stripe_customer = $customer;
3084
  $update_order->Gateway->process_subscriptions( $update_order );
3085
 
3086
  //update membership
3104
  }
3105
 
3106
  /**
3107
+ * Update the payment method for a subscription.
3108
  *
3109
+ * Only called on update billing page. Should be completely deprecated if we switch to using Stripe Customer Portal.
3110
+ *
3111
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3112
+ *
3113
+ * @param MemberOrder $order The MemberOrder object.
3114
  */
3115
+ public function update_payment_method_for_subscriptions( &$order ) {
3116
+ pmpro_method_should_be_private( '2.7.0' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3117
 
 
 
 
 
 
 
3118
  // get customer
3119
+ $customer = $this->update_customer_at_checkout( $order );
3120
 
3121
+ if ( empty( $customer ) ) {
3122
  return false;
3123
  }
3124
 
3125
  // get all subscriptions
3126
+ if ( ! empty( $customer->subscriptions ) ) {
3127
+ $subscriptions = $customer->subscriptions->all();
3128
  }
3129
 
3130
  foreach( $subscriptions as $subscription ) {
3134
  }
3135
 
3136
  // check if we have a related order for it
3137
+ $one_order = new MemberOrder();
3138
+ $one_order->getLastMemberOrderBySubscriptionTransactionID( $subscription->id );
3139
+ if ( empty( $one_order ) || empty( $one_order->id ) ) {
3140
+ continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3141
  }
3142
+
3143
+ // update the payment method
3144
+ $subscription->default_payment_method = $customer->invoice_settings->default_payment_method;
3145
+ $subscription->save();
 
 
 
3146
  }
3147
+ return true;
3148
  }
3149
 
3150
  /**
3151
  * Helper method to cancel a subscription at Stripe and also clear up any upaid invoices.
3152
  *
3153
  * @since 1.8
3154
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3155
  */
3156
+ public function cancelSubscriptionAtGateway( $subscription, $preserve_local_membership = false ) {
3157
+ pmpro_method_should_be_private( '2.7.0' );
3158
+
3159
  // Check if a valid sub.
3160
  if ( empty( $subscription ) || empty( $subscription->id ) ) {
3161
  return false;
3179
  }
3180
 
3181
  // Okay have an order, so get customer so we can cancel invoices too
3182
+ $customer = $this->update_customer_at_checkout( $order );
3183
 
3184
  // Get open invoices.
3185
+ $invoices = Stripe_Invoice::all(['customer' => $customer->id, 'status' => 'open']);
3186
 
3187
  // Found it, cancel it.
3188
  try {
3197
 
3198
  // Sometimes we don't want to cancel the local membership when Stripe sends its webhook.
3199
  if ( $preserve_local_membership ) {
3200
+ self::ignoreCancelWebhookForThisSubscription( $subscription->id, $order->user_id );
3201
  }
3202
 
3203
  // Cancel
3212
  }
3213
 
3214
  /**
3215
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3216
+ */
3217
+ public function get_payment_method( &$order ) {
3218
+ pmpro_method_should_be_private( '2.7.0' );
3219
+ if ( ! empty( $order->payment_method_id ) ) {
3220
+ try {
3221
+ $payment_method = Stripe_PaymentMethod::retrieve( $order->payment_method_id );
3222
+ } catch ( Stripe\Error\Base $e ) {
3223
+ $order->error = $e->getMessage();
3224
+ return false;
3225
+ } catch ( \Throwable $e ) {
3226
+ $order->error = $e->getMessage();
3227
+ return false;
3228
+ } catch ( \Exception $e ) {
3229
+ $order->error = $e->getMessage();
3230
+ return false;
3231
+ }
3232
+ }
3233
+
3234
+ if ( empty( $payment_method ) ) {
3235
+ return false;
3236
+ }
3237
+
3238
+ return $payment_method;
3239
+ }
3240
+
3241
+ /**
3242
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private in a future version.
3243
+ */
3244
+ public function process_charges( &$order ) {
3245
+ pmpro_method_should_be_private( '2.7.0' );
3246
+ if ( 0 == floatval( $order->InitialPayment ) ) {
3247
+ return true;
3248
+ }
3249
+
3250
+ $payment_intent = $this->get_payment_intent( $order );
3251
+ if ( empty( $payment_intent) ) {
3252
+ // There was an error, and the message should already
3253
+ // be saved on the order.
3254
+ return false;
3255
+ }
3256
+ // Save payment intent to order so that we can use it in confirm_payment_intent().
3257
+ $order->stripe_payment_intent = $payment_intent;
3258
+
3259
+ $this->confirm_payment_intent( $order );
3260
+
3261
+ if ( ! empty( $order->error ) ) {
3262
+ $order->error = $order->error;
3263
+
3264
+ return false;
3265
+ }
3266
+
3267
+ return true;
3268
+ }
3269
+
3270
+ /**
3271
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
3272
  *
3273
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3274
+ *
3275
+ * @param MemberOrder $order The MemberOrder object.
3276
  */
3277
+ public function get_setup_intent( &$order ) {
3278
+ pmpro_method_should_be_private( '2.7.0' );
3279
+ if ( ! empty( $order->setup_intent_id ) ) {
3280
+ try {
3281
+ $setup_intent = Stripe_SetupIntent::retrieve( $order->setup_intent_id );
3282
+ } catch ( Stripe\Error\Base $e ) {
3283
+ $order->error = $e->getMessage();
3284
+ return false;
3285
+ } catch ( \Throwable $e ) {
3286
+ $order->error = $e->getMessage();
3287
+ return false;
3288
+ } catch ( \Exception $e ) {
3289
+ $order->error = $e->getMessage();
3290
+ return false;
3291
+ }
3292
+ }
3293
 
3294
+ if ( empty( $setup_intent ) ) {
3295
+ $setup_intent = $this->create_setup_intent( $order );
3296
+ }
 
3297
 
3298
+ if ( empty( $setup_intent ) ) {
3299
+ return false;
3300
+ }
3301
+
3302
+ return $setup_intent;
3303
+ }
3304
+
3305
+ /**
3306
+ * @deprecated 2.7.0. Use get_setup_intent() instead.
3307
+ */
3308
+ public function set_setup_intent( &$order, $force = false ) {
3309
+ _deprecated_function( __FUNCTION__, '2.7.0', 'get_setup_intent()' );
3310
+ if ( ! empty( $this->setup_intent ) && ! $force ) {
3311
+ return true;
3312
+ }
3313
+
3314
+ $setup_intent = $this->get_setup_intent( $order );
3315
+
3316
+ if ( empty( $setup_intent ) ) {
3317
+ return false;
3318
+ }
3319
+
3320
+ $this->setup_intent = $setup_intent;
3321
+
3322
+ return true;
3323
+ }
3324
+
3325
+ /**
3326
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
3327
+ *
3328
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3329
+ *
3330
+ * @param MemberOrder $order The MemberOrder object.
3331
+ */
3332
+ public function create_setup_intent( &$order ) {
3333
+ pmpro_method_should_be_private( '2.7.0' );
3334
+ $this->create_plan( $order );
3335
+ $order->stripe_subscription = $this->create_subscription( $order );
3336
+ $this->delete_plan( $order );
3337
+
3338
+ if ( ! empty( $order->error ) || empty( $order->stripe_subscription->pending_setup_intent ) ) {
3339
+ return false;
3340
+ }
3341
+
3342
+ return $order->stripe_subscription->pending_setup_intent;
3343
+ }
3344
+
3345
+ /**
3346
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3347
+ */
3348
+ public function confirm_payment_intent( &$order ) {
3349
+ pmpro_method_should_be_private( '2.7.0' );
3350
+ try {
3351
+ $params = array(
3352
+ 'expand' => array(
3353
+ 'payment_method',
3354
+ ),
3355
+ );
3356
+ $order->stripe_payment_intent->confirm( $params );
3357
+ } catch ( Stripe\Error\Base $e ) {
3358
+ $order->error = $e->getMessage();
3359
+ return false;
3360
+ } catch ( \Throwable $e ) {
3361
+ $order->error = $e->getMessage();
3362
+ return false;
3363
+ } catch ( \Exception $e ) {
3364
+ $order->error = $e->getMessage();
3365
+ return false;
3366
+ }
3367
+
3368
+ if ( 'requires_action' == $order->stripe_payment_intent->status ) {
3369
+ $order->errorcode = true;
3370
+ $order->error = __( 'Customer authentication is required to complete this transaction. Please complete the verification steps issued by your payment provider.', 'paid-memberships-pro' );
3371
+ $order->error_type = 'pmpro_alert';
3372
+
3373
+ return false;
3374
+ }
3375
+
3376
+ return true;
3377
+ }
3378
+
3379
+ /**
3380
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3381
+ */
3382
+ public function confirm_setup_intent( &$order ) {
3383
+ pmpro_method_should_be_private( '2.7.0' );
3384
+ if ( empty( $order->stripe_setup_intent ) ) {
3385
+ return true;
3386
+ }
3387
+
3388
+ if ( 'requires_action' === $order->stripe_setup_intent->status ) {
3389
+ $order->errorcode = true;
3390
+ $order->error = __( 'Customer authentication is required to finish setting up your subscription. Please complete the verification steps issued by your payment provider.', 'paid-memberships-pro' );
3391
+
3392
+ return false;
3393
+ }
3394
+
3395
+ }
3396
+
3397
+ /**
3398
+ * Get available Apple Pay domains.
3399
+ *
3400
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3401
+ */
3402
+ public function pmpro_get_apple_pay_domains( $limit = 10 ) {
3403
+ pmpro_method_should_be_private( '2.7.0' );
3404
+ try {
3405
+ $apple_pay_domains = Stripe_ApplePayDomain::all( [ 'limit' => apply_filters( 'pmpro_stripe_apple_pay_domain_retrieve_limit', $limit ) ] );
3406
+ } catch (\Throwable $th) {
3407
+ $apple_pay_domains = array();
3408
+ }
3409
+
3410
+ return $apple_pay_domains;
3411
+ }
3412
+
3413
+ /**
3414
+ * Register domain with Apple Pay.
3415
+ *
3416
+ * @since 2.4
3417
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3418
+ */
3419
+ public function pmpro_create_apple_pay_domain() {
3420
+ pmpro_method_should_be_private( '2.7.0' );
3421
+ try {
3422
+ $create = Stripe_ApplePayDomain::create([
3423
+ 'domain_name' => $_SERVER['HTTP_HOST'],
3424
+ ]);
3425
+ } catch (\Throwable $th) {
3426
+ //throw $th;
3427
+ return false;
3428
+ }
3429
+ return $create;
3430
+ }
3431
+
3432
+ /**
3433
+ * See if domain is registered with Apple Pay.
3434
+ *
3435
+ * @since 2.4
3436
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3437
+ */
3438
+ public function pmpro_does_apple_pay_domain_exist() {
3439
+ pmpro_method_should_be_private( '2.7.0' );
3440
+ $apple_pay_domains = $this->pmpro_get_apple_pay_domains();
3441
+
3442
+ if ( empty( $apple_pay_domains ) ) {
3443
+ return false;
3444
+ }
3445
+
3446
+ foreach( $apple_pay_domains as $apple_pay_domain ) {
3447
+ if ( $apple_pay_domain->domain_name === $_SERVER['HTTP_HOST'] ) {
3448
+ return true;
3449
  }
3450
  }
3451
+ return false;
3452
+ }
3453
 
3454
+ /**
3455
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3456
+ */
3457
+ public function get_account() {
3458
+ pmpro_method_should_be_private( '2.7.0' );
3459
+ try {
3460
+ $account = Stripe_Account::retrieve();
3461
+ } catch ( Stripe\Error\Base $e ) {
3462
+ return false;
3463
+ } catch ( \Throwable $e ) {
3464
+ return false;
3465
+ } catch ( \Exception $e ) {
3466
+ return false;
3467
+ }
3468
+
3469
+ if ( empty( $account ) ) {
3470
+ return false;
3471
+ }
3472
+
3473
+ return $account;
3474
+ }
3475
+
3476
+ /**
3477
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3478
+ */
3479
+ public static function get_account_country() {
3480
+ pmpro_method_should_be_private( '2.7.0' );
3481
+ $account_country = get_transient( 'pmpro_stripe_account_country' );
3482
+ if ( empty( $account_country ) ) {
3483
+ $stripe = new PMProGateway_stripe();
3484
+ $account = $stripe->get_account();
3485
+ if ( ! empty( $account ) && ! empty( $account->country ) ) {
3486
+ $account_country = $account->country;
3487
+ set_transient( 'pmpro_stripe_account_country', $account_country );
3488
+ }
3489
+ }
3490
+ return $account_country ?: 'US';
3491
+ }
3492
+
3493
+ /**
3494
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3495
+ */
3496
+ public function clean_up( &$order ) {
3497
+ pmpro_method_should_be_private( '2.7.0' );
3498
+ if ( ! empty( $order->stripe_payment_intent ) && 'succeeded' == $order->stripe_payment_intent->status ) {
3499
+ $order->payment_transaction_id = $order->stripe_payment_intent->charges->data[0]->id;
3500
+ }
3501
+
3502
+ if ( empty( $order->subscription_transaction_id ) && ! empty( $order->stripe_subscription ) ) {
3503
+ $order->subscription_transaction_id = $order->stripe_subscription->id;
3504
+ }
3505
+ }
3506
+
3507
+ /**
3508
+ * Get percentage of Stripe payment to charge as application fee.
3509
+ *
3510
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3511
+ *
3512
+ * @return int percentage to charge for application fee.
3513
+ */
3514
+ public static function get_application_fee_percentage() {
3515
+ pmpro_method_should_be_private( '2.7.0' );
3516
+ $application_fee_percentage = pmpro_license_isValid( null, 'plus' ) ? 0 : 1;
3517
+ $application_fee_percentage = apply_filters( 'pmpro_set_application_fee_percentage', $application_fee_percentage );
3518
+ return round( floatval( $application_fee_percentage ), 2 );
3519
+ }
3520
+
3521
+ /**
3522
+ * Add application fee to params to be sent to Stripe.
3523
+ *
3524
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3525
+ *
3526
+ * @param array $params to be sent to Stripe.
3527
+ * @param bool $add_percent true if percentage should be added, false if actual amount.
3528
+ * @return array params with application fee if applicable.
3529
+ */
3530
+ public static function add_application_fee_amount( $params ) {
3531
+ pmpro_method_should_be_private( '2.7.0' );
3532
+ if ( empty( $params['amount'] ) || self::using_legacy_keys() ) {
3533
+ return $params;
3534
+ }
3535
+ $amount = $params['amount'];
3536
+ $application_fee = $amount * ( self::get_application_fee_percentage() / 100 );
3537
+ $application_fee = floor( $application_fee );
3538
+ if ( ! empty( $application_fee ) ) {
3539
+ $params['application_fee_amount'] = intval( $application_fee );
3540
+ }
3541
+ return $params;
3542
+ }
3543
+
3544
+ /**
3545
+ * Should we show the legacy key fields on the payment settings page.
3546
+ * We should if the site is using legacy keys already or
3547
+ * if a filter has been set.
3548
+ * @since 2.6
3549
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3550
+ */
3551
+ public static function show_legacy_keys_settings() {
3552
+ pmpro_method_should_be_private( '2.7.0' );
3553
+ $r = self::using_legacy_keys();
3554
+ $r = apply_filters( 'pmpro_stripe_show_legacy_keys_settings', $r );
3555
+ return $r;
3556
+ }
3557
+
3558
+ /**
3559
+ * Get the Stripe secret key based on gateway environment.
3560
+ *
3561
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3562
+ *
3563
+ * @return The Stripe secret key.
3564
+ */
3565
+ public static function get_secretkey() {
3566
+ pmpro_method_should_be_private( '2.7.0' );
3567
+ $secretkey = '';
3568
+ if ( self::using_legacy_keys() ) {
3569
+ $secretkey = pmpro_getOption( 'stripe_secretkey' );
3570
+ } else {
3571
+ $secretkey = pmpro_getOption( 'gateway_environment' ) === 'live'
3572
+ ? pmpro_getOption( 'live_stripe_connect_secretkey' )
3573
+ : pmpro_getOption( 'sandbox_stripe_connect_secretkey' );
3574
+ }
3575
+ return $secretkey;
3576
+ }
3577
+
3578
+ /**
3579
+ * Get the Stripe publishable key based on gateway environment.
3580
+ *
3581
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3582
+ *
3583
+ * @return The Stripe publishable key.
3584
+ */
3585
+ public static function get_publishablekey() {
3586
+ pmpro_method_should_be_private( '2.7.0' );
3587
+ $publishablekey = '';
3588
+ if ( self::using_legacy_keys() ) {
3589
+ $publishablekey = pmpro_getOption( 'stripe_publishablekey' );
3590
+ } else {
3591
+ $publishablekey = pmpro_getOption( 'gateway_environment' ) === 'live'
3592
+ ? pmpro_getOption( 'live_stripe_connect_publishablekey' )
3593
+ : pmpro_getOption( 'sandbox_stripe_connect_publishablekey' );
3594
+ }
3595
+ return $publishablekey;
3596
  }
3597
 
3598
  /**
3599
+ * Get the Stripe Connect User ID based on gateway environment.
3600
  *
3601
+ * @since 2.6.0
3602
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3603
  *
3604
+ * @return string The Stripe Connect User ID.
3605
  */
3606
+ public static function get_connect_user_id() {
3607
+ pmpro_method_should_be_private( '2.7.0' );
3608
+ return pmpro_getOption( 'gateway_environment' ) === 'live'
3609
+ ? pmpro_getOption( 'live_stripe_connect_user_id' )
3610
+ : pmpro_getOption( 'sandbox_stripe_connect_user_id' );
3611
  }
3612
 
3613
  /**
3614
+ * Determine whether the webhook is working by checking for Stripe orders with invalid transaction IDs.
3615
  *
3616
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
 
3617
  *
3618
+ * @param string|null $gateway_environment to check webhooks for. Defaults to set gateway environment.
3619
+ * @return bool Whether the webhook is working.
3620
  */
3621
+ public static function webhook_is_working( $gateway_environment = null ) {
3622
+ pmpro_method_should_be_private( '2.7.0' );
3623
+ global $wpdb;
 
 
3624
 
3625
+ if ( empty( $gateway_environment ) ) {
3626
+ $gateway_engvironemnt = pmpro_getOption( 'pmpro_gateway_environment' );
 
3627
  }
3628
 
3629
+ $last_webhook = get_option( 'pmpro_stripe_last_webhook_received_' . $gateway_environment );
 
 
3630
 
3631
+ if ( empty( $last_webhook ) ) {
3632
+ // Probably never got a webhook event.
3633
+ $last_webhook_safe = date( 'Y-m-d H:i:s', strtotime( '-5 years' ) );
3634
+ } else {
3635
+ // In case recurring order made after webhook event received.
3636
+ $last_webhook_safe = date( 'Y-m-d H:i:s', strtotime( $last_webhook . ' +5 minutes' ) );
3637
+ }
3638
+
3639
+ $hour_before_now = date( 'Y-m-d H:i:s', strtotime( '-1 hour' ) );
3640
+
3641
+ $num_problem_orders = $wpdb->get_var(
3642
+ $wpdb->prepare(
3643
+ "
3644
+ SELECT COUNT(*)
3645
+ FROM `{$wpdb->pmpro_membership_orders}`
3646
+ WHERE
3647
+ `gateway` = 'stripe'
3648
+ AND `gateway_environment` = %s
3649
+ AND `subscription_transaction_id` <> ''
3650
+ AND `subscription_transaction_id` IS NOT NULL
3651
+ AND `timestamp` > %s
3652
+ AND `timestamp` < %s
3653
+ ",
3654
+ $gateway_environment,
3655
+ $last_webhook_safe,
3656
+ $hour_before_now
3657
+ )
3658
+ );
3659
+
3660
+ return ( empty( $num_problem_orders ) );
3661
+ }
3662
+
3663
+ /**
3664
+ * Get the date the last webhook was processed.
3665
+ * @param environment The gateway environment (live or sandbox) to check for.
3666
+ * @returns HTML with the date of the last webhook or an error message.
3667
+ * @since 2.6
3668
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3669
+ */
3670
+ public static function get_last_webhook_date( $environment = 'live' ) {
3671
+ pmpro_method_should_be_private( '2.7.0' );
3672
+ $last_webhook = get_option( 'pmpro_stripe_last_webhook_received_' . $environment );
3673
+ if ( ! empty( $last_webhook ) ) {
3674
+ echo '<p>' . esc_html__( 'Last webhook received at', 'paid-memberships-pro' ) . ': ' . esc_html( $last_webhook ) . ' GMT.</p>';
3675
+ } else {
3676
+ echo '<p>' . esc_html__( 'No webhooks have been received.', 'paid-memberships-pro' ) . '</p>';
3677
  }
3678
+ if ( ! self::webhook_is_working( $environment ) ) {
3679
+ echo '<div class="notice error inline"><p>';
3680
+ echo esc_html__( 'Your webhook may not be working correctly.', 'paid-memberships-pro' );
3681
+ echo ' <a target="_blank" href="https://www.paidmembershipspro.com/gateway/stripe/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=gateways&utm_content=stripe-webhook#tab-gateway-setup">';
3682
+ echo esc_html__( 'Click here for info on setting up your webhook with Stripe.', 'paid-memberships-pro' );
3683
+ echo '</a>';
3684
+ echo '</p></div>';
3685
+ }
3686
+ }
3687
 
3688
+ /**
3689
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private in a future version.
3690
+ */
3691
+ public function get_payment_intent( &$order ) {
3692
+ pmpro_method_should_be_private( '2.7.0' );
3693
+ if ( ! empty( $order->payment_intent_id ) ) {
3694
+ try {
3695
+ $payment_intent = Stripe_PaymentIntent::retrieve( $order->payment_intent_id );
3696
+ } catch ( Stripe\Error\Base $e ) {
3697
+ $order->error = $e->getMessage();
3698
+ return false;
3699
+ } catch ( \Throwable $e ) {
3700
+ $order->error = $e->getMessage();
3701
+ return false;
3702
+ } catch ( \Exception $e ) {
3703
+ $order->error = $e->getMessage();
3704
+ return false;
3705
+ }
3706
  }
3707
 
3708
+ if ( empty( $payment_intent ) ) {
3709
+ $payment_intent = $this->create_payment_intent( $order );
3710
+ }
 
 
 
3711
 
3712
+ if ( empty( $payment_intent ) ) {
3713
  return false;
3714
  }
3715
 
3716
+ return $payment_intent;
3717
+ }
3718
+
3719
+ /**
3720
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private in a future version.
3721
+ */
3722
+ public function create_payment_intent( &$order ) {
3723
+ pmpro_method_should_be_private( '2.7.0' );
3724
+ global $pmpro_currency;
3725
+
3726
+ $amount = $order->InitialPayment;
3727
+ $order->subtotal = $amount;
3728
+ $tax = $order->getTax( true );
3729
+
3730
+ $amount = pmpro_round_price( (float) $order->subtotal + (float) $tax );
3731
+
3732
+ $params = array(
3733
+ 'customer' => $order->stripe_customer->id,
3734
+ 'payment_method' => $order->payment_method_id,
3735
+ 'amount' => $this->convert_price_to_unit_amount( $amount ),
3736
+ 'currency' => $pmpro_currency,
3737
+ 'confirmation_method' => 'manual',
3738
+ 'description' => apply_filters( 'pmpro_stripe_order_description', "Order #" . $order->code . ", " . trim( $order->FirstName . " " . $order->LastName ) . " (" . $order->Email . ")", $order ),
3739
+ 'setup_future_usage' => 'off_session',
3740
+ );
3741
+ $params = self::add_application_fee_amount( $params );
3742
+
3743
+ /**
3744
+ * Filter params used to create the payment intent.
3745
+ *
3746
+ * @since 2.4.1
3747
+ *
3748
+ * @param array $params Array of params sent to Stripe.
3749
+ * @param object $order Order object for this checkout.
3750
+ */
3751
+ $params = apply_filters( 'pmpro_stripe_payment_intent_params', $params, $order );
3752
+
3753
  try {
3754
+ $payment_intent = Stripe_PaymentIntent::create( $params );
3755
+ } catch ( Stripe\Error\Base $e ) {
3756
+ $order->error = $e->getMessage();
3757
+ return false;
3758
  } catch ( \Throwable $e ) {
3759
+ $order->error = $e->getMessage();
 
 
 
3760
  return false;
3761
  } catch ( \Exception $e ) {
3762
+ $order->error = $e->getMessage();
 
 
 
3763
  return false;
3764
  }
3765
 
3766
+ return $payment_intent;
3767
+ }
 
3768
 
3769
+ /**
3770
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
3771
+ *
3772
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private in a future version.
3773
+ *
3774
+ * @param MemberOrder $order The MemberOrder object.
3775
+ */
3776
+ public function process_subscriptions( &$order ) {
3777
+ pmpro_method_should_be_private( '2.7.0' );
3778
+ if ( ! pmpro_isLevelRecurring( $order->membership_level ) ) {
3779
  return true;
3780
+ }
 
 
 
 
3781
 
3782
+ //before subscribing, let's clear out the updates so we don't trigger any during sub
3783
+ if ( ! empty( $user_id ) ) {
3784
+ $old_user_updates = get_user_meta( $user_id, "pmpro_stripe_updates", true );
3785
+ update_user_meta( $user_id, "pmpro_stripe_updates", array() );
3786
  }
 
3787
 
3788
+ $setup_intent = $this->get_setup_intent( $order );
3789
+ if ( empty( $setup_intent ) ) {
3790
+ // There was an error, and the message should already
3791
+ // be saved on the order.
3792
+ return false;
3793
  }
3794
+ // Save setup intent to order so that we can use it in confirm_setup_intent().
3795
+ $order->stripe_setup_intent = $setup_intent;
3796
 
3797
+ $this->confirm_setup_intent( $order );
3798
+
3799
+ if ( ! empty( $order->error ) ) {
3800
+ $order->error = $order->error;
3801
+
3802
+ //give the user any old updates back
3803
+ if ( ! empty( $user_id ) ) {
3804
+ update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
3805
+ }
3806
 
 
3807
  return false;
3808
  }
3809
 
3810
+ //save new updates if this is at checkout
3811
+ //empty out updates unless set above
3812
+ if ( empty( $new_user_updates ) ) {
3813
+ $new_user_updates = array();
3814
+ }
3815
+
3816
+ //update user meta
3817
+ if ( ! empty( $user_id ) ) {
3818
+ update_user_meta( $user_id, "pmpro_stripe_updates", $new_user_updates );
3819
+ } else {
3820
+ //need to remember the user updates to save later
3821
+ global $pmpro_stripe_updates;
3822
+ $pmpro_stripe_updates = $new_user_updates;
3823
+
3824
+ if( ! function_exists( 'pmpro_user_register_stripe_updates' ) ) {
3825
+ function pmpro_user_register_stripe_updates( $user_id ) {
3826
+ global $pmpro_stripe_updates;
3827
+ update_user_meta( $user_id, 'pmpro_stripe_updates', $pmpro_stripe_updates );
3828
+ }
3829
+ add_action( 'user_register', 'pmpro_user_register_stripe_updates' );
3830
+ }
3831
+ }
3832
 
3833
  return true;
3834
  }
3835
 
3836
+ /**
3837
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
3838
+ *
3839
+ * @deprecated 2.7.0. Will only be deprecated once we create a function with better params.
3840
+ *
3841
+ * @param MemberOrder $order The MemberOrder object.
3842
+ */
3843
+ public function create_subscription( &$order ) {
3844
+ // _deprecated_function( __FUNCTION__, '2.7.0' );
3845
+ //subscribe to the plan
3846
+ try {
3847
+ $params = array(
3848
+ 'customer' => $order->stripe_customer->id,
3849
+ 'items' => array(
3850
+ array( 'plan' => $order->code ),
3851
+ ),
3852
+ 'trial_period_days' => $order->TrialPeriodDays,
3853
+ 'expand' => array(
3854
+ 'pending_setup_intent.payment_method',
3855
+ ),
3856
+ );
3857
+ if ( ! self::using_legacy_keys() ) {
3858
+ $params['application_fee_percent'] = self::get_application_fee_percentage();
3859
+ }
3860
+ $order->subscription = Stripe_Subscription::create( apply_filters( 'pmpro_stripe_create_subscription_array', $params ) );
3861
+ } catch ( Stripe\Error\Base $e ) {
3862
+ $order->error = $e->getMessage();
3863
+ return false;
3864
+ } catch ( \Throwable $e ) {
3865
+ $order->error = $e->getMessage();
3866
+ return false;
3867
+ } catch ( \Exception $e ) {
3868
+ $order->error = $e->getMessage();
3869
+ return false;
3870
+ }
3871
+
3872
+ return $order->subscription;
3873
 
3874
+ }
3875
+
3876
+ /**
3877
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
3878
+ *
3879
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3880
+ *
3881
+ * @param MemberOrder $order The MemberOrder object.
3882
+ */
3883
+ public function delete_plan( &$order ) {
3884
+ // _deprecated_function( __FUNCTION__, '2.7.0' );
3885
+ try {
3886
+ // Delete the product first while we have a reference to it...
3887
+ if ( ( ! empty( $order->plan->product ) ) && ( ! $this->archive_product( $order ) ) ) {
3888
  return false;
3889
  }
3890
+ // Then delete the plan.
3891
+ $order->plan->delete();
3892
+ } catch ( Stripe\Error\Base $e ) {
3893
+ $order->error = $e->getMessage();
3894
+
3895
+ return false;
3896
+ } catch ( \Throwable $e ) {
3897
+ $order->error = $e->getMessage();
3898
+
3899
+ return false;
3900
+ } catch ( \Exception $e ) {
3901
+ $order->error = $e->getMessage();
3902
+
3903
+ return false;
3904
  }
3905
 
3906
+ return true;
3907
+ }
3908
+
3909
+ /**
3910
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
3911
+ *
3912
+ * @deprecated 2.7.0. Only deprecated for public use, will be changed to private non-static in a future version.
3913
+ *
3914
+ * @param MemberOrder $order The MemberOrder object.
3915
+ */
3916
+ public function archive_product( &$order ) {
3917
+ // _deprecated_function( __FUNCTION__, '2.7.0' );
3918
+ try {
3919
+ $product = Stripe_Product::update( $order->plan->product, array( 'active' => false ) );
3920
+ } catch ( Stripe\Error\Base $e ) {
3921
+ $order->error = $e->getMessage();
3922
+ return false;
3923
+ } catch ( \Throwable $e ) {
3924
+ $order->error = $e->getMessage();
3925
+ return false;
3926
+ } catch ( \Exception $e ) {
3927
+ $order->error = $e->getMessage();
3928
  return false;
3929
  }
3930
 
3931
+ return true;
3932
  }
3933
 
3934
+ /****************************************
3935
+ ********** DEPRECATED METHODS **********
3936
+ ****************************************/
3937
+ /**
3938
+ * Make a one-time charge with Stripe
3939
+ *
3940
+ * @since 1.4
3941
+ * @deprecated 2.7.0. Use process_charges() instead.
3942
+ */
3943
+ public function charge( &$order ) {
3944
+ _deprecated_function( __FUNCTION__, '2.7.0' );
3945
+ global $pmpro_currency;
3946
+
3947
+ //create a code for the order
3948
+ if ( empty( $order->code ) ) {
3949
+ $order->code = $order->getRandomCode();
3950
  }
 
 
3951
 
3952
+ //what amount to charge?
3953
+ $amount = $order->InitialPayment;
3954
 
3955
+ //tax
3956
+ $order->subtotal = $amount;
3957
+ $tax = $order->getTax( true );
3958
+ $amount = pmpro_round_price( (float) $order->subtotal + (float) $tax );
3959
+
3960
+ //create a customer
3961
+ $customer = $this->update_customer_at_checkout( $order );
3962
+ if ( empty( $customer ) ) {
3963
+ //failed to create customer
3964
+ return false;
3965
  }
3966
 
3967
+ //charge
3968
  try {
3969
+ $params = array(
3970
+ "amount" => $this->convert_price_to_unit_amount( $amount ), # amount in cents, again
3971
+ "currency" => strtolower( $pmpro_currency ),
3972
+ "customer" => $customer->id,
3973
+ "description" => apply_filters( 'pmpro_stripe_order_description', "Order #" . $order->code . ", " . trim( $order->FirstName . " " . $order->LastName ) . " (" . $order->Email . ")", $order )
3974
+ );
3975
+ $params = self::add_application_fee_amount( $params );
3976
+ /**
3977
+ * Filter params used to create the Stripe charge.
3978
+ *
3979
+ * @since 2.4.4
3980
+ *
3981
+ * @param array $params Array of params sent to Stripe.
3982
+ * @param object $order Order object for this checkout.
3983
+ */
3984
+ $params = apply_filters( 'pmpro_stripe_charge_params', $params, $order );
3985
+ $response = Stripe_Charge::create( $params );
3986
  } catch ( \Throwable $e ) {
3987
+ //$order->status = "error";
3988
+ $order->errorcode = true;
3989
+ $order->error = "Error: " . $e->getMessage();
3990
+ $order->shorterror = $order->error;
3991
+
3992
  return false;
3993
  } catch ( \Exception $e ) {
3994
+ //$order->status = "error";
3995
+ $order->errorcode = true;
3996
+ $order->error = "Error: " . $e->getMessage();
3997
+ $order->shorterror = $order->error;
3998
+
3999
  return false;
4000
  }
4001
 
4002
+ if ( empty( $response["failure_message"] ) ) {
4003
+ //successful charge
4004
+ $order->payment_transaction_id = $response["id"];
4005
+ $order->updateStatus( "success" );
4006
+ $order->saveOrder();
4007
 
 
4008
  return true;
4009
+ } else {
4010
+ //$order->status = "error";
4011
+ $order->errorcode = true;
4012
+ $order->error = $response['failure_message'];
4013
+ $order->shorterror = $response['failure_message'];
 
 
4014
 
4015
  return false;
4016
  }
4017
+ }
4018
 
4019
+ /**
4020
+ * Get a Stripe Customer object and update it.
4021
+ *
4022
+ * @since 1.4
4023
+ * @deprecated 2.7.0. Use get_customer_for_user() or update_customer_from_user().
4024
+ *
4025
+ * @return Stripe_Customer|false
4026
+ */
4027
+ public function getCustomer( &$order = false, $force = false ) {
4028
+ _deprecated_function( __FUNCTION__, '2.7.0', 'update_customer_from_user()' );
4029
+ return $this->update_customer_at_checkout( $order );
4030
  }
4031
 
4032
+ /**
4033
+ * Get a Stripe subscription from a PMPro order
4034
+ *
4035
+ * @since 1.8
4036
+ * @deprecated 2.7.0. Need to write replacement methods for this.
4037
+ */
4038
+ public function getSubscription( &$order ) {
4039
+ _deprecated_function( __FUNCTION__, '2.7.0' );
4040
+ global $wpdb;
4041
 
4042
+ //no order?
4043
+ if ( empty( $order ) || empty( $order->code ) ) {
4044
+ return false;
4045
  }
4046
 
4047
+ $customer = $this->update_customer_at_checkout( $order, true ); //force so we don't get a cached sub for someone else
4048
 
4049
+ //no customer?
4050
+ if ( empty( $customer ) ) {
4051
  return false;
4052
  }
4053
 
4054
+ //no subscriptions?
4055
+ if ( empty( $customer->subscriptions ) ) {
4056
+ return false;
4057
+ }
 
 
4058
 
4059
+ //is there a subscription transaction id pointing to a sub?
4060
+ if ( ! empty( $order->subscription_transaction_id ) && strpos( $order->subscription_transaction_id, "sub_" ) !== false ) {
4061
  try {
4062
+ $sub = $customer->subscriptions->retrieve( $order->subscription_transaction_id );
 
 
 
4063
  } catch ( \Throwable $e ) {
4064
+ $order->error = __( "Error getting subscription with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
4065
+ $order->shorterror = $order->error;
4066
+
4067
  return false;
4068
  } catch ( \Exception $e ) {
4069
+ $order->error = __( "Error getting subscription with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
4070
+ $order->shorterror = $order->error;
4071
+
4072
  return false;
4073
  }
 
4074
 
4075
+ return $sub;
 
4076
  }
4077
 
4078
+ //find subscription based on customer id and order/plan id
4079
+ $subscriptions = $customer->subscriptions->all();
4080
+
4081
+ //no subscriptions
4082
+ if ( empty( $subscriptions ) || empty( $subscriptions->data ) ) {
4083
  return false;
4084
  }
4085
 
4086
+ //we really want to test against the order codes of all orders with the same subscription_transaction_id (customer id)
4087
+ $codes = $wpdb->get_col( "SELECT code FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . esc_sql( $order->user_id ) . "' AND subscription_transaction_id = '" . esc_sql( $order->subscription_transaction_id ) . "' AND status NOT IN('refunded', 'review', 'token', 'error')" );
4088
+
4089
+ //find the one for this order
4090
+ foreach ( $subscriptions->data as $sub ) {
4091
+ if ( in_array( $sub->plan->id, $codes ) ) {
4092
+ return $sub;
4093
+ }
4094
+ }
4095
+
4096
+ //didn't find anything yet
4097
+ return false;
4098
  }
4099
 
4100
+ /**
4101
+ * Create a new subscription with Stripe.
4102
+ *
4103
+ * This function is not run as a part of the PMPro Checkout Process.
4104
+ * See method create_setup_intent().
4105
+ *
4106
+ * @since 1.4
4107
+ * @deprecated 2.7.0. Use process_subscriptions() instead.
4108
+ */
4109
+ public function subscribe( &$order, $checkout = true ) {
4110
+ _deprecated_function( __FUNCTION__, '2.7.0' );
4111
+ global $pmpro_currency;
4112
 
4113
+ //create a code for the order
4114
+ if ( empty( $order->code ) ) {
4115
+ $order->code = $order->getRandomCode();
4116
+ }
4117
 
4118
+ //filter order before subscription. use with care.
4119
+ $order = apply_filters( "pmpro_subscribe_order", $order, $this );
4120
+
4121
+ //figure out the user
4122
+ if ( ! empty( $order->user_id ) ) {
4123
+ $user_id = $order->user_id;
4124
+ } else {
4125
+ global $current_user;
4126
+ $user_id = $current_user->ID;
4127
  }
4128
 
4129
+ //set up customer
 
 
4130
 
4131
+ $result = $this->update_customer_at_checkout( $order );
4132
+ if ( empty( $result ) ) {
4133
+ return false; //error retrieving customer
4134
+ }
4135
+ $order->stripe_customer = $result;
4136
 
4137
+ // set subscription id to custom id
 
 
 
 
 
 
 
 
4138
 
4139
+ $order->subscription_transaction_id = $order->stripe_customer['id']; //transaction id is the customer id, we save it in user meta later too
4140
+
4141
+ //figure out the amounts
4142
+ $amount = $order->PaymentAmount;
4143
+ $amount_tax = $order->getTaxForPrice( $amount );
4144
+ $amount = pmpro_round_price( (float) $amount + (float) $amount_tax );
4145
+
4146
+ $trial_period_days = $this->calculate_trial_period_days( $order );
4147
+
4148
+ // Save $trial_period_days to order for now too.
4149
+ $order->TrialPeriodDays = $trial_period_days;
4150
 
4151
+ //create a plan
4152
  try {
4153
+ $plan = array(
4154
+ "amount" => $this->convert_price_to_unit_amount( $amount ),
4155
+ "interval_count" => $order->BillingFrequency,
4156
+ "interval" => strtolower( $order->BillingPeriod ),
4157
+ "trial_period_days" => $trial_period_days,
4158
+ 'product' => array( 'name' => $order->membership_name . " for order " . $order->code ),
4159
+ "currency" => strtolower( $pmpro_currency ),
4160
+ "id" => $order->code
4161
+ );
4162
+ $plan = self::add_application_fee_amount( $plan );
4163
+ $plan = Stripe_Plan::create( apply_filters( 'pmpro_stripe_create_plan_array', $plan ) );
4164
  } catch ( \Throwable $e ) {
4165
+ $order->error = __( "Error creating plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
4166
+ $order->shorterror = $order->error;
4167
+
4168
  return false;
4169
  } catch ( \Exception $e ) {
4170
+ $order->error = __( "Error creating plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
4171
+ $order->shorterror = $order->error;
 
 
 
 
 
 
4172
 
4173
+ return false;
 
4174
  }
4175
 
4176
+ // before subscribing, let's clear out the updates so we don't trigger any during sub
4177
  if ( ! empty( $user_id ) ) {
4178
  $old_user_updates = get_user_meta( $user_id, "pmpro_stripe_updates", true );
4179
  update_user_meta( $user_id, "pmpro_stripe_updates", array() );
4180
  }
4181
 
 
 
4182
 
4183
+ if ( empty( $order->subscription_transaction_id ) && ! empty( $order->stripe_customer['id'] ) ) {
4184
+ $order->subscription_transaction_id = $order->stripe_customer['id'];
4185
+ }
4186
+
4187
+ // subscribe to the plan
4188
+ try {
4189
+ $subscription = array( "plan" => $order->code );
4190
+ $result = $this->create_subscription( $order );
4191
+ } catch ( \Throwable $e ) {
4192
+ //try to delete the plan
4193
+ $plan->delete();
4194
 
4195
  //give the user any old updates back
4196
  if ( ! empty( $user_id ) ) {
4197
  update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
4198
  }
4199
 
4200
+ //return error
4201
+ $order->error = __( "Error subscribing customer to plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
4202
+ $order->shorterror = $order->error;
4203
+
4204
  return false;
4205
+ } catch ( \Exception $e ) {
4206
+ //try to delete the plan
4207
+ $plan->delete();
4208
 
4209
+ //give the user any old updates back
4210
+ if ( ! empty( $user_id ) ) {
4211
+ update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
4212
+ }
4213
+
4214
+ //return error
4215
+ $order->error = __( "Error subscribing customer to plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
4216
+ $order->shorterror = $order->error;
4217
+
4218
+ return false;
4219
  }
4220
 
4221
+ // delete the plan
4222
+ $plan = Stripe_Plan::retrieve( $order->code );
4223
+ $plan->delete();
4224
+
4225
+ //if we got this far, we're all good
4226
+ $order->status = "success";
4227
+ $order->subscription_transaction_id = $result['id'];
4228
+
4229
+ //save new updates if this is at checkout
4230
+ if ( $checkout ) {
4231
+ //empty out updates unless set above
4232
+ if ( empty( $new_user_updates ) ) {
4233
+ $new_user_updates = array();
4234
+ }
4235
+
4236
+ //update user meta
4237
+ if ( ! empty( $user_id ) ) {
4238
+ update_user_meta( $user_id, "pmpro_stripe_updates", $new_user_updates );
4239
+ } else {
4240
+ //need to remember the user updates to save later
4241
+ global $pmpro_stripe_updates;
4242
+ $pmpro_stripe_updates = $new_user_updates;
4243
  function pmpro_user_register_stripe_updates( $user_id ) {
4244
  global $pmpro_stripe_updates;
4245
+ update_user_meta( $user_id, "pmpro_stripe_updates", $pmpro_stripe_updates );
4246
  }
4247
+
4248
+ add_action( "user_register", "pmpro_user_register_stripe_updates" );
4249
  }
4250
+ } else {
4251
+ //give them their old updates back
4252
+ update_user_meta( $user_id, "pmpro_stripe_updates", $old_user_updates );
4253
  }
4254
 
4255
  return true;
4256
  }
4257
 
4258
+ /**
4259
+ * Refund a payment or invoice
4260
+ *
4261
+ * @deprecated 2.7.0.
4262
+ *
4263
+ * @param object &$order Related PMPro order object.
4264
+ * @param string $transaction_id Payment or Invoice id to void.
4265
+ *
4266
+ * @return bool True or false if the void worked
4267
+ */
4268
+ public function void( &$order, $transaction_id = null ) {
4269
+ _deprecated_function( __FUNCTION__, '2.7.0' );
4270
+ //stripe doesn't differentiate between voids and refunds, so let's just pass on to the refund function
4271
+ return $this->refund( $order, $transaction_id );
4272
+ }
4273
 
4274
+ /**
4275
+ * Refund a payment or invoice
4276
+ *
4277
+ * @deprecated 2.7.0.
4278
+ *
4279
+ * @param object &$order Related PMPro order object.
4280
+ * @param string $transaction_id Payment or invoice id to void.
4281
+ *
4282
+ * @return bool True or false if the refund worked.
4283
+ */
4284
+ public function refund( &$order, $transaction_id = null ) {
4285
+ _deprecated_function( __FUNCTION__, '2.7.0' );
4286
+ //default to using the payment id from the order
4287
+ if ( empty( $transaction_id ) && ! empty( $order->payment_transaction_id ) ) {
4288
+ $transaction_id = $order->payment_transaction_id;
4289
  }
4290
 
4291
+ //need a transaction id
4292
+ if ( empty( $transaction_id ) ) {
4293
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4294
  }
4295
 
4296
+ //if an invoice ID is passed, get the charge/payment id
4297
+ if ( strpos( $transaction_id, "in_" ) !== false ) {
4298
+ $invoice = Stripe_Invoice::retrieve( $transaction_id );
 
 
 
 
 
4299
 
4300
+ if ( ! empty( $invoice ) && ! empty( $invoice->charge ) ) {
4301
+ $transaction_id = $invoice->charge;
 
 
 
 
 
 
 
 
 
4302
  }
 
 
4303
  }
4304
 
4305
+ //get the charge
 
 
 
4306
  try {
4307
+ $charge = Stripe_Charge::retrieve( $transaction_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
4308
  } catch ( \Throwable $e ) {
4309
+ $charge = false;
 
 
4310
  } catch ( \Exception $e ) {
4311
+ $charge = false;
 
 
4312
  }
4313
 
4314
+ //can't find the charge?
4315
+ if ( empty( $charge ) ) {
4316
+ $order->status = "error";
4317
+ $order->errorcode = "";
4318
+ $order->error = "";
4319
+ $order->shorterror = "";
4320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4321
  return false;
4322
  }
4323
 
4324
+ //attempt refund
 
 
 
 
4325
  try {
4326
+ $refund = $charge->refund();
 
 
 
 
4327
  } catch ( \Throwable $e ) {
4328
+ $order->errorcode = true;
4329
+ $order->error = __( "Error: ", 'paid-memberships-pro' ) . $e->getMessage();
4330
+ $order->shorterror = $order->error;
4331
 
4332
  return false;
4333
  } catch ( \Exception $e ) {
4334
+ $order->errorcode = true;
4335
+ $order->error = __( "Error: ", 'paid-memberships-pro' ) . $e->getMessage();
4336
+ $order->shorterror = $order->error;
4337
 
4338
  return false;
4339
  }
4340
 
4341
+ if ( $refund->status == "succeeded" ) {
4342
+ $order->status = "refunded";
4343
+ $order->saveOrder();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4344
 
4345
+ return true;
4346
+ } else {
4347
+ $order->status = "error";
4348
+ $order->errorcode = true;
4349
+ $order->error = sprintf( __( "Error: Unkown error while refunding charge #%s", 'paid-memberships-pro' ), $transaction_id );
4350
+ $order->shorterror = $order->error;
4351
 
 
4352
  return false;
4353
  }
 
 
4354
  }
4355
 
4356
+ /**
4357
+ * @deprecated 2.7.0. Use get_payment_method() instead.
4358
+ */
4359
+ public function set_payment_method( &$order, $force = false ) {
4360
+ _deprecated_function( __FUNCTION__, '2.7.0', 'get_payment_method' );
4361
+ if ( ! empty( $this->payment_method ) && ! $force ) {
4362
  return true;
4363
  }
4364
 
4365
+ $payment_method = $this->get_payment_method( $order );
4366
 
4367
+ if ( empty( $payment_method ) ) {
4368
  return false;
4369
  }
4370
 
4371
+ $this->payment_method = $payment_method;
4372
 
4373
  return true;
4374
  }
4375
 
4376
+ /**
4377
+ * @deprecated 2.7.0. Use get_customer_for_user() or update_customer_from_user().
4378
+ */
4379
+ public function set_customer( &$order, $force = false ) {
4380
+ _deprecated_function( __FUNCTION__, '2.7.0', 'get_customer_for_user()' );
4381
+ if ( ! empty( $this->customer ) && ! $force ) {
4382
+ return true;
 
4383
  }
4384
+ // Temporarily setting this, will be removed when this function is deprecated.
4385
+ $this->customer = $this->update_customer_at_checkout( $order );
4386
+ return $this->customer;
4387
  }
4388
 
4389
+ /**
4390
+ * @deprecated 2.7.0. Use set_default_payment_method_for_customer().
4391
+ */
4392
+ public function attach_payment_method_to_customer( &$order ) {
4393
+ _deprecated_function( __FUNCTION__, '2.7.0', 'set_default_payment_method_for_customer()' );
4394
+ $customer = $this->update_customer_at_checkout( $order );
4395
+
4396
+ if ( ! empty( $customer->invoice_settings->default_payment_method ) &&
4397
+ $customer->invoice_settings->default_payment_method === $this->payment_method->id ) {
4398
+ return true;
4399
+ }
4400
 
4401
  try {
4402
+ $this->payment_method->attach( [ 'customer' => $customer->id ] );
4403
+ $customer->invoice_settings->default_payment_method = $this->payment_method->id;
4404
+ $customer->save();
 
 
 
4405
  } catch ( Stripe\Error\Base $e ) {
4406
  $order->error = $e->getMessage();
4407
  return false;
4413
  return false;
4414
  }
4415
 
4416
+ return true;
4417
+ }
4418
+
4419
+ /**
4420
+ * @deprecated 2.7.0. Use get_payment_intent() instead.
4421
+ */
4422
+ public function set_payment_intent( &$order, $force = false ) {
4423
+ _deprecated_function( __FUNCTION__, '2.7.0', 'get_payment_intent()' );
4424
+ if ( ! empty( $order->stripe_payment_intent ) && ! $force ) {
4425
+ return true;
4426
+ }
4427
+
4428
+ $payment_intent = $this->get_payment_intent( $order );
4429
 
4430
+ if ( empty( $payment_intent ) ) {
4431
  return false;
4432
  }
4433
 
4434
+ $this->payment_intent = $payment_intent;
4435
+
4436
  return true;
4437
  }
4438
 
4439
+ /**
4440
+ * Only called during subscription updates. Should be completely deprecated once that functionality is removed.
4441
+ *
4442
+ * @deprecated 2.7.0. Will only be deprecated once we are using Prices.
4443
+ */
4444
+ public function create_plan( &$order ) {
4445
+ // _deprecated_function( __FUNCTION__, '2.7.0' );
4446
+ global $pmpro_currency;
4447
+
4448
+ //figure out the amounts
4449
+ $amount = $order->PaymentAmount;
4450
+ $amount_tax = $order->getTaxForPrice( $amount );
4451
+ $amount = pmpro_round_price( (float) $amount + (float) $amount_tax );
4452
 
4453
+
4454
+ $trial_period_days = $this->calculate_trial_period_days( $order );
4455
+ // Save $trial_period_days to order for now too.
4456
+ $order->TrialPeriodDays = $trial_period_days;
4457
 
4458
+ //create a plan
4459
+ try {
4460
+ $plan = array(
4461
+ "amount" => $this->convert_price_to_unit_amount( $amount ),
4462
+ "interval_count" => $order->BillingFrequency,
4463
+ "interval" => strtolower( $order->BillingPeriod ),
4464
+ "trial_period_days" => $trial_period_days,
4465
+ 'product' => array( 'name' => $order->membership_name . " for order " . $order->code ),
4466
+ "currency" => strtolower( $pmpro_currency ),
4467
+ "id" => $order->code
4468
+ );
4469
+ $order->plan = Stripe_Plan::create( apply_filters( 'pmpro_stripe_create_plan_array', $plan ) );
4470
+ } catch ( Stripe\Error\Base $e ) {
4471
+ $order->error = $e->getMessage();
4472
 
4473
  return false;
4474
+ } catch ( \Throwable $e ) {
4475
+ $order->error = $e->getMessage();
4476
 
4477
+ return false;
4478
+ } catch ( \Exception $e ) {
4479
+ $order->error = $e->getMessage();
4480
 
4481
+ return false;
 
 
4482
  }
4483
 
4484
+ return $order->plan;
 
 
4485
  }
4486
+
4487
 
4488
  }
classes/gateways/class.pmprogateway_twocheckout.php CHANGED
@@ -129,7 +129,7 @@
129
  <label for="twocheckout_apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
130
  </th>
131
  <td>
132
- <input type="text" id="twocheckout_apipassword" name="twocheckout_apipassword" value="<?php echo esc_attr($values['twocheckout_apipassword'])?>" class="regular-text code" />
133
  <p class="description"><?php esc_html_e( 'Password for the API user created.', 'paid-memberships-pro' ); ?></p>
134
  </td>
135
  </tr>
@@ -217,7 +217,7 @@
217
  */
218
  static function pmpro_checkout_before_change_membership_level($user_id, $morder)
219
  {
220
- global $wpdb, $discount_code_id;
221
 
222
  //if no order, no need to pay
223
  if(empty($morder))
@@ -227,9 +227,11 @@
227
  $morder->saveOrder();
228
 
229
  //save discount code use
230
- if(!empty($discount_code_id))
 
 
231
  $wpdb->query("INSERT INTO $wpdb->pmpro_discount_codes_uses (code_id, user_id, order_id, timestamp) VALUES('" . $discount_code_id . "', '" . $user_id . "', '" . $morder->id . "', now())");
232
-
233
  do_action("pmpro_before_send_to_twocheckout", $user_id, $morder);
234
 
235
  $morder->Gateway->sendToTwocheckout($morder);
129
  <label for="twocheckout_apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label>
130
  </th>
131
  <td>
132
+ <input type="text" id="twocheckout_apipassword" name="twocheckout_apipassword" value="<?php echo esc_attr($values['twocheckout_apipassword'])?>" autocomplete="off" class="regular-text code pmpro-admin-secure-key" />
133
  <p class="description"><?php esc_html_e( 'Password for the API user created.', 'paid-memberships-pro' ); ?></p>
134
  </td>
135
  </tr>
217
  */
218
  static function pmpro_checkout_before_change_membership_level($user_id, $morder)
219
  {
220
+ global $wpdb;
221
 
222
  //if no order, no need to pay
223
  if(empty($morder))
227
  $morder->saveOrder();
228
 
229
  //save discount code use
230
+ if(isset($morder->membership_level) && !empty($morder->membership_level->code_id))
231
+ {
232
+ $discount_code_id = (int)$morder->membership_level->code_id;
233
  $wpdb->query("INSERT INTO $wpdb->pmpro_discount_codes_uses (code_id, user_id, order_id, timestamp) VALUES('" . $discount_code_id . "', '" . $user_id . "', '" . $morder->id . "', now())");
234
+ }
235
  do_action("pmpro_before_send_to_twocheckout", $user_id, $morder);
236
 
237
  $morder->Gateway->sendToTwocheckout($morder);
css/admin.css CHANGED
@@ -1,485 +1,847 @@
 
 
 
 
 
 
 
 
1
  /* icon */
2
  #wp-admin-bar-paid-memberships-pro .ab-item .ab-icon:before {
3
- font-family: "dashicons";
4
- content: "\f307";
 
 
 
5
  }
6
- .pmpro_admin tr td .dashicons {padding-top: 5px; }
7
 
8
  /* header/etc */
9
  .pmpro_admin {
10
- background-image: url(../images/Paid-Memberships-Pro_watermark.png);
11
- background-position: bottom right;
12
- background-repeat: no-repeat;
13
- background-size: 290px 40px;
14
- padding: 1em 0 70px 0;
15
  }
16
  .pmpro_admin .pmpro_banner {
17
- display: grid;
18
- grid-template-areas: "logo meta";
19
- grid-template-columns: 350px auto;
20
  }
21
  .pmpro_admin .pmpro_banner .pmpro_logo {
22
- grid-area: logo;
23
  }
24
  .pmpro_admin .pmpro_banner .pmpro_meta {
25
- align-self: center;
26
- grid-area: meta;
27
- font-size: 16px;
28
- line-height: 1.5;
29
  }
30
  .pmpro_admin .pmpro_banner .pmpro_meta .pmpro_version {
31
- color: #77a02e;
32
- display: inline-block;
33
- font-weight: bold;
34
- padding: 5px 10px 5px 2px;
35
  }
36
  .pmpro_admin .pmpro_banner .pmpro_meta a {
37
- border-left: 1px solid #CCC;
38
- padding: 5px 10px;
39
  }
40
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag {
41
- font-weight: bold;
42
- padding: 5px 10px 5px 5px;
43
- text-decoration: none;
44
  }
45
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag:before {
46
- bottom: 7px;
47
- display: inline-block;
48
- font: 400 20px/1 dashicons;
49
- left: 0;
50
- position: relative;
51
- text-decoration: none;
52
- vertical-align: bottom;
53
  }
54
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-valid {
55
- color: rgb( 70, 180, 80 );
56
  }
57
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-valid:before {
58
- content: "\f147";
59
  }
60
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-invalid {
61
- color: #AAA;
62
  }
63
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-invalid:before {
64
- content: "\f335";
65
  }
66
 
67
  .pmpro_admin .pmpro_wp-notice-fix {
68
- height: 1px;
69
- line-height: 1px;
70
- margin: 0;
 
 
 
 
 
 
 
 
 
71
  }
72
- .pmpro_admin .topborder {border-top: 1px solid #CCC; margin-top: 1em; padding-top: 1em; }
73
- .pmpro_admin #editorcontainer #description {width: 100%; height: 180px; }
74
-
75
  .pmpro_admin .pmpro_icon {
76
- max-width: 200px;
77
- height: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
 
80
- /* checkboxes */
81
- .checkbox_box {width: 300px; background: #FFFFFF; border: 1px solid #CCC;}
82
- .checkbox_box div {border-bottom: 1px solid #CCC; padding: 3px;}
83
- .checkbox_box .clickable {cursor: pointer;}
84
- .checkbox_box .clickable:hover {background: #FFC;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
- /* consent log */
87
  .pmpro_consent_log.pmpro_scrollable {
88
- padding: .5em;
89
- background: #FFF;
90
- border: 1px solid #CCC;
91
- height: 200px;
92
  max-width: 800px;
93
- overflow: auto;
 
 
 
 
 
 
 
 
 
 
 
 
94
  }
95
 
96
  /* levels */
97
- .memberships_page_pmpro-membershiplevels .pmpro_admin #posts-filter p.search-box { margin: 2em 0 1em 0; }
98
- .memberships_page_pmpro-membershiplevels .pmpro_admin tr.pmpro_gray td {color: #AAA;}
99
- .memberships_page_pmpro-membershiplevels .pmpro_admin tr td.level_name span.level-name a {font-size: 115%; font-weight: bold; }
100
- .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr {background: #fff;}
101
- .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr.alternate {background: #f1f1f1;}
 
 
 
 
 
 
 
 
 
 
 
102
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr.ui-sortable-handle {
103
- border: 1px solid #2997C8;
104
- cursor: move;
105
  }
106
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr th:first-child:before,
107
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr td:first-child:before {
108
- color: #CCC;
109
- content: "\f333";
110
- display: inline-block;
111
- font: 400 16px/1 dashicons;
112
- opacity: 0;
113
- width: 25px;
114
  }
115
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr:hover td:first-child:before {
116
- opacity: 1;
 
 
 
 
 
 
 
117
  }
118
- .memberships_page_pmpro-membershiplevels .pmpro_admin tr.testclass {border: 3px solid #2997C8; background: #2997C8;}
119
- .memberships_page_pmpro-membershiplevels .pmpro_admin tr.membership_categories ul {margin-left: 25px;}
120
 
121
  /* payment settings */
122
  .admin_page_pmpro-paymentsettings .form-table select {
123
- max-width: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  }
125
 
126
  /* advanced settings */
127
  .admin_page_pmpro-advancedsettings .form-table select:not(.admin_page_pmpro-advancedsettings .form-table select#tospage) {
128
- max-width: none;
129
  }
130
 
131
  /* orders */
132
  .memberships_page_pmpro-orders .pmpro_admin .nav-tab-wrapper {
133
- margin-bottom: 10px;
134
  }
135
 
136
  /* members list */
137
  .wp-list-table.members .column-ID {
138
- width: 5%;
139
  }
140
- .wp-list-table.members .column-username {
141
- width: 15%;
142
  }
143
 
144
  /* settings */
145
  tr.pmpro_settings_divider td {
146
- padding: 0;
147
- margin: 0;
 
 
 
 
 
 
 
 
148
  }
149
 
150
  /* messages */
151
  .pmpro_admin .pmpro_message {
152
- background: #FFF;
153
- border-left: 4px solid #FFF;
154
- margin-right: 15px;
155
- padding: 15px;
156
  }
157
  .pmpro_admin .pmpro_success {
158
- background-color: rgba( 70, 180, 80, 0.1 );
159
- border-left-color: rgb( 70, 180, 80 );
160
  }
161
  .pmpro_admin .pmpro_error,
162
  .pmpro_admin tr.pmpro_error td {
163
- background-color: rgba( 220, 50, 50, 0.1 );
164
- border-left-color: rgb( 220, 50, 50 );
165
  }
166
  .pmpro_admin .pmpro_alert {
167
- background-color: rgba( 255, 185, 0, 0.1 );
168
- border-left-color: rgb( 255, 185, 0 );
169
  }
170
  .pmpro_admin .pmpro_success a {
171
- color: #208A1B;
172
  }
173
  .pmpro_admin .pmpro_error a,
174
  .pmpro_admin tr.pmpro_error td {
175
- color: #CC0000;
176
  }
177
  .pmpro_admin .pmpro_alert a {
178
- color: #CF8516;
179
  }
180
 
181
  /* notifications */
182
  #pmpro_notifications .pmpro_notification {
183
- position: relative;
184
  }
185
  #pmpro_notifications .pmpro_notification .pmpro_notification-general {
186
- background: #FFF;
187
- border-left: 4px solid #77A02E;
188
- box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
189
- margin: 1em 0;
190
- padding: 15px;
191
  }
192
  #pmpro_notifications .pmpro_notification .pmpro_notification-error {
193
- background: #FFF;
194
- border-left: 4px solid #DC3232;
195
- box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
196
- margin: 1em 0;
197
- padding: 15px;
198
  }
199
  #pmpro_notifications .pmpro_notification h3 {
200
- font-size: 24px;
201
- font-weight: 400;
202
- margin: 0;
203
  }
204
  #pmpro_notifications .pmpro_notification p {
205
- margin: 1em 0 0 0;
206
  }
207
  #pmpro_notifications .pmpro_notification .button {
208
- margin-right: 5px;
209
  }
210
  #pmpro_notifications .pmpro_notification .pmpro_notification-general h3 {
211
- color: #77A02E;
212
  }
213
  #pmpro_notifications .pmpro_notification .pmpro_notification-error h3 {
214
- color: #DC3232;
215
  }
216
 
217
  /* highlighted trs */
218
- tr.pmpro_message {background-image: none;}
219
- tr.pmpro_success {background-image: none;}
220
- tr.pmpro_error {background-image: none;}
221
- tr.pmpro_alert {background-image: none;}
 
 
 
 
 
 
 
 
222
 
223
  /* discount levels */
224
  .pmpro_discount_levels .pmpro_discount_level {
225
- border: 1px solid #CCC;
226
- margin: 15px 0;
227
- padding: 10px;
228
  }
229
  .pmpro_discount_levels .pmpro_discount_level .pmpro_discount_levels_pricing {
230
- background: #FFFFFF;
231
- border: 1px solid #CCC;
232
- margin: 10px 0 0 0;
233
- padding: 0px 20px;
234
  }
235
 
236
  /* pagination */
237
- div.pmpro_pagination {padding: 3px; margin: 5px 0px 5px 0px; font-size: 10px; float: right; }
238
- div.pmpro_pagination a {padding: 2px 5px 2px 5px; margin: 1px; border: 1px solid #666; text-decoration: none; /* no underline */ color: #666; background: #EEE; }
239
- div.pmpro_pagination a:hover, div.pmpro_pagination a:active {background: #FFF; }
240
- div.pmpro_pagination span.current {border: 1px solid #FFF; color: #FFF; background: #666; padding: 2px 5px 2px 5px; margin: 1px; font-weight: bold; }
241
- div.pmpro_pagination span.disabled {padding: 2px 5px 2px 5px; margin: 2px; border: 1px solid #BBB; color: #BBB; background: #EFEFEF;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
- p.pmpro_meta_notice {font-size: .8em; padding-top: 5px; border-top: 1px solid #CCC;}
 
 
 
 
244
 
245
  /* add ons */
246
  .memberships_page_pmpro-addons .pmpro_admin .nav-tab-wrapper {
247
- margin-bottom: 10px;
248
- }
249
- .pmpro_admin .widgets-holder-wrap {clear: both; margin-top: 20px; padding: 0 8px; }
250
- .pmpro_admin .widgets-holder-wrap .widget {float: left; width: 32%; margin: 0 1% 1% 0; position: relative; }
251
- .pmpro_admin .widgets-holder-wrap p.description {padding: 0; }
252
- .pmpro_admin .widgets-holder-wrap .widget-top {height: auto; cursor: default; }
253
- .pmpro_admin .widgets-holder-wrap .widget-inside {display: block; height: 130px; overflow: hidden; }
254
- .pmpro_admin .widgets-holder-wrap .widget-inside p {height: 80px; overflow: hidden; }
255
- .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside, .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside p {height: auto; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  .pmpro_admin .widgets-holder-wrap .widget-title { }
257
  .pmpro_admin .widgets-holder-wrap .widget-title h4 { }
258
- .pmpro_admin .widgets-holder-wrap .widget-title .status-label {display: block; float: left; margin: 0 5px 0 0; width: 10px;
259
- height: 10px; overflow: hidden; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border: 1px solid #DFDFDF; text-indent: -9999em; }
260
- .pmpro_admin .widgets-holder-wrap .disabled .widget-title .status-label {background: #F00; }
261
- .pmpro_admin .widgets-holder-wrap .enabled .widget-title .status-label {background: #0C0; }
262
-
263
- .pmpro_admin .widgets-holder-wrap .widget-title .version {position: absolute; top: 13px; right: 10px; }
264
- .pmpro_admin .widgets-holder-wrap .widget-inside .addon-thumb {width: 100px; height: 100px; float: right; margin: 10px 0 0 10px; border: 1px solid #DFDFDF; background: #FFF; padding: 2px;}
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
- /*@media (min-width: 1200px) {
267
- .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
 
 
 
 
 
 
 
 
 
 
 
268
  }
269
- */
270
  @media (max-width:900px) {
271
- .auto-fold .pmpro_admin .widgets-holder-wrap .widget {float: none; width: 100%; }
272
- .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
 
 
 
 
 
 
273
  }
274
 
275
  /* license */
276
  .memberships_page_pmpro-license h2 {
277
- text-align: left;
278
  }
279
  .memberships_page_pmpro-license .pmpro_icon {
280
- margin-top: 20px;
281
  }
282
 
283
  /* misc */
284
- .pmpro_lite {color: #777;}
285
- .pmpro_pad20 {padding: 20px !important;}
286
- .pmpro_red {color: #CC0000;}
287
- .pmpro_green {color: #00AA00;}
288
- .ssp_description #description {width: 100%;}
289
- .top0em {margin-top: 0;}
290
- h2.nav-tab-wrapper {margin-bottom: 1em; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
  /* reports */
293
  .memberships_page_pmpro-reports .pmpro_admin .nav-tab-wrapper {
294
- margin-bottom: 10px;
295
  }
296
  .pmpro_reports-holder { }
297
- .pmpro_reports-holder .postbox h2 {border-bottom: 1px solid #eee; font-size: 14px; padding: 8px 12px; margin: 0; line-height: 1.4; }
298
- .pmpro_clickable {cursor: pointer;}
299
- .js .postbox.pmpro_clickable h3 {cursor: pointer;}
 
 
 
 
 
 
 
 
 
 
300
  #pmpro_report_login .wp-list-table tbody td,
301
  #pmpro_report_memberships .wp-list-table tbody tr.pmpro_report_tr td,
302
  #pmpro_report_sales .wp-list-table tbody tr.pmpro_report_tr td {
303
- font-size: 1.2rem;
304
- font-weight: bold;
305
  }
306
  #pmpro_report_sales .wp-list-table tbody td:last-child {
307
- text-align: right;
308
  }
309
  .pmpro_report-holder .wp-list-table tbody th button {
310
- border: none;
311
- background: 0 0;
312
- -webkit-border-radius: 0;
313
- border-radius: 0;
314
- color: #555;
315
- cursor: pointer;
316
- line-height: 1.7;
317
- padding: 0;
318
  }
319
  button.pmpro_report_th:before {
320
- bottom: 2px;
321
- display: inline-block;
322
- font: 400 20px/1 dashicons;
323
- left: 0;
324
- padding: 0 5px 0 0;
325
- position: relative;
326
- text-decoration: none;
327
- vertical-align: bottom;
328
  }
329
  button.pmpro_report_th_opened:before {
330
- content: "\f140";
331
  }
332
  button.pmpro_report_th_closed:before {
333
- content: "\f139";
334
  }
335
  .pmpro_report-holder .wp-list-table tbody tr.pmpro_report_tr_sub:last-child th,
336
  .pmpro_report-holder .wp-list-table tbody tr.pmpro_report_tr_sub:last-child td {
337
- border-bottom: 1px solid #CCC;
338
  }
339
  #pmpro_report_memberships .wp-list-table tbody:nth-child(odd) .pmpro_report_tr th,
340
  #pmpro_report_memberships .wp-list-table tbody:nth-child(odd) .pmpro_report_tr td,
341
  #pmpro_report_sales .wp-list-table tbody:nth-child(odd) .pmpro_report_tr th,
342
  #pmpro_report_sales .wp-list-table tbody:nth-child(odd) .pmpro_report_tr td {
343
- background-color: #fff;
344
  }
345
  #pmpro_report_memberships .wp-list-table tbody:nth-child(even) .pmpro_report_tr th,
346
  #pmpro_report_memberships .wp-list-table tbody:nth-child(even) .pmpro_report_tr td,
347
  #pmpro_report_sales .wp-list-table tbody:nth-child(even) .pmpro_report_tr th,
348
  #pmpro_report_sales .wp-list-table tbody:nth-child(even) .pmpro_report_tr td {
349
- background-color: #f9f9f9;
350
  }
351
  .pmpro_report-holder .pmpro_report-button {
352
- text-align: center;
353
  }
354
 
355
  /* Dashboard */
356
  .pmpro_admin #pmpro_dashboard_welcome h3 {
357
- font-size: 16px;
358
- font-weight: 600;
359
- margin: 1rem 0 0;
360
  }
361
  .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-columns {
362
- display: grid;
363
- grid-gap: 2rem;
364
- grid-template-columns: 1fr 1fr 1fr;
365
  }
366
  .pmpro_admin #pmpro_dashboard_welcome .button-action {
367
- background: #dd823b;
368
- border-color: #c36922 #ad5d1e #ad5d1e;
369
- color: #fff; box-shadow: 0 1px 0 #ad5d1e;
370
- text-shadow: 0 -1px 1px #ad5d1e, 1px 0 1px #ad5d1e, 0 1px 1px #ad5d1e, -1px 0 1px #ad5d1e;
 
371
  }
372
  .pmpro_admin #pmpro_dashboard_welcome ul li {
373
- font-size: 14px;
374
- line-height: 16px;
375
- padding: 0 0 8px;
376
  }
377
  .pmpro_admin #pmpro_dashboard_welcome ul li a .dashicons {
378
- color: #82878c;
379
  }
380
  .pmpro_admin p.text-center {
381
- text-align: center;
382
  }
383
 
384
  /* new installation notices */
385
  .pmpro-new-install {
386
- padding: 2rem;
387
- text-align: center;
388
  }
389
  .pmpro-new-install h2 {
390
- color: #AAA;
391
- font-size: 2rem;
392
  }
393
  .pmpro-new-install h2:before {
394
- display: block;
395
- font: 400 5rem/1.5 dashicons;
396
  }
397
  .memberships_page_pmpro-membershiplevels .pmpro-new-install h2:before {
398
- content: "\f110";
399
  }
400
  .memberships_page_pmpro-pagesettings .pmpro-new-install h2:before {
401
- content: "\f133";
402
  }
403
  .memberships_page_pmpro-discountcodes .pmpro-new-install h2:before {
404
- content: "\f323";
405
  }
406
  #wpbody-content .pmpro-new-install .button-primary,
407
  #wpbody-content .pmpro-new-install .button {
408
- font-size: 1.2em;
409
- height: auto;
410
- margin-bottom: .5rem;
411
- padding: .75em 1.5em;
412
  }
413
 
414
  @media screen and (max-width: 782px) {
415
- .pmpro_admin .pmpro_banner {
416
- display: block;
417
- }
418
- .pmpro_admin .nav-tab-wrapper:after {
419
- clear: both;
420
- display: block;
421
- }
422
- .pmpro_admin .nav-tab-wrapper a {
423
- margin-top: 5px;
424
- }
425
- .dashboard-widgets-wrap .wp-list-table tr:not(.inline-edit-row):not(.no-items) td:not(.check-column),
426
- .pmpro_report-holder .wp-list-table tr:not(.inline-edit-row):not(.no-items) td:not(.check-column) {
427
- display: table-cell;
428
- overflow: hidden;
429
- word-wrap: break-word;
430
- }
431
- #pmpro_dashboard_report_recent_members .wp-list-table .column-username img {
432
- display: none;
433
- }
434
- #pmpro_dashboard_report_recent_orders .wp-list-table {
435
- border: none;
436
- }
437
- #pmpro_dashboard_report_recent_orders .wp-list-table thead,
438
- #pmpro_dashboard_report_recent_orders br {
439
- display: none;
440
- }
441
- #pmpro_dashboard_report_recent_orders .wp-list-table tr,
442
- #pmpro_dashboard_report_recent_orders .wp-list-table tr td {
443
- background: none;
444
- display: inline-block;
445
- padding: 0;
446
- position: relative;
447
- }
448
- #pmpro_dashboard_report_recent_orders .wp-list-table tr td:after {
449
- color: #CCC;
450
- content: ' |';
451
- }
452
- #pmpro_dashboard_report_recent_orders .wp-list-table tr {
453
- padding: 5px;
454
- }
455
- #pmpro_dashboard_report_recent_orders .wp-list-table tr td:last-child:after {
456
- content: '';
457
- }
458
- .pmpro-new-install {
459
- padding: 0;
460
- }
461
- .pmpro-new-install h2 {
462
- font-size: 1.4rem;
463
- }
464
- .pmpro-new-install .button-primary, .pmpro-new-install .button {
465
- display: block;
466
- }
467
- .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-columns {
468
- display: block;
469
- }
470
- .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-column {
471
- border-bottom: 1px solid #CCC;
472
- margin-bottom: 2em;
473
- padding-bottom: 2em;
474
- }
475
- .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-column:last-child {
476
- border: none;
477
- margin: 0;
478
- padding: 0;
479
- }
480
- .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr th:first-child:before,
481
- .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr td:first-child:before {
482
- content: '';
483
- width: 0;
484
- }
485
  }
1
+ /* Metaboxes on Editor */
2
+ #poststuff #pmpro_page_meta.postbox .postbox-header {
3
+ border-bottom-width: 0;
4
+ }
5
+ #poststuff #pmpro_page_meta.postbox.closed .postbox-header {
6
+ border-bottom-width: 1px;
7
+ }
8
+
9
  /* icon */
10
  #wp-admin-bar-paid-memberships-pro .ab-item .ab-icon:before {
11
+ font-family: "dashicons";
12
+ content: "\f307";
13
+ }
14
+ .pmpro_admin tr td .dashicons {
15
+ padding-top: 5px;
16
  }
 
17
 
18
  /* header/etc */
19
  .pmpro_admin {
20
+ background-image: url(../images/Paid-Memberships-Pro_watermark.png);
21
+ background-position: bottom right;
22
+ background-repeat: no-repeat;
23
+ background-size: 290px 40px;
24
+ padding: 1em 0 70px 0;
25
  }
26
  .pmpro_admin .pmpro_banner {
27
+ display: grid;
28
+ grid-template-areas: "logo meta";
29
+ grid-template-columns: 350px auto;
30
  }
31
  .pmpro_admin .pmpro_banner .pmpro_logo {
32
+ grid-area: logo;
33
  }
34
  .pmpro_admin .pmpro_banner .pmpro_meta {
35
+ align-self: center;
36
+ grid-area: meta;
37
+ font-size: 16px;
38
+ line-height: 1.5;
39
  }
40
  .pmpro_admin .pmpro_banner .pmpro_meta .pmpro_version {
41
+ color: #77a02e;
42
+ display: inline-block;
43
+ font-weight: bold;
44
+ padding: 5px 10px 5px 2px;
45
  }
46
  .pmpro_admin .pmpro_banner .pmpro_meta a {
47
+ border-left: 1px solid #CCC;
48
+ padding: 5px 10px;
49
  }
50
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag {
51
+ font-weight: bold;
52
+ padding: 5px 10px 5px 5px;
53
+ text-decoration: none;
54
  }
55
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag:before {
56
+ bottom: 7px;
57
+ display: inline-block;
58
+ font: 400 20px/1 dashicons;
59
+ left: 0;
60
+ position: relative;
61
+ text-decoration: none;
62
+ vertical-align: bottom;
63
  }
64
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-valid {
65
+ color: rgb( 70, 180, 80 );
66
  }
67
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-valid:before {
68
+ content: "\f147";
69
  }
70
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-invalid {
71
+ color: #AAA;
72
  }
73
  .pmpro_admin .pmpro_banner .pmpro_meta a.pmpro_license_tag.pmpro_license_tag-invalid:before {
74
+ content: "\f335";
75
  }
76
 
77
  .pmpro_admin .pmpro_wp-notice-fix {
78
+ height: 1px;
79
+ line-height: 1px;
80
+ margin: 0;
81
+ }
82
+ .pmpro_admin .topborder {
83
+ border-top: 1px solid #CCC;
84
+ margin-top: 1em;
85
+ padding-top: 1em;
86
+ }
87
+ .pmpro_admin #editorcontainer #description {
88
+ width: 100%;
89
+ height: 180px;
90
  }
 
 
 
91
  .pmpro_admin .pmpro_icon {
92
+ max-width: 200px;
93
+ height: auto;
94
+ }
95
+
96
+ /* Scollable Boxes */
97
+ .pmpro_scrollable {
98
+ background-color: #FFFFFF;
99
+ height: 170px;
100
+ overflow: auto;
101
+ }
102
+ .pmpro_scrollable::-webkit-scrollbar {
103
+ background-color: #c3c4c7;
104
+ width: 8px;
105
+ height: 8px;
106
+ }
107
+ .pmpro_scrollable::-webkit-scrollbar-thumb {
108
+ background: #2c3338;
109
+ border-radius: 5px;
110
+ }
111
+ .pmpro_scrollable table.widefat {
112
+ border: none;
113
  }
114
 
115
+ /**
116
+ * Checkbox Boxes
117
+ *
118
+ * Note: We will eventually remove the non-prefixed class names here. They are still in some Add Ons.
119
+ *
120
+ */
121
+ .pmpro_checkbox_box,
122
+ .checkbox_box {
123
+ background-color: #FFFFFF;
124
+ border: 1px solid #c3c4c7;
125
+ max-width: 300px;
126
+ }
127
+ .pmpro_checkbox_box div,
128
+ .checkbox_box div {
129
+ border-bottom: 1px solid #CCC;
130
+ padding: 3px;
131
+ }
132
+ .pmpro_checkbox_box div:last-child,
133
+ .checkbox_box div:last-child {
134
+ border-bottom: none;
135
+ }
136
+ .pmpro_checkbox_box .pmpro_clickable,
137
+ .checkbox_box .clickable {
138
+ cursor: pointer;
139
+ }
140
+ .pmpro_checkbox_box .pmpro_clickable:hover,
141
+ .checkbox_box .clickable:hover {
142
+ background: #FFC;
143
+ }
144
+ .pmpro_checkbox_box .pmpro_clickable {
145
+ align-items: center;
146
+ display: flex;
147
+ }
148
+ .pmpro_checkbox_box .pmpro_clickable label {
149
+ flex-grow: 1;
150
+ }
151
+ .pmpro_checkbox_box .pmpro_clickable input[type=checkbox] {
152
+ margin-top: 0;
153
+ }
154
 
155
+ /* Consent Log */
156
  .pmpro_consent_log.pmpro_scrollable {
 
 
 
 
157
  max-width: 800px;
158
+ border: 1px solid #c3c4c7;
159
+ padding: 0;
160
+ }
161
+ .pmpro_consent_log li {
162
+ font-size: 13px;
163
+ line-height: 1.5em;
164
+ margin: 0;
165
+ }
166
+ .pmpro_consent_log.pmpro_scrollable li {
167
+ padding: 8px 10px;
168
+ }
169
+ .pmpro_consent_log.pmpro_scrollable li:nth-child(odd) {
170
+ background-color: #f6f7f7;
171
  }
172
 
173
  /* levels */
174
+ .memberships_page_pmpro-membershiplevels .pmpro_admin #posts-filter p.search-box {
175
+ margin: 2em 0 1em 0;
176
+ }
177
+ .memberships_page_pmpro-membershiplevels .pmpro_admin tr.pmpro_gray td {
178
+ color: #AAA;
179
+ }
180
+ .memberships_page_pmpro-membershiplevels .pmpro_admin tr td.level_name span.level-name a {
181
+ font-size: 115%;
182
+ font-weight: bold;
183
+ }
184
+ .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr {
185
+ background: #fff;
186
+ }
187
+ .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr.alternate {
188
+ background: #f1f1f1;
189
+ }
190
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr.ui-sortable-handle {
191
+ border: 1px solid #2997C8;
192
+ cursor: move;
193
  }
194
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr th:first-child:before,
195
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr td:first-child:before {
196
+ color: #CCC;
197
+ content: "\f333";
198
+ display: inline-block;
199
+ font: 400 16px/1 dashicons;
200
+ opacity: 0;
201
+ width: 25px;
202
  }
203
  .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr:hover td:first-child:before {
204
+ opacity: 1;
205
+ }
206
+ .memberships_page_pmpro-membershiplevels .pmpro_admin tr.testclass {
207
+ border: 3px solid #2997C8;
208
+ background: #2997C8;
209
+ }
210
+ .memberships_page_pmpro-membershiplevels .pmpro_admin tr.membership_categories ul {
211
+ margin-left: 25px;
212
  }
 
 
213
 
214
  /* payment settings */
215
  .admin_page_pmpro-paymentsettings .form-table select {
216
+ max-width: none;
217
+ }
218
+
219
+ .admin_page_pmpro-paymentsettings span.pmpro_gateway-mode {
220
+ border: 1px solid;
221
+ border-radius: 3px;
222
+ display: inline-block;
223
+ font-size: 75%;
224
+ margin-left: 5px;
225
+ padding: 3px 5px;
226
+ }
227
+
228
+ .admin_page_pmpro-paymentsettings span.pmpro_gateway-mode-live {
229
+ background-color: #edfaef;
230
+ border-color: #00a32a;
231
+ color: #00a32a;
232
+ }
233
+
234
+ .admin_page_pmpro-paymentsettings span.pmpro_gateway-mode-live.pmpro_gateway-mode-not-connected {
235
+ background-color: #fcf0f1;
236
+ border-color: #d63638;
237
+ color: #d63638;
238
+ }
239
+
240
+ .admin_page_pmpro-paymentsettings span.pmpro_gateway-mode-test {
241
+ background-color: #fcf9e8;
242
+ border-color: #dba617;
243
+ color: #dba617;
244
+ }
245
+
246
+ .admin_page_pmpro-paymentsettings span.pmpro_gateway-mode-test.pmpro_gateway-mode-not-connected {
247
+ background-color: #f9f3d3;
248
+ border-color: #d59a13;
249
+ color: #d59a13;
250
+ }
251
+
252
+ .admin_page_pmpro-paymentsettings .pmpro-stripe-connect {
253
+ display: inline-block;
254
+ margin-bottom: 1px;
255
+
256
+ background-image: -webkit-linear-gradient(#28A0E5, #015E94);
257
+ background-image: -moz-linear-gradient(#28A0E5, #015E94);
258
+ background-image: -ms-linear-gradient(#28A0E5, #015E94);
259
+ background-image: linear-gradient(#28A0E5, #015E94);
260
+
261
+ -webkit-font-smoothing: antialiased;
262
+ border: 0;
263
+ padding: 1px;
264
+ height: 30px;
265
+ text-decoration: none;
266
+
267
+ -moz-border-radius: 4px;
268
+ -webkit-border-radius: 4px;
269
+ border-radius: 4px;
270
+
271
+ -moz-box-shadow: 0 1px 0 rgba(0,0,0,0.2);
272
+ -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
273
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
274
+
275
+ -webkit-box-sizing: initial;
276
+ -moz-box-sizing: initial;
277
+ box-sizing: initial;
278
+
279
+ cursor: pointer;
280
+
281
+ -moz-user-select: none;
282
+ -webkit-user-select: none;
283
+ -ms-user-select: none;
284
+ user-select: none;
285
+ }
286
+
287
+ .admin_page_pmpro-paymentsettings .pmpro-stripe-connect span {
288
+ display: block;
289
+ position: relative;
290
+ padding: 0 12px 0 44px;
291
+ height: 30px;
292
+
293
+ background: #1275FF;
294
+ background-image: -webkit-linear-gradient(#7DC5EE, #008CDD 85%, #30A2E4);
295
+ background-image: -moz-linear-gradient(#7DC5EE, #008CDD 85%, #30A2E4);
296
+ background-image: -ms-linear-gradient(#7DC5EE, #008CDD 85%, #30A2E4);
297
+ background-image: linear-gradient(#7DC5EE, #008CDD 85%, #30A2E4);
298
+
299
+ font-size: 14px;
300
+ line-height: 30px;
301
+ color: white;
302
+ font-weight: bold;
303
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
304
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
305
+
306
+ -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.25);
307
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
308
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
309
+
310
+ -moz-border-radius: 3px;
311
+ -webkit-border-radius: 3px;
312
+ border-radius: 3px;
313
+ }
314
+
315
+ .admin_page_pmpro-paymentsettings .pmpro-stripe-connect span:before {
316
+ content: '';
317
+ display: block;
318
+ position: absolute;
319
+ left: 11px;
320
+ top: 50%;
321
+ width: 23px;
322
+ height: 24px;
323
+ margin-top: -12px;
324
+ background-repeat: no-repeat;
325
+ background-size: 23px 24px;
326
+ }
327
+
328
+ .admin_page_pmpro-paymentsettings .pmpro-stripe-connect:active {
329
+ background: #005D93;
330
+ }
331
+
332
+ .admin_page_pmpro-paymentsettings .pmpro-stripe-connect:active span {
333
+ color: #EEE;
334
+
335
+ background: #008CDD;
336
+ background-image: -webkit-linear-gradient(#008CDD, #008CDD 85%, #239ADF);
337
+ background-image: -moz-linear-gradient(#008CDD, #008CDD 85%, #239ADF);
338
+ background-image: -ms-linear-gradient(#008CDD, #008CDD 85%, #239ADF);
339
+ background-image: linear-gradient(#008CDD, #008CDD 85%, #239ADF);
340
+
341
+ -moz-box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
342
+ -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1);
343
+ box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1);
344
+ }
345
+
346
+ .admin_page_pmpro-paymentsettings .pmpro-stripe-connect span:before {
347
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAYCAYAAAARfGZ1AAAKRGlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUFNcXx9/MbC+0XZYiZem9twWkLr1IlSYKy+4CS1nWZRewN0QFIoqICFYkKGLAaCgSK6JYCAgW7AEJIkoMRhEVlczGHPX3Oyf5/U7eH3c+8333nnfn3vvOGQAoASECYQ6sAEC2UCKO9PdmxsUnMPG9AAZEgAM2AHC4uaLQKL9ogK5AXzYzF3WS8V8LAuD1LYBaAK5bBIQzmX/p/+9DkSsSSwCAwtEAOx4/l4tyIcpZ+RKRTJ9EmZ6SKWMYI2MxmiDKqjJO+8Tmf/p8Yk8Z87KFPNRHlrOIl82TcRfKG/OkfJSREJSL8gT8fJRvoKyfJc0WoPwGZXo2n5MLAIYi0yV8bjrK1ihTxNGRbJTnAkCgpH3FKV+xhF+A5gkAO0e0RCxIS5cwjbkmTBtnZxYzgJ+fxZdILMI53EyOmMdk52SLOMIlAHz6ZlkUUJLVlokW2dHG2dHRwtYSLf/n9Y+bn73+GWS9/eTxMuLPnkGMni/al9gvWk4tAKwptDZbvmgpOwFoWw+A6t0vmv4+AOQLAWjt++p7GLJ5SZdIRC5WVvn5+ZYCPtdSVtDP6386fPb8e/jqPEvZeZ9rx/Thp3KkWRKmrKjcnKwcqZiZK+Jw+UyL/x7ifx34VVpf5WEeyU/li/lC9KgYdMoEwjS03UKeQCLIETIFwr/r8L8M+yoHGX6aaxRodR8BPckSKPTRAfJrD8DQyABJ3IPuQJ/7FkKMAbKbF6s99mnuUUb3/7T/YeAy9BXOFaQxZTI7MprJlYrzZIzeCZnBAhKQB3SgBrSAHjAGFsAWOAFX4Al8QRAIA9EgHiwCXJAOsoEY5IPlYA0oAiVgC9gOqsFeUAcaQBM4BtrASXAOXARXwTVwE9wDQ2AUPAOT4DWYgSAID1EhGqQGaUMGkBlkC7Egd8gXCoEioXgoGUqDhJAUWg6tg0qgcqga2g81QN9DJ6Bz0GWoH7oDDUPj0O/QOxiBKTAd1oQNYSuYBXvBwXA0vBBOgxfDS+FCeDNcBdfCR+BW+Bx8Fb4JD8HP4CkEIGSEgeggFggLYSNhSAKSioiRlUgxUonUIk1IB9KNXEeGkAnkLQaHoWGYGAuMKyYAMx/DxSzGrMSUYqoxhzCtmC7MdcwwZhLzEUvFamDNsC7YQGwcNg2bjy3CVmLrsS3YC9ib2FHsaxwOx8AZ4ZxwAbh4XAZuGa4UtxvXjDuL68eN4KbweLwa3gzvhg/Dc/ASfBF+J/4I/gx+AD+Kf0MgE7QJtgQ/QgJBSFhLqCQcJpwmDBDGCDNEBaIB0YUYRuQRlxDLiHXEDmIfcZQ4Q1IkGZHcSNGkDNIaUhWpiXSBdJ/0kkwm65KdyRFkAXk1uYp8lHyJPEx+S1GimFLYlESKlLKZcpBylnKH8pJKpRpSPakJVAl1M7WBep76kPpGjiZnKRcox5NbJVcj1yo3IPdcnihvIO8lv0h+qXyl/HH5PvkJBaKCoQJbgaOwUqFG4YTCoMKUIk3RRjFMMVuxVPGw4mXFJ0p4JUMlXyWeUqHSAaXzSiM0hKZHY9O4tHW0OtoF2igdRzeiB9Iz6CX07+i99EllJWV75RjlAuUa5VPKQwyEYcgIZGQxyhjHGLcY71Q0VbxU+CqbVJpUBlSmVeeoeqryVYtVm1Vvqr5TY6r5qmWqbVVrU3ugjlE3VY9Qz1ffo35BfWIOfY7rHO6c4jnH5tzVgDVMNSI1lmkc0OjRmNLU0vTXFGnu1DyvOaHF0PLUytCq0DqtNa5N03bXFmhXaJ/RfspUZnoxs5hVzC7mpI6GToCOVGe/Tq/OjK6R7nzdtbrNug/0SHosvVS9Cr1OvUl9bf1Q/eX6jfp3DYgGLIN0gx0G3QbThkaGsYYbDNsMnxipGgUaLTVqNLpvTDX2MF5sXGt8wwRnwjLJNNltcs0UNnUwTTetMe0zg80czQRmu836zbHmzuZC81rzQQuKhZdFnkWjxbAlwzLEcq1lm+VzK32rBKutVt1WH60drLOs66zv2SjZBNmstemw+d3W1JZrW2N7w45q52e3yq7d7oW9mT3ffo/9bQeaQ6jDBodOhw+OTo5ixybHcSd9p2SnXU6DLDornFXKuuSMdfZ2XuV80vmti6OLxOWYy2+uFq6Zroddn8w1msufWzd3xE3XjeO2323Ineme7L7PfchDx4PjUevxyFPPk+dZ7znmZeKV4XXE67m3tbfYu8V7mu3CXsE+64P4+PsU+/T6KvnO9632fein65fm1+g36e/gv8z/bAA2IDhga8BgoGYgN7AhcDLIKWhFUFcwJTgquDr4UYhpiDikIxQODQrdFnp/nsE84by2MBAWGLYt7EG4Ufji8B8jcBHhETURjyNtIpdHdkfRopKiDke9jvaOLou+N994vnR+Z4x8TGJMQ8x0rE9seexQnFXcirir8erxgvj2BHxCTEJ9wtQC3wXbF4wmOiQWJd5aaLSwYOHlReqLshadSpJP4iQdT8YmxyYfTn7PCePUcqZSAlN2pUxy2dwd3Gc8T14Fb5zvxi/nj6W6pZanPklzS9uWNp7ukV6ZPiFgC6oFLzICMvZmTGeGZR7MnM2KzWrOJmQnZ58QKgkzhV05WjkFOf0iM1GRaGixy+LtiyfFweL6XCh3YW67hI7+TPVIjaXrpcN57nk1eW/yY/KPFygWCAt6lpgu2bRkbKnf0m+XYZZxl3Uu11m+ZvnwCq8V+1dCK1NWdq7SW1W4anS1/+pDa0hrMtf8tNZ6bfnaV+ti13UUahauLhxZ77++sUiuSFw0uMF1w96NmI2Cjb2b7Dbt3PSxmFd8pcS6pLLkfSm39Mo3Nt9UfTO7OXVzb5lj2Z4tuC3CLbe2emw9VK5YvrR8ZFvottYKZkVxxavtSdsvV9pX7t1B2iHdMVQVUtW+U3/nlp3vq9Orb9Z41zTv0ti1adf0bt7ugT2ee5r2au4t2ftun2Df7f3++1trDWsrD+AO5B14XBdT1/0t69uGevX6kvoPB4UHhw5FHupqcGpoOKxxuKwRbpQ2jh9JPHLtO5/v2pssmvY3M5pLjoKj0qNPv0/+/tax4GOdx1nHm34w+GFXC62luBVqXdI62ZbeNtQe395/IuhEZ4drR8uPlj8ePKlzsuaU8qmy06TThadnzyw9M3VWdHbiXNq5kc6kznvn487f6Iro6r0QfOHSRb+L57u9us9ccrt08rLL5RNXWFfarjpebe1x6Gn5yeGnll7H3tY+p772a87XOvrn9p8e8Bg4d93n+sUbgTeu3px3s//W/Fu3BxMHh27zbj+5k3Xnxd28uzP3Vt/H3i9+oPCg8qHGw9qfTX5uHnIcOjXsM9zzKOrRvRHuyLNfcn95P1r4mPq4ckx7rOGJ7ZOT437j154ueDr6TPRsZqLoV8Vfdz03fv7Db56/9UzGTY6+EL+Y/b30pdrLg6/sX3VOhU89fJ39ema6+I3am0NvWW+738W+G5vJf49/X/XB5EPHx+CP92ezZ2f/AAOY8/wRDtFgAAADQklEQVRIDbWVaUiUQRjHZ96dXY/d1fYQj1U03dJSw9YkFgy6DIkILRArQSSC7PjQjQQqVH7oQ0GHQUWgpQhKHzoNSqiUwpXcsrwIjzVtPVrzbPV9Z6bZhYV3N3WXYAeGmWeeZ37z8J95GEgpBf5oeXn1Es4fYAdzPDlM6je4RBYhR+LMU89UxiCBGiCgkUwsBYSA+SlPKLQBQAYEAZm+3j42K96z3NyOF7VOeMrp62opRcacjPW5+43rDTpNSKQ8QKZAEg7xmPCTs/O27uGJgXuNbW0pxyvLfTmAEBzthEsFZLxRvPdi5rpYo2cmUiQJDA4IVeo0obGdlvGfXUPj0Sym2zPuHxvzcWjDyVupJ/YYizKTGNjLw/HiduNTAqIRIUJ6Vpp+ky8bCSFgwQ2xgkGxFi1ioNWEBGuJB31gbLIv/2pd7SpFoGxtpCYkLSEq4ptlzIYFO7tc7w0TKkeEYg5ADnrWkkYhD8s26GPq3nW0WKxTptftPYBI4Mj3O2fHvKNZBMVSDmMwarXNjDkSF3d5kExZeiCr8M2VI+VFu9IvsPcYtzAvkfoEZkEEE45jMppq3ppbCNPFIY1nD1cpo07lbMmvOXeoDCF8BLKy9uUAAjDkBh+c6bz78mNtVVP7MwET7JBnqb4xXpdWVpC1OVzWn+ELHLCsneX/s7rkRWl1463cy1U3WroG21jhCGKJXPOtKQnpAuENvsAppgDB3TcDVIrpDHbK5Kd+y7W8iodNybHh22rOHyxUK+UaMYjZaoyp25rYL54TSihSKmwZ14v3lc3ZFxdbeywjn/tGJnkmzrydX1ApxOEACKymmXLYfXVpi1JMEOGxPi1ep18doY4r2J7uFumQQ9yGf01bMcZW8dpyc0oIjxxpuC5wuUDX+ovWrnYeg3aXvdLIqnmOvXPsfH6uA5YbTb1DX8ofvTLzTy6ZV4K6fAw+gXiATfdffmjeaUgc1UdpdWplsCooQBrEnqUw82dhdnjit/Vxc4f59tP3DRjzJvYteqrl4rmNlJIfrOwpgNklesDRNQBCHYtQAQqD2CgACNjHAJnG1EyfV/S67fZiJB5t2OGEe4n7L3fS4fpEv/2hUEATfoPbuam5v8N7nps70YTbAAAAAElFTkSuQmCC");
348
+ }
349
+
350
+ /* Retina support */
351
+ @media only screen and (-webkit-min-device-pixel-ratio: 1.5),
352
+ only screen and (min--moz-device-pixel-ratio: 1.5),
353
+ only screen and (min-device-pixel-ratio: 1.5) {
354
+ .admin_page_pmpro-paymentsettings .pmpro-stripe-connect span:before {
355
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAwCAYAAABuZUjcAAAKRGlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUFNcXx9/MbC+0XZYiZem9twWkLr1IlSYKy+4CS1nWZRewN0QFIoqICFYkKGLAaCgSK6JYCAgW7AEJIkoMRhEVlczGHPX3Oyf5/U7eH3c+8333nnfn3vvOGQAoASECYQ6sAEC2UCKO9PdmxsUnMPG9AAZEgAM2AHC4uaLQKL9ogK5AXzYzF3WS8V8LAuD1LYBaAK5bBIQzmX/p/+9DkSsSSwCAwtEAOx4/l4tyIcpZ+RKRTJ9EmZ6SKWMYI2MxmiDKqjJO+8Tmf/p8Yk8Z87KFPNRHlrOIl82TcRfKG/OkfJSREJSL8gT8fJRvoKyfJc0WoPwGZXo2n5MLAIYi0yV8bjrK1ihTxNGRbJTnAkCgpH3FKV+xhF+A5gkAO0e0RCxIS5cwjbkmTBtnZxYzgJ+fxZdILMI53EyOmMdk52SLOMIlAHz6ZlkUUJLVlokW2dHG2dHRwtYSLf/n9Y+bn73+GWS9/eTxMuLPnkGMni/al9gvWk4tAKwptDZbvmgpOwFoWw+A6t0vmv4+AOQLAWjt++p7GLJ5SZdIRC5WVvn5+ZYCPtdSVtDP6386fPb8e/jqPEvZeZ9rx/Thp3KkWRKmrKjcnKwcqZiZK+Jw+UyL/x7ifx34VVpf5WEeyU/li/lC9KgYdMoEwjS03UKeQCLIETIFwr/r8L8M+yoHGX6aaxRodR8BPckSKPTRAfJrD8DQyABJ3IPuQJ/7FkKMAbKbF6s99mnuUUb3/7T/YeAy9BXOFaQxZTI7MprJlYrzZIzeCZnBAhKQB3SgBrSAHjAGFsAWOAFX4Al8QRAIA9EgHiwCXJAOsoEY5IPlYA0oAiVgC9gOqsFeUAcaQBM4BtrASXAOXARXwTVwE9wDQ2AUPAOT4DWYgSAID1EhGqQGaUMGkBlkC7Egd8gXCoEioXgoGUqDhJAUWg6tg0qgcqga2g81QN9DJ6Bz0GWoH7oDDUPj0O/QOxiBKTAd1oQNYSuYBXvBwXA0vBBOgxfDS+FCeDNcBdfCR+BW+Bx8Fb4JD8HP4CkEIGSEgeggFggLYSNhSAKSioiRlUgxUonUIk1IB9KNXEeGkAnkLQaHoWGYGAuMKyYAMx/DxSzGrMSUYqoxhzCtmC7MdcwwZhLzEUvFamDNsC7YQGwcNg2bjy3CVmLrsS3YC9ib2FHsaxwOx8AZ4ZxwAbh4XAZuGa4UtxvXjDuL68eN4KbweLwa3gzvhg/Dc/ASfBF+J/4I/gx+AD+Kf0MgE7QJtgQ/QgJBSFhLqCQcJpwmDBDGCDNEBaIB0YUYRuQRlxDLiHXEDmIfcZQ4Q1IkGZHcSNGkDNIaUhWpiXSBdJ/0kkwm65KdyRFkAXk1uYp8lHyJPEx+S1GimFLYlESKlLKZcpBylnKH8pJKpRpSPakJVAl1M7WBep76kPpGjiZnKRcox5NbJVcj1yo3IPdcnihvIO8lv0h+qXyl/HH5PvkJBaKCoQJbgaOwUqFG4YTCoMKUIk3RRjFMMVuxVPGw4mXFJ0p4JUMlXyWeUqHSAaXzSiM0hKZHY9O4tHW0OtoF2igdRzeiB9Iz6CX07+i99EllJWV75RjlAuUa5VPKQwyEYcgIZGQxyhjHGLcY71Q0VbxU+CqbVJpUBlSmVeeoeqryVYtVm1Vvqr5TY6r5qmWqbVVrU3ugjlE3VY9Qz1ffo35BfWIOfY7rHO6c4jnH5tzVgDVMNSI1lmkc0OjRmNLU0vTXFGnu1DyvOaHF0PLUytCq0DqtNa5N03bXFmhXaJ/RfspUZnoxs5hVzC7mpI6GToCOVGe/Tq/OjK6R7nzdtbrNug/0SHosvVS9Cr1OvUl9bf1Q/eX6jfp3DYgGLIN0gx0G3QbThkaGsYYbDNsMnxipGgUaLTVqNLpvTDX2MF5sXGt8wwRnwjLJNNltcs0UNnUwTTetMe0zg80czQRmu836zbHmzuZC81rzQQuKhZdFnkWjxbAlwzLEcq1lm+VzK32rBKutVt1WH60drLOs66zv2SjZBNmstemw+d3W1JZrW2N7w45q52e3yq7d7oW9mT3ffo/9bQeaQ6jDBodOhw+OTo5ixybHcSd9p2SnXU6DLDornFXKuuSMdfZ2XuV80vmti6OLxOWYy2+uFq6Zroddn8w1msufWzd3xE3XjeO2323Ineme7L7PfchDx4PjUevxyFPPk+dZ7znmZeKV4XXE67m3tbfYu8V7mu3CXsE+64P4+PsU+/T6KvnO9632fein65fm1+g36e/gv8z/bAA2IDhga8BgoGYgN7AhcDLIKWhFUFcwJTgquDr4UYhpiDikIxQODQrdFnp/nsE84by2MBAWGLYt7EG4Ufji8B8jcBHhETURjyNtIpdHdkfRopKiDke9jvaOLou+N994vnR+Z4x8TGJMQ8x0rE9seexQnFXcirir8erxgvj2BHxCTEJ9wtQC3wXbF4wmOiQWJd5aaLSwYOHlReqLshadSpJP4iQdT8YmxyYfTn7PCePUcqZSAlN2pUxy2dwd3Gc8T14Fb5zvxi/nj6W6pZanPklzS9uWNp7ukV6ZPiFgC6oFLzICMvZmTGeGZR7MnM2KzWrOJmQnZ58QKgkzhV05WjkFOf0iM1GRaGixy+LtiyfFweL6XCh3YW67hI7+TPVIjaXrpcN57nk1eW/yY/KPFygWCAt6lpgu2bRkbKnf0m+XYZZxl3Uu11m+ZvnwCq8V+1dCK1NWdq7SW1W4anS1/+pDa0hrMtf8tNZ6bfnaV+ti13UUahauLhxZ77++sUiuSFw0uMF1w96NmI2Cjb2b7Dbt3PSxmFd8pcS6pLLkfSm39Mo3Nt9UfTO7OXVzb5lj2Z4tuC3CLbe2emw9VK5YvrR8ZFvottYKZkVxxavtSdsvV9pX7t1B2iHdMVQVUtW+U3/nlp3vq9Orb9Z41zTv0ti1adf0bt7ugT2ee5r2au4t2ftun2Df7f3++1trDWsrD+AO5B14XBdT1/0t69uGevX6kvoPB4UHhw5FHupqcGpoOKxxuKwRbpQ2jh9JPHLtO5/v2pssmvY3M5pLjoKj0qNPv0/+/tax4GOdx1nHm34w+GFXC62luBVqXdI62ZbeNtQe395/IuhEZ4drR8uPlj8ePKlzsuaU8qmy06TThadnzyw9M3VWdHbiXNq5kc6kznvn487f6Iro6r0QfOHSRb+L57u9us9ccrt08rLL5RNXWFfarjpebe1x6Gn5yeGnll7H3tY+p772a87XOvrn9p8e8Bg4d93n+sUbgTeu3px3s//W/Fu3BxMHh27zbj+5k3Xnxd28uzP3Vt/H3i9+oPCg8qHGw9qfTX5uHnIcOjXsM9zzKOrRvRHuyLNfcn95P1r4mPq4ckx7rOGJ7ZOT437j154ueDr6TPRsZqLoV8Vfdz03fv7Db56/9UzGTY6+EL+Y/b30pdrLg6/sX3VOhU89fJ39ema6+I3am0NvWW+738W+G5vJf49/X/XB5EPHx+CP92ezZ2f/AAOY8/wRDtFgAAAIbklEQVRoBdVZa5BURxU+fZ9z57mzs7PvF4i7srAQSCifMVDERC0jYlzUlJalKeGPlCnL/NEfywpWacoiVZRVJIYfGjGUu5bxj5qHFSAYyQOBEsJzYSHDvnd2dp535j66PX1vNgsULDPs1cr2Vs+9e7v79NfnnnP663MJYwwWYxEWI2iOedEClxabxgkBwjEvOuA9PQOOlSw64JMr4vK8GidYYMcOES4tVSEAAZ8FAUqon1GiAJEEEG0CjFB8cTaxZUMAo1gEqQA0UABprAjPbrUwXnkesgqKP8CBk5vDIenrE+BKmwI+MawA1MbCkdV10cBDflXuVmSxQRbFkCAQZ9U2ZTaONyxKcyXDHjMs83ImV3rz6njmDRPMUZB80zAJOuvvsflkXpTP7DrWyeXcYCqk75AEieawrEoty1vrvlcV0ja3VQdb1rVUQVd9EFqqNIj5ZfDJooPBsCnohq2ldDMynC42XZnW7z09lu25lMxDMl34y0gyvTsBwyewc84Z4MEPpWIzF/MBcLLtNzJISmxZU+PmWETbtqGzfvVja5uguyF02+kCIEJUk6Ex4oMV9XP9ZnQT/nZ24it7XrtoJ5LZ7SjAM+Bg2+0ckAOcbBkQIaZFVzY1bGurjezYfn87PNQZ5+13ZaQRXMzH26Lg8ymfUokQdAR59INOc53GQ6q/Jiiua6oJ7+h9uAPua47cHeLrwHEmQRmTGLHV6x4v+JYwWsOFCGRDn6RKem1rPPrkN9Y0uqAXLN4VwCgjYGEE8rBgMAjwKsF9S9WgLa9qjYcf+Po9jXdlGrfC5Wj8Vg0Lf+ZENAFmpGB9TWTLhmUxUD1UDg/gtudRnK+a4RtkgqQyO+RT5LVrmiLgJcN19gcGNojUWriS5yRQm7pcBTc/vyCKdW1RrWwzOTiYhGf+dRUmcgZosgDVfgWaMCS2V2tO+OzG0MiVjdUwiFiYm9a7O4kJAoZEooV9H4T0O0ofODkKr5+6+nY6V3heVZQpv6ZWaz55qSJJnXjtUBW5pT7k8xeK5u+B0PQdBVbQgTLq9HbQYthyNVSmTT6A/nB0aGpF0K99+trY1F7TNI9PZGXkKUVRtYjGZCIOV1dHR4Ynz8FSLV8BrjK6uiAlpLcmco1ipmgpAaU8rfesboCuumBg31uJbx6+qH0uX9D/em0i85xFhaslKZKA8/82RtYDhd/1MkCuBnjxrLgKB0EQSb5oWO+9O1bZrsy3+Kc3dcH+b99b07NuyXe6P9r8z/am+C9lkuqCjo4qGGkQES76qJcuz/2GOlUoFuVsQS+98frlaSeq8Gkqqctrg7Dz853wwrfugUfXtj3W3tJ8oCletRUEXy1SCSSYHhdu41gFqILcZCrzwkvnJmE0U3JtHefiL7eS2l7th11f7IQ9j65aVh+r+nlzbd2TELJrHPLmIXZX3wyBX8MTQMm8PJ0u9Pe9chGQYy9omvXouHu/thJqI+Ef1sZDm0AMBmfPiQsSPDuY2zhWwSH5ISU5Pjm98x9nRo7+7JVBB3wl5nJz35Vo/z/esBQUVf2+QlkD9Aw42/Ts3Au7ushdAhQ5UzJoOjE+OrV9/1tDR7cNnIax7N2bDX9nm1bUQXdz9Rp/MLwRoqAtDOzcaO7rvDrAWW8vhcatWVNjF6cmJre9embkz1947h3YfXgIUgVzblQldxgFH0ZOr/qULwM15k4Zlci4Vd9ZU5ltY71oObHBnBFQBidmUk8kEsOP7Hntwqsb974NfS8PAh7LKoo23Hw+2R4FQcSzKlDPgFOEyf8kx3HW94kQ7xJgRRdAJG7CyIWxgiXNUN0+k5nJLN83k3n8D8eHN3+1ux5+8uBHIKiWt1G1Rn3IJkiUCcQzU3G0h9qWHMeJdoSrwtr9dl6I6DNjFwRRyxiKnStSqkPJPsGSmZ+mp1P9z2dzOy3Klj31yMdmX9S8V75APEsomMZwT9fz9i6vkW9AvEgQyqrBQM2Dq9rrD0gCgXfHA0jpjIRm2Zcw+3CR2tZl27SnMZFSZ1lWcRwZITeDckresAEXaoKwwBh7/WQubgTOQj5BVjdv7KiBJz7bztMNcHIk03JiONNyfiK/ntv2VMHAMx6BjpoA/Gj9Emdjul7W7e6TeQNDK9WJLRm361P5c1drEmAaymaYoXpfjZoiOk7FHWuh5dxEHmzLHiXM9oyTz9FawRZw65f5yyzXBMpd0JGhFKB5nSwRMVvumDv2cxm4m1f5X4AuWhRePDUOtqEPQJVVGfWcBz1ahmPlTlxzqaJLquYZU1HTvjcTMD6dOULM0n+g5nKposHzdWbo7FgEkDBviWlYx++53XtQ33kvDU8dHAJm6L8usdwEZn09S3qiPed5lcCSLUpI0eEA8620zLbDl6bh8T+egkI+/7Rl6kegcTSPst1QUKaM+brhrjnF2yUQJNxnrGMnR7KbTw5nYFVjyAl98w2+VdvVlA67Dw3BgROjAKa+yyrpz0BKTbJnez1NT6AKrrnA1bEi1av2v3xaiL90dnxL2Kc0rsXc4WpcQEc8AEtiGrRiejmK6WWeMDIxtVwwKExijB5KFuBYIg1cy8dx0dTQ/yQVc78yBXMIqJ5i/VvvkqHdSjXuM/THKy7w2LQJ6fpJms38QiHGvlzBt+RwJv2JQ2elbjyRtjIi1AIRMAsKPuQduHVzr2YW+kIBE5BTwOzzxLKOiMX8QVuWh00IpqD+S0WHtLlzefpLBOZo/IYvEqQPnTX5dxmy4xookqaCjRuT4mMi8g3bxs2KCkj3GFj4+QSzA0RkeskU8iCJeUiBDv09Jt8OPEV6k7DlP3gxxh/dAPymPh/Kf5d897dIOd9P7H8oEd4G1JV8wPGbRadx52sgLmrRAZ99EZ5+LZgV+v+4Llrg/wX6HRCxgvzAAwAAAABJRU5ErkJggg==");
356
+ }
357
+ }
358
+
359
+ .admin_page_pmpro-paymentsettings .pmpro-admin-secure-key {
360
+ -webkit-text-security: disc;
361
  }
362
 
363
  /* advanced settings */
364
  .admin_page_pmpro-advancedsettings .form-table select:not(.admin_page_pmpro-advancedsettings .form-table select#tospage) {
365
+ max-width: none;
366
  }
367
 
368
  /* orders */
369
  .memberships_page_pmpro-orders .pmpro_admin .nav-tab-wrapper {
370
+ margin-bottom: 10px;
371
  }
372
 
373
  /* members list */
374
  .wp-list-table.members .column-ID {
375
+ white-space: nowrap;
376
  }
377
+ .wp-list-table.members #display_name {
378
+ width: auto;
379
  }
380
 
381
  /* settings */
382
  tr.pmpro_settings_divider td {
383
+ padding: 0;
384
+ margin: 0;
385
+ }
386
+
387
+ /* admin pages */
388
+ .pmpro_admin_section-email-templates-content .pmpro-email-templates-variable-reference .form-table td {
389
+ padding: 5px;
390
+ }
391
+ .pmpro_admin_section-email-templates-content .pmpro-email-templates-variable-reference .form-table .widefat {
392
+ margin-bottom: 20px;
393
  }
394
 
395
  /* messages */
396
  .pmpro_admin .pmpro_message {
397
+ background: #FFF;
398
+ border-left: 4px solid #FFF;
399
+ margin-right: 15px;
400
+ padding: 15px;
401
  }
402
  .pmpro_admin .pmpro_success {
403
+ background-color: rgba( 70, 180, 80, 0.1 );
404
+ border-left-color: rgb( 70, 180, 80 );
405
  }
406
  .pmpro_admin .pmpro_error,
407
  .pmpro_admin tr.pmpro_error td {
408
+ background-color: rgba( 220, 50, 50, 0.1 );
409
+ border-left-color: rgb( 220, 50, 50 );
410
  }
411
  .pmpro_admin .pmpro_alert {
412
+ background-color: rgba( 255, 185, 0, 0.1 );
413
+ border-left-color: rgb( 255, 185, 0 );
414
  }
415
  .pmpro_admin .pmpro_success a {
416
+ color: #208A1B;
417
  }
418
  .pmpro_admin .pmpro_error a,
419
  .pmpro_admin tr.pmpro_error td {
420
+ color: #CC0000;
421
  }
422
  .pmpro_admin .pmpro_alert a {
423
+ color: #CF8516;
424
  }
425
 
426
  /* notifications */
427
  #pmpro_notifications .pmpro_notification {
428
+ position: relative;
429
  }
430
  #pmpro_notifications .pmpro_notification .pmpro_notification-general {
431
+ background: #FFF;
432
+ border-left: 4px solid #77A02E;
433
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
434
+ margin: 1em 0;
435
+ padding: 15px;
436
  }
437
  #pmpro_notifications .pmpro_notification .pmpro_notification-error {
438
+ background: #FFF;
439
+ border-left: 4px solid #DC3232;
440
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
441
+ margin: 1em 0;
442
+ padding: 15px;
443
  }
444
  #pmpro_notifications .pmpro_notification h3 {
445
+ font-size: 24px;
446
+ font-weight: 400;
447
+ margin: 0;
448
  }
449
  #pmpro_notifications .pmpro_notification p {
450
+ margin: 1em 0 0 0;
451
  }
452
  #pmpro_notifications .pmpro_notification .button {
453
+ margin-right: 5px;
454
  }
455
  #pmpro_notifications .pmpro_notification .pmpro_notification-general h3 {
456
+ color: #77A02E;
457
  }
458
  #pmpro_notifications .pmpro_notification .pmpro_notification-error h3 {
459
+ color: #DC3232;
460
  }
461
 
462
  /* highlighted trs */
463
+ tr.pmpro_message {
464
+ background-image: none;
465
+ }
466
+ tr.pmpro_success {
467
+ background-image: none;
468
+ }
469
+ tr.pmpro_error {
470
+ background-image: none;
471
+ }
472
+ tr.pmpro_alert {
473
+ background-image: none;
474
+ }
475
 
476
  /* discount levels */
477
  .pmpro_discount_levels .pmpro_discount_level {
478
+ border: 1px solid #CCC;
479
+ margin: 15px 0;
480
+ padding: 10px;
481
  }
482
  .pmpro_discount_levels .pmpro_discount_level .pmpro_discount_levels_pricing {
483
+ background: #FFFFFF;
484
+ border: 1px solid #CCC;
485
+ margin: 10px 0 0 0;
486
+ padding: 0px 20px;
487
  }
488
 
489
  /* pagination */
490
+ div.pmpro_pagination {
491
+ padding: 3px;
492
+ margin: 5px 0px 5px 0px;
493
+ font-size: 10px;
494
+ float: right;
495
+ }
496
+ div.pmpro_pagination a {
497
+ padding: 2px 5px 2px 5px;
498
+ margin: 1px;
499
+ border: 1px solid #666;
500
+ text-decoration: none;
501
+ /* no underline */ color: #666;
502
+ background: #EEE;
503
+ }
504
+ div.pmpro_pagination a:hover, div.pmpro_pagination a:active {
505
+ background: #FFF;
506
+ }
507
+ div.pmpro_pagination span.current {
508
+ border: 1px solid #FFF;
509
+ color: #FFF;
510
+ background: #666;
511
+ padding: 2px 5px 2px 5px;
512
+ margin: 1px;
513
+ font-weight: bold;
514
+ }
515
+ div.pmpro_pagination span.disabled {
516
+ padding: 2px 5px 2px 5px;
517
+ margin: 2px;
518
+ border: 1px solid #BBB;
519
+ color: #BBB;
520
+ background: #EFEFEF;
521
+ }
522
 
523
+ p.pmpro_meta_notice {
524
+ font-size: .8em;
525
+ padding-top: 5px;
526
+ border-top: 1px solid #CCC;
527
+ }
528
 
529
  /* add ons */
530
  .memberships_page_pmpro-addons .pmpro_admin .nav-tab-wrapper {
531
+ margin-bottom: 10px;
532
+ }
533
+ .pmpro_admin .widgets-holder-wrap {
534
+ clear: both;
535
+ margin-top: 20px;
536
+ padding: 0 8px;
537
+ }
538
+ .pmpro_admin .widgets-holder-wrap .widget {
539
+ float: left;
540
+ width: 32%;
541
+ margin: 0 1% 1% 0;
542
+ position: relative;
543
+ }
544
+ .pmpro_admin .widgets-holder-wrap p.description {
545
+ padding: 0;
546
+ }
547
+ .pmpro_admin .widgets-holder-wrap .widget-top {
548
+ height: auto;
549
+ cursor: default;
550
+ }
551
+ .pmpro_admin .widgets-holder-wrap .widget-inside {
552
+ display: block;
553
+ height: 130px;
554
+ overflow: hidden;
555
+ }
556
+ .pmpro_admin .widgets-holder-wrap .widget-inside p {
557
+ height: 80px;
558
+ overflow: hidden;
559
+ }
560
+ .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside,
561
+ .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside p {
562
+ height: auto;
563
+ }
564
  .pmpro_admin .widgets-holder-wrap .widget-title { }
565
  .pmpro_admin .widgets-holder-wrap .widget-title h4 { }
566
+ .pmpro_admin .widgets-holder-wrap .widget-title .status-label {
567
+ display: block;
568
+ float: left;
569
+ margin: 0 5px 0 0;
570
+ width: 10px;
571
+ height: 10px;
572
+ overflow: hidden;
573
+ border-radius: 10px;
574
+ -moz-border-radius: 10px;
575
+ -webkit-border-radius: 10px;
576
+ border: 1px solid #DFDFDF;
577
+ text-indent: -9999em;
578
+ }
579
+ .pmpro_admin .widgets-holder-wrap .disabled .widget-title .status-label {
580
+ background: #F00;
581
+ }
582
+ .pmpro_admin .widgets-holder-wrap .enabled .widget-title .status-label {
583
+ background: #0C0;
584
+ }
585
 
586
+ .pmpro_admin .widgets-holder-wrap .widget-title .version {
587
+ position: absolute;
588
+ top: 13px;
589
+ right: 10px;
590
+ }
591
+ .pmpro_admin .widgets-holder-wrap .widget-inside .addon-thumb {
592
+ width: 100px;
593
+ height: 100px;
594
+ float: right;
595
+ margin: 10px 0 0 10px;
596
+ border: 1px solid #DFDFDF;
597
+ background: #FFF;
598
+ padding: 2px;
599
  }
600
+
601
  @media (max-width:900px) {
602
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget {
603
+ float: none;
604
+ width: 100%;
605
+ }
606
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside,
607
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {
608
+ height: auto;
609
+ }
610
  }
611
 
612
  /* license */
613
  .memberships_page_pmpro-license h2 {
614
+ text-align: left;
615
  }
616
  .memberships_page_pmpro-license .pmpro_icon {
617
+ margin-top: 20px;
618
  }
619
 
620
  /* misc */
621
+ .pmpro_lite {
622
+ color: #777;
623
+ }
624
+ .pmpro_pad20 {
625
+ padding: 20px !important;
626
+ }
627
+ .pmpro_red {
628
+ color: #CC0000;
629
+ }
630
+ .pmpro_green {
631
+ color: #00AA00;
632
+ }
633
+ .ssp_description #description {
634
+ width: 100%;
635
+ }
636
+ .top0em {
637
+ margin-top: 0;
638
+ }
639
+ h2.nav-tab-wrapper {
640
+ margin-bottom: 1em;
641
+ }
642
 
643
  /* reports */
644
  .memberships_page_pmpro-reports .pmpro_admin .nav-tab-wrapper {
645
+ margin-bottom: 10px;
646
  }
647
  .pmpro_reports-holder { }
648
+ .pmpro_reports-holder .postbox h2 {
649
+ border-bottom: 1px solid #eee;
650
+ font-size: 14px;
651
+ padding: 8px 12px;
652
+ margin: 0;
653
+ line-height: 1.4;
654
+ }
655
+ .pmpro_clickable {
656
+ cursor: pointer;
657
+ }
658
+ .js .postbox.pmpro_clickable h3 {
659
+ cursor: pointer;
660
+ }
661
  #pmpro_report_login .wp-list-table tbody td,
662
  #pmpro_report_memberships .wp-list-table tbody tr.pmpro_report_tr td,
663
  #pmpro_report_sales .wp-list-table tbody tr.pmpro_report_tr td {
664
+ font-size: 1.2rem;
665
+ font-weight: bold;
666
  }
667
  #pmpro_report_sales .wp-list-table tbody td:last-child {
668
+ text-align: right;
669
  }
670
  .pmpro_report-holder .wp-list-table tbody th button {
671
+ border: none;
672
+ background: 0 0;
673
+ -webkit-border-radius: 0;
674
+ border-radius: 0;
675
+ color: #555;
676
+ cursor: pointer;
677
+ line-height: 1.7;
678
+ padding: 0;
679
  }
680
  button.pmpro_report_th:before {
681
+ bottom: 2px;
682
+ display: inline-block;
683
+ font: 400 20px/1 dashicons;
684
+ left: 0;
685
+ padding: 0 5px 0 0;
686
+ position: relative;
687
+ text-decoration: none;
688
+ vertical-align: bottom;
689
  }
690
  button.pmpro_report_th_opened:before {
691
+ content: "\f140";
692
  }
693
  button.pmpro_report_th_closed:before {
694
+ content: "\f139";
695
  }
696
  .pmpro_report-holder .wp-list-table tbody tr.pmpro_report_tr_sub:last-child th,
697
  .pmpro_report-holder .wp-list-table tbody tr.pmpro_report_tr_sub:last-child td {
698
+ border-bottom: 1px solid #CCC;
699
  }
700
  #pmpro_report_memberships .wp-list-table tbody:nth-child(odd) .pmpro_report_tr th,
701
  #pmpro_report_memberships .wp-list-table tbody:nth-child(odd) .pmpro_report_tr td,
702
  #pmpro_report_sales .wp-list-table tbody:nth-child(odd) .pmpro_report_tr th,
703
  #pmpro_report_sales .wp-list-table tbody:nth-child(odd) .pmpro_report_tr td {
704
+ background-color: #fff;
705
  }
706
  #pmpro_report_memberships .wp-list-table tbody:nth-child(even) .pmpro_report_tr th,
707
  #pmpro_report_memberships .wp-list-table tbody:nth-child(even) .pmpro_report_tr td,
708
  #pmpro_report_sales .wp-list-table tbody:nth-child(even) .pmpro_report_tr th,
709
  #pmpro_report_sales .wp-list-table tbody:nth-child(even) .pmpro_report_tr td {
710
+ background-color: #f9f9f9;
711
  }
712
  .pmpro_report-holder .pmpro_report-button {
713
+ text-align: center;
714
  }
715
 
716
  /* Dashboard */
717
  .pmpro_admin #pmpro_dashboard_welcome h3 {
718
+ font-size: 16px;
719
+ font-weight: 600;
720
+ margin: 1rem 0 0;
721
  }
722
  .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-columns {
723
+ display: grid;
724
+ grid-gap: 2rem;
725
+ grid-template-columns: 1fr 1fr 1fr;
726
  }
727
  .pmpro_admin #pmpro_dashboard_welcome .button-action {
728
+ background: #dd823b;
729
+ border-color: #c36922 #ad5d1e #ad5d1e;
730
+ color: #fff;
731
+ box-shadow: 0 1px 0 #ad5d1e;
732
+ text-shadow: 0 -1px 1px #ad5d1e, 1px 0 1px #ad5d1e, 0 1px 1px #ad5d1e, -1px 0 1px #ad5d1e;
733
  }
734
  .pmpro_admin #pmpro_dashboard_welcome ul li {
735
+ font-size: 14px;
736
+ line-height: 16px;
737
+ padding: 0 0 8px;
738
  }
739
  .pmpro_admin #pmpro_dashboard_welcome ul li a .dashicons {
740
+ color: #82878c;
741
  }
742
  .pmpro_admin p.text-center {
743
+ text-align: center;
744
  }
745
 
746
  /* new installation notices */
747
  .pmpro-new-install {
748
+ padding: 2rem;
749
+ text-align: center;
750
  }
751
  .pmpro-new-install h2 {
752
+ color: #AAA;
753
+ font-size: 2rem;
754
  }
755
  .pmpro-new-install h2:before {
756
+ display: block;
757
+ font: 400 5rem/1.5 dashicons;
758
  }
759
  .memberships_page_pmpro-membershiplevels .pmpro-new-install h2:before {
760
+ content: "\f110";
761
  }
762
  .memberships_page_pmpro-pagesettings .pmpro-new-install h2:before {
763
+ content: "\f133";
764
  }
765
  .memberships_page_pmpro-discountcodes .pmpro-new-install h2:before {
766
+ content: "\f323";
767
  }
768
  #wpbody-content .pmpro-new-install .button-primary,
769
  #wpbody-content .pmpro-new-install .button {
770
+ font-size: 1.2em;
771
+ height: auto;
772
+ margin-bottom: .5rem;
773
+ padding: .75em 1.5em;
774
  }
775
 
776
  @media screen and (max-width: 782px) {
777
+ .pmpro_admin .pmpro_banner {
778
+ display: block;
779
+ }
780
+ .pmpro_admin .nav-tab-wrapper:after {
781
+ clear: both;
782
+ display: block;
783
+ }
784
+ .pmpro_admin .nav-tab-wrapper a {
785
+ margin-top: 5px;
786
+ }
787
+ .dashboard-widgets-wrap .wp-list-table tr:not(.inline-edit-row):not(.no-items) td:not(.check-column),
788
+ .pmpro_report-holder .wp-list-table tr:not(.inline-edit-row):not(.no-items) td:not(.check-column) {
789
+ display: table-cell;
790
+ overflow: hidden;
791
+ word-wrap: break-word;
792
+ }
793
+ #pmpro_dashboard_report_recent_members .wp-list-table .column-username img {
794
+ display: none;
795
+ }
796
+ #pmpro_dashboard_report_recent_orders .wp-list-table {
797
+ border: none;
798
+ }
799
+ #pmpro_dashboard_report_recent_orders .wp-list-table thead,
800
+ #pmpro_dashboard_report_recent_orders br {
801
+ display: none;
802
+ }
803
+ #pmpro_dashboard_report_recent_orders .wp-list-table tr,
804
+ #pmpro_dashboard_report_recent_orders .wp-list-table tr td {
805
+ background: none;
806
+ display: inline-block;
807
+ padding: 0;
808
+ position: relative;
809
+ }
810
+ #pmpro_dashboard_report_recent_orders .wp-list-table tr td:after {
811
+ color: #CCC;
812
+ content: ' |';
813
+ }
814
+ #pmpro_dashboard_report_recent_orders .wp-list-table tr {
815
+ padding: 5px;
816
+ }
817
+ #pmpro_dashboard_report_recent_orders .wp-list-table tr td:last-child:after {
818
+ content: '';
819
+ }
820
+ .pmpro-new-install {
821
+ padding: 0;
822
+ }
823
+ .pmpro-new-install h2 {
824
+ font-size: 1.4rem;
825
+ }
826
+ .pmpro-new-install .button-primary, .pmpro-new-install .button {
827
+ display: block;
828
+ }
829
+ .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-columns {
830
+ display: block;
831
+ }
832
+ .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-column {
833
+ border-bottom: 1px solid #CCC;
834
+ margin-bottom: 2em;
835
+ padding-bottom: 2em;
836
+ }
837
+ .pmpro_admin #pmpro_dashboard_welcome .pmpro-dashboard-welcome-column:last-child {
838
+ border: none;
839
+ margin: 0;
840
+ padding: 0;
841
+ }
842
+ .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr th:first-child:before,
843
+ .memberships_page_pmpro-membershiplevels .pmpro_admin .membership-levels tr td:first-child:before {
844
+ content: '';
845
+ width: 0;
846
+ }
847
  }
css/blocks.editor.css CHANGED
@@ -44,15 +44,23 @@
44
  text-transform: uppercase;
45
  }
46
  .pmpro-block-require-membership-element .components-panel__body {
47
- background: rgba(237, 239, 240, 0.8);
48
  border-bottom: 0;
49
  border-top: none;
 
 
50
  padding-top: 0;
 
 
51
  }
52
  .pmpro-block-require-membership-element .block-editor-inner-blocks {
53
  padding-left: 16px;
54
  padding-right: 16px;
55
  }
 
 
 
 
56
 
57
  /* Checkout Button Block */
58
  .wp-block-pmpro-checkout-button {
44
  text-transform: uppercase;
45
  }
46
  .pmpro-block-require-membership-element .components-panel__body {
47
+ background: rgba(237, 239, 240, 0);
48
  border-bottom: 0;
49
  border-top: none;
50
+ border-left: 5px solid rgba(237, 239, 240, 0.8);
51
+ border-bottom: 5px solid rgba(237, 239, 240, 0.8);
52
  padding-top: 0;
53
+ height: 200px;
54
+ overflow: auto;
55
  }
56
  .pmpro-block-require-membership-element .block-editor-inner-blocks {
57
  padding-left: 16px;
58
  padding-right: 16px;
59
  }
60
+ .pmpro-block-inspector-scrollable {
61
+ height: 200px;
62
+ overflow: auto;
63
+ }
64
 
65
  /* Checkout Button Block */
66
  .wp-block-pmpro-checkout-button {
css/frontend.css CHANGED
@@ -279,6 +279,29 @@ select.pmpro_error {
279
  #pmpro_message_bottom {
280
  margin-bottom: 1em;
281
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  /*---------------------------------------
283
  Membership Checkout
284
  ---------------------------------------*/
@@ -306,6 +329,14 @@ select.pmpro_error {
306
  -ms-grid-columns: 3fr 1em 1fr;
307
  grid-template-columns: 3fr 1fr;
308
  grid-column-gap: 1em;
 
 
 
 
 
 
 
 
309
  }
310
  #pmpro_license {
311
  background: #FFF;
@@ -318,12 +349,23 @@ select.pmpro_error {
318
  }
319
 
320
  /* Stripe gateway Membership Checkout specific styles. */
 
 
 
 
 
 
 
 
 
 
 
321
  .pmpro_checkout_gateway-stripe form.pmpro_form #pmpro_payment_information_fields .pmpro_checkout-fields,
322
  .pmpro_billing_gateway-stripe form.pmpro_form #pmpro_payment_information_fields .pmpro_checkout-fields {
323
  display: -ms-grid;
324
  display: grid;
325
  -ms-grid-rows: auto 1em auto 1em auto;
326
- grid-template-areas:
327
  "AccountNumber AccountNumber"
328
  "Expiry CVV"
329
  "DiscountCode DiscountCode";
@@ -394,6 +436,17 @@ select.pmpro_error {
394
  padding-right: 10px;
395
  }
396
 
 
 
 
 
 
 
 
 
 
 
 
397
  /*---------------------------------------
398
  Membership Account
399
  ---------------------------------------*/
@@ -631,6 +684,16 @@ li.pmpro_more {
631
  /*---------------------------------------
632
  Responsive Styles
633
  ---------------------------------------*/
 
 
 
 
 
 
 
 
 
 
634
  @media (max-width:768px) {
635
  #pmpro_account #pmpro_account-membership .pmpro_table td:nth-child(1),
636
  #pmpro_levels_table td:nth-child(1) {
279
  #pmpro_message_bottom {
280
  margin-bottom: 1em;
281
  }
282
+
283
+ /*---------------------------------------
284
+ Display Price Parts
285
+ ---------------------------------------*/
286
+ .pmpro_price_part_span {
287
+ display: block;
288
+ }
289
+ .pmpro_price_part-total {
290
+ border-top: 1px solid #CCC;
291
+ margin-top: 5px;
292
+ padding-top: 5px;
293
+ }
294
+ .pmpro_price_part_label:after {
295
+ content: ": ";
296
+ }
297
+ .pmpro_price_part_sub {
298
+ font-size: 75%;
299
+ }
300
+ span.pmpro_price_part_sub:before {
301
+ content: "\2022";
302
+ padding-right: 5px;
303
+ }
304
+
305
  /*---------------------------------------
306
  Membership Checkout
307
  ---------------------------------------*/
329
  -ms-grid-columns: 3fr 1em 1fr;
330
  grid-template-columns: 3fr 1fr;
331
  grid-column-gap: 1em;
332
+ grid-template-areas:
333
+ "leftcol rightcol";
334
+ }
335
+ #pmpro_payment_information_fields .pmpro_checkout-fields-display-seal .pmpro_checkout-fields-leftcol {
336
+ grid-area: leftcol;
337
+ }
338
+ #pmpro_payment_information_fields .pmpro_checkout-fields-display-seal .pmpro_checkout-fields-rightcol {
339
+ grid-area: rightcol;
340
  }
341
  #pmpro_license {
342
  background: #FFF;
349
  }
350
 
351
  /* Stripe gateway Membership Checkout specific styles. */
352
+ .pmpro_checkout_gateway-stripe form.pmpro_form #pmpro_payment_information_fields .pmpro_checkout-field-payment-request-button {
353
+ grid-area: paymentrequestbutton;
354
+ }
355
+ .pmpro_checkout_gateway-stripe form.pmpro_form #pmpro_payment_information_fields .pmpro_checkout-field-payment-request-button h4 {
356
+ margin-top: 1em;
357
+ }
358
+ .pmpro_checkout_gateway-stripe form.pmpro_form #pmpro_payment_information_fields .pmpro_checkout-fields-display-seal {
359
+ grid-template-areas:
360
+ "paymentrequestbutton rightcol"
361
+ "leftcol rightcol";
362
+ }
363
  .pmpro_checkout_gateway-stripe form.pmpro_form #pmpro_payment_information_fields .pmpro_checkout-fields,
364
  .pmpro_billing_gateway-stripe form.pmpro_form #pmpro_payment_information_fields .pmpro_checkout-fields {
365
  display: -ms-grid;
366
  display: grid;
367
  -ms-grid-rows: auto 1em auto 1em auto;
368
+ grid-template-areas:
369
  "AccountNumber AccountNumber"
370
  "Expiry CVV"
371
  "DiscountCode DiscountCode";
436
  padding-right: 10px;
437
  }
438
 
439
+ .pmpro_invoice-field-billing_name,
440
+ .pmpro_invoice-field-billing_street,
441
+ .pmpro_invoice-field-billing_country,
442
+ .pmpro_invoice-field-billing_phone {
443
+ display: block;
444
+ }
445
+
446
+ .pmpro_invoice-field-billing_city:after {
447
+ content: ',';
448
+ }
449
+
450
  /*---------------------------------------
451
  Membership Account
452
  ---------------------------------------*/
684
  /*---------------------------------------
685
  Responsive Styles
686
  ---------------------------------------*/
687
+ @media only screen and (min-width: 1160px) {
688
+ .pmpro_checkout_gateway-stripe form.pmpro_form #pmpro_payment_information_fields #payment-request-button {
689
+ max-width: 50%;
690
+ }
691
+ }
692
+ @media only screen and (min-width: 960px) and (max-width: 1160px) {
693
+ .pmpro_checkout_gateway-stripe form.pmpro_form #pmpro_payment_information_fields #payment-request-button {
694
+ max-width: 50%;
695
+ }
696
+ }
697
  @media (max-width:768px) {
698
  #pmpro_account #pmpro_account-membership .pmpro_table td:nth-child(1),
699
  #pmpro_levels_table td:nth-child(1) {
email/admin_change.html DELETED
@@ -1,7 +0,0 @@
1
- <p>An administrator at !!sitename!! has changed your membership level.</p>
2
-
3
- <p>!!membership_change!!.</p>
4
-
5
- <p>If you did not request this membership change and would like more information please contact us at !!siteemail!!</p>
6
-
7
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
email/admin_change_admin.html DELETED
@@ -1,5 +0,0 @@
1
- <p>An administrator at !!sitename!! has changed a membership level for !!name!!.</p>
2
-
3
- <p>!!membership_change!!.</p>
4
-
5
- <p>Log in to your WordPress admin here: !!login_link!!</p>
 
 
 
 
 
email/billable_invoice.html DELETED
@@ -1,6 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Below is your invoice for order #: !!order_code!!</p>
2
-
3
- !!invoice!!
4
-
5
- <p>Log in to your membership account here: !!login_link!!</p>
6
- <p>To view an online version of this invoice, click here: !!invoice_link!!</p>
 
 
 
 
 
 
email/billing.html DELETED
@@ -1,16 +0,0 @@
1
- <p>Your billing information at !!sitename!! has been changed.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>
5
- Billing Information:<br />
6
- !!billing_address!!
7
- </p>
8
-
9
- <p>
10
- !!cardtype!!: !!accountnumber!!<br />
11
- Expires: !!expirationmonth!!/!!expirationyear!!
12
- </p>
13
-
14
- <p>If you did not request a billing information change please contact us at !!siteemail!!</p>
15
-
16
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/billing_admin.html DELETED
@@ -1,17 +0,0 @@
1
- <p>The billing information for !!display_name!! at !!sitename!! has been changed.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>
5
- Billing Information:<br />
6
- !!billing_name!!<br />
7
- !!billing_street!!<br />
8
- !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
9
- !!billing_phone!!
10
- </p>
11
-
12
- <p>
13
- !!cardtype!!: !!accountnumber!!<br />
14
- Expires: !!expirationmonth!!/!!expirationyear!!
15
- </p>
16
-
17
- <p>Log in to your WordPress dashboard here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/billing_failure.html DELETED
@@ -1,3 +0,0 @@
1
- <p>The current subscription payment for your !!sitename!! membership has failed. <strong>Please click the following link to log in and update your billing information to avoid account suspension. !!login_link!!</strong></p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
 
 
 
email/billing_failure_admin.html DELETED
@@ -1,6 +0,0 @@
1
- <p>The subscription payment for !!user_login!! at !!sitename!! has failed.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>Membership Level: !!membership_level_name!!</p>
5
-
6
- <p>Log in to your WordPress admin here: !!login_link!!</p>
 
 
 
 
 
 
email/cancel.html DELETED
@@ -1,6 +0,0 @@
1
- <p>Your membership at !!sitename!! has been cancelled.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>Membership Level: !!membership_level_name!!</p>
5
-
6
- <p>If you did not request this cancellation and would like more information please contact us at !!siteemail!!</p>
 
 
 
 
 
 
email/cancel_admin.html DELETED
@@ -1,8 +0,0 @@
1
- <p>The membership for !!user_login!! at !!sitename!! has been cancelled.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>Membership Level: !!membership_level_name!!</p>
5
- <p>Start Date: !!startdate!!</p>
6
- <p>Cancellation Date: !!enddate!!</p>
7
-
8
- <p>Log in to your WordPress admin here: !!login_link!!</p>
 
 
 
 
 
 
 
 
email/checkout_check.html DELETED
@@ -1,19 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
-
3
- !!membership_level_confirmation_message!!
4
-
5
- !!instructions!!
6
-
7
- <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
8
-
9
- <p>Account: !!display_name!! (!!user_email!!)</p>
10
- <p>Membership Level: !!membership_level_name!!</p>
11
- <p>Membership Fee: !!membership_cost!!</p>
12
- !!membership_expiration!! !!discount_code!!
13
-
14
- <p>
15
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
16
- Total Billed: !!invoice_total!!
17
- </p>
18
-
19
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_check_admin.html DELETED
@@ -1,17 +0,0 @@
1
- <p>There was a new member checkout at !!sitename!!.</p>
2
-
3
- <p><strong>They have chosen to pay by check.</strong></p>
4
-
5
- <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
6
-
7
- <p>Account: !!display_name!! (!!user_email!!)</p>
8
- <p>Membership Level: !!membership_level_name!!</p>
9
- <p>Membership Fee: !!membership_cost!!</p>
10
- !!membership_expiration!! !!discount_code!!
11
-
12
- <p>
13
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
14
- Total Billed: $!!invoice_total!!
15
- </p>
16
-
17
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_express.html DELETED
@@ -1,15 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
- !!membership_level_confirmation_message!!
3
- <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
4
-
5
- <p>Account: !!display_name!! (!!user_email!!)</p>
6
- <p>Membership Level: !!membership_level_name!!</p>
7
- <p>Membership Fee: !!membership_cost!!</p>
8
- !!membership_expiration!! !!discount_code!!
9
-
10
- <p>
11
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
12
- Total Billed: !!invoice_total!!
13
- </p>
14
-
15
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_express_admin.html DELETED
@@ -1,14 +0,0 @@
1
- <p>There was a new member checkout at !!sitename!!.</p>
2
- <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
-
4
- <p>Account: !!display_name!! (!!user_email!!)</p>
5
- <p>Membership Level: !!membership_level_name!!</p>
6
- <p>Membership Fee: !!membership_cost!!</p>
7
- !!membership_expiration!! !!discount_code!!
8
-
9
- <p>
10
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
- Total Billed: !!invoice_total!!
12
- </p>
13
-
14
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_free.html DELETED
@@ -1,9 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
- !!membership_level_confirmation_message!!
3
- <p>Below are details about your membership account.</p>
4
-
5
- <p>Account: !!display_name!! (!!user_email!!)</p>
6
- <p>Membership Level: !!membership_level_name!!</p>
7
- !!membership_expiration!! !!discount_code!!
8
-
9
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
email/checkout_free_admin.html DELETED
@@ -1,8 +0,0 @@
1
- <p>There was a new member checkout at !!sitename!!.</p>
2
- <p>Below are details about the new membership account.</p>
3
-
4
- <p>Account: !!display_name!! (!!user_email!!)</p>
5
- <p>Membership Level: !!membership_level_name!!</p>
6
- !!membership_expiration!! !!discount_code!!
7
-
8
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
email/checkout_freetrial.html DELETED
@@ -1,20 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
- !!membership_level_confirmation_message!!
3
- <p>Below are details about your membership account.</p>
4
-
5
- <p>Account: !!display_name!! (!!user_email!!)</p>
6
- <p>Membership Level: !!membership_level_name!!</p>
7
- <p>Membership Fee: !!membership_cost!!</p>
8
- !!membership_expiration!! !!discount_code!!
9
-
10
- <p>
11
- Billing Information on File:<br />
12
- !!billing_address!!
13
- </p>
14
-
15
- <p>
16
- !!cardtype!!: !!accountnumber!!<br />
17
- Expires: !!expirationmonth!!/!!expirationyear!!
18
- </p>
19
-
20
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_freetrial_admin.html DELETED
@@ -1,19 +0,0 @@
1
- <p>There was a new member checkout at !!sitename!!.</p>
2
- <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
-
4
- <p>Account: !!display_name!! (!!user_email!!)</p>
5
- <p>Membership Level: !!membership_level_name!!</p>
6
- <p>Membership Fee: !!membership_cost!!</p>
7
- !!membership_expiration!! !!discount_code!!
8
-
9
- <p>
10
- Billing Information on File:<br />
11
- !!billing_address!!
12
- </p>
13
-
14
- <p>
15
- !!cardtype!!: !!accountnumber!!<br />
16
- Expires: !!expirationmonth!!/!!expirationyear!!
17
- </p>
18
-
19
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_paid.html DELETED
@@ -1,24 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
- !!membership_level_confirmation_message!!
3
- <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
4
-
5
- <p>Account: !!display_name!! (!!user_email!!)</p>
6
- <p>Membership Level: !!membership_level_name!!</p>
7
- <p>Membership Fee: !!membership_cost!!</p>
8
- !!membership_expiration!! !!discount_code!!
9
-
10
- <p>
11
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
12
- Total Billed: !!invoice_total!!
13
- </p>
14
- <p>
15
- Billing Information:<br />
16
- !!billing_address!!
17
- </p>
18
-
19
- <p>
20
- !!cardtype!!: !!accountnumber!!<br />
21
- Expires: !!expirationmonth!!/!!expirationyear!!
22
- </p>
23
-
24
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_paid_admin.html DELETED
@@ -1,23 +0,0 @@
1
- <p>There was a new member checkout at !!sitename!!.</p>
2
- <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
-
4
- <p>Account: !!display_name!! (!!user_email!!)</p>
5
- <p>Membership Level: !!membership_level_name!!</p>
6
- <p>Membership Fee: !!membership_cost!!</p>
7
- !!membership_expiration!! !!discount_code!!
8
-
9
- <p>
10
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
- Total Billed: !!invoice_total!!
12
- </p>
13
- <p>
14
- Billing Information:<br />
15
- !!billing_address!!
16
- </p>
17
-
18
- <p>
19
- !!cardtype!!: !!accountnumber!!<br />
20
- Expires: !!expirationmonth!!/!!expirationyear!!
21
- </p>
22
-
23
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_trial.html DELETED
@@ -1,24 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
- !!membership_level_confirmation_message!!
3
- <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
4
-
5
- <p>Account: !!display_name!! (!!user_email!!)</p>
6
- <p>Membership Level: !!membership_level_name!!</p>
7
- <p>Membership Fee: !!membership_cost!!</p>
8
- !!membership_expiration!! !!discount_code!!
9
-
10
- <p>
11
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
12
- Total Billed: !!invoice_total!!
13
- </p>
14
- <p>
15
- Billing Information:<br />
16
- !!billing_address!!
17
- </p>
18
-
19
- <p>
20
- !!cardtype!!: !!accountnumber!!<br />
21
- Expires: !!expirationmonth!!/!!expirationyear!!
22
- </p>
23
-
24
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/checkout_trial_admin.html DELETED
@@ -1,23 +0,0 @@
1
- <p>There was a new member checkout at !!sitename!!.</p>
2
- <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
-
4
- <p>Account: !!display_name!! (!!user_email!!)</p>
5
- <p>Membership Level: !!membership_level_name!!</p>
6
- <p>Membership Fee: !!membership_cost!!</p>
7
- !!membership_expiration!! !!discount_code!!
8
-
9
- <p>
10
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
- Total Billed: !!invoice_total!!
12
- </p>
13
- <p>
14
- Billing Information:<br />
15
- !!billing_address!!
16
- </p>
17
-
18
- <p>
19
- !!cardtype!!: !!accountnumber!!<br />
20
- Expires: !!expirationmonth!!/!!expirationyear!!
21
- </p>
22
-
23
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/credit_card_expiring.html DELETED
@@ -1,13 +0,0 @@
1
- <p>The payment method used for your membership at !!sitename!! will expire soon. <strong>Please click the following link to log in and update your billing information to avoid account suspension. !!login_link!!</strong></p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>The most recent account information we have on file is:</p>
5
-
6
- <p>!!billing_name!!</br />
7
- !!billing_address!!
8
- </p>
9
-
10
- <p>
11
- !!cardtype!!: !!accountnumber!!<br />
12
- Expires: !!expirationmonth!!/!!expirationyear!!
13
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
email/default.html DELETED
@@ -1 +0,0 @@
1
- !!body!!
 
email/footer.html DELETED
@@ -1,4 +0,0 @@
1
- <p>
2
- Respectfully,<br />
3
- !!sitename!!
4
- </p>
 
 
 
 
email/header.html DELETED
@@ -1 +0,0 @@
1
- <p>Dear !!name!!,</p>
 
email/invoice.html DELETED
@@ -1,19 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Below is a receipt for your most recent membership invoice.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>
5
- Invoice #!!invoice_id!! on !!invoice_date!!<br />
6
- Total Billed: !!invoice_total!!
7
- </p>
8
- <p>
9
- Billing Information:<br />
10
- !!billing_address!!
11
- </p>
12
-
13
- <p>
14
- !!cardtype!!: !!accountnumber!!<br />
15
- Expires: !!expirationmonth!!/!!expirationyear!!
16
- </p>
17
-
18
- <p>Log in to your membership account here: !!login_link!!</p>
19
- <p>To view an online version of this invoice, click here: !!invoice_link!!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
email/membership_expired.html DELETED
@@ -1,7 +0,0 @@
1
- <p>Your membership at !!sitename!! has ended.</p>
2
-
3
- <p>Thank you for your support.</p>
4
-
5
- <p>View our current membership offerings here: !!levels_link!!</p>
6
-
7
- <p>Log in to manage your account here: !!login_link!!</p>
 
 
 
 
 
 
 
email/membership_expiring.html DELETED
@@ -1,6 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. This is just a reminder that your membership will end on !!enddate!!.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>Membership Level: !!membership_level_name!!</p>
5
-
6
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
email/payment_action.html DELETED
@@ -1,4 +0,0 @@
1
- <p>Customer authentication is required to finish setting up your subscription at !!sitename!!.</p>
2
-
3
- <p>Please complete the verification steps issued by your payment provider at the following link:</p>
4
- <p>!!invoice_url!!</p>
 
 
 
 
email/payment_action_admin.html DELETED
@@ -1,7 +0,0 @@
1
- <p>A payment at !!sitename!! for !!user_login!! requires additional authentication customer authentication to complete.</p>
2
- <p>Below is a copy of the email we sent to !!user_email!! to notify them that they need to complete their payment:</p>
3
-
4
- <p>Customer authentication is required to finish setting up your subscription at !!sitename!!.</p>
5
-
6
- <p>Please complete the verification steps issued by your payment provider at the following link:</p>
7
- <p>!!invoice_url!!</p>
 
 
 
 
 
 
 
email/trial_ending.html DELETED
@@ -1,8 +0,0 @@
1
- <p>Thank you for your membership to !!sitename!!. Your trial period is ending on !!trial_end!!.</p>
2
-
3
- <p>Account: !!display_name!! (!!user_email!!)</p>
4
- <p>Membership Level: !!membership_level_name!!</p>
5
-
6
- <p>Your fee will be changing from !!trial_amount!! to !!billing_amount!! every !!cycle_number!! !!cycle_period!!(s).</p>
7
-
8
- <p>Log in to your membership account here: !!login_link!!</p>
 
 
 
 
 
 
 
 
includes/addons.php CHANGED
@@ -130,7 +130,7 @@ function pmpro_update_plugins_filter( $value ) {
130
  }
131
 
132
  // compare versions
133
- if ( ! empty( $addon['License'] ) && version_compare( $plugin_data['Version'], $addon['Version'], '<' ) ) {
134
  $value->response[ $plugin_file ] = pmpro_getPluginAPIObjectFromAddon( $addon );
135
  $value->response[ $plugin_file ]->new_version = $addon['Version'];
136
  } else {
@@ -306,7 +306,7 @@ function pmpro_admin_init_updating_plugins() {
306
 
307
  $slug = str_replace( '.php', '', basename( $plugin ) );
308
  $addon = pmpro_getAddonBySlug( $slug );
309
- if ( ! empty( $addon ) && ! pmpro_license_isValid( null, 'plus' ) ) {
310
  require_once( ABSPATH . 'wp-admin/admin-header.php' );
311
 
312
  echo '<div class="wrap"><h2>' . __( 'Update Plugin' ) . '</h2>';
@@ -332,7 +332,7 @@ function pmpro_admin_init_updating_plugins() {
332
 
333
  $slug = str_replace( '.php', '', basename( $plugin ) );
334
  $addon = pmpro_getAddonBySlug( $slug );
335
- if ( ! empty( $addon ) && ! pmpro_license_isValid( null, 'plus' ) ) {
336
  $msg = __( 'You must enter a valid PMPro Plus License Key under Settings > PMPro License to update this add on.', 'paid-memberships-pro' );
337
  echo '<div class="error"><p>' . $msg . '</p></div>';
338
 
130
  }
131
 
132
  // compare versions
133
+ if ( version_compare( $plugin_data['Version'], $addon['Version'], '<' ) ) {
134
  $value->response[ $plugin_file ] = pmpro_getPluginAPIObjectFromAddon( $addon );
135
  $value->response[ $plugin_file ]->new_version = $addon['Version'];
136
  } else {
306
 
307
  $slug = str_replace( '.php', '', basename( $plugin ) );
308
  $addon = pmpro_getAddonBySlug( $slug );
309
+ if ( ! empty( $addon ) && $addon->License == 'plus' && ! pmpro_license_isValid( null, 'plus' ) ) {
310
  require_once( ABSPATH . 'wp-admin/admin-header.php' );
311
 
312
  echo '<div class="wrap"><h2>' . __( 'Update Plugin' ) . '</h2>';
332
 
333
  $slug = str_replace( '.php', '', basename( $plugin ) );
334
  $addon = pmpro_getAddonBySlug( $slug );
335
+ if ( ! empty( $addon ) && $addon->License == 'plus' && ! pmpro_license_isValid( null, 'plus' ) ) {
336
  $msg = __( 'You must enter a valid PMPro Plus License Key under Settings > PMPro License to update this add on.', 'paid-memberships-pro' );
337
  echo '<div class="error"><p>' . $msg . '</p></div>';
338
 
includes/admin.php CHANGED
@@ -45,21 +45,41 @@ add_action( 'admin_init', 'pmpro_block_dashboard_redirect', 9 );
45
  * @since 2.3
46
  */
47
  function pmpro_block_dashboard() {
48
- global $current_user;
49
 
50
  $block_dashboard = pmpro_getOption( 'block_dashboard' );
51
 
52
- if ( ! wp_doing_ajax()
53
- && ! empty( $block_dashboard )
54
- && ! current_user_can( 'manage_options' )
55
- && ! current_user_can( 'edit_users' )
56
- && ! current_user_can( 'edit_posts' )
57
- && in_array( 'subscriber', (array) $current_user->roles ) ) {
 
 
 
58
  $block = true;
59
  } else {
60
  $block = false;
61
- }
62
  $block = apply_filters( 'pmpro_block_dashboard', $block );
63
 
64
- return $block;
 
 
 
 
 
65
  }
 
 
 
 
 
 
 
 
 
 
 
 
45
  * @since 2.3
46
  */
47
  function pmpro_block_dashboard() {
48
+ global $current_user, $pagenow;
49
 
50
  $block_dashboard = pmpro_getOption( 'block_dashboard' );
51
 
52
+ if (
53
+ ! wp_doing_ajax()
54
+ && 'admin-post.php' !== $pagenow
55
+ && ! empty( $block_dashboard )
56
+ && ! current_user_can( 'manage_options' )
57
+ && ! current_user_can( 'edit_users' )
58
+ && ! current_user_can( 'edit_posts' )
59
+ && in_array( 'subscriber', (array) $current_user->roles )
60
+ ) {
61
  $block = true;
62
  } else {
63
  $block = false;
64
+ }
65
  $block = apply_filters( 'pmpro_block_dashboard', $block );
66
 
67
+ /**
68
+ * Allow filtering whether to block Dashboard access.
69
+ *
70
+ * @param bool $block Whether to block Dashboard access.
71
+ */
72
+ return apply_filters( 'pmpro_block_dashboard', $block );
73
  }
74
+
75
+ /**
76
+ * Initialize our Site Health integration and add hooks.
77
+ *
78
+ * @since 2.6.2
79
+ */
80
+ function pmpro_init_site_health_integration() {
81
+ $site_health = PMPro_Site_Health::init();
82
+ $site_health->hook();
83
+ }
84
+
85
+ add_action( 'admin_init', 'pmpro_init_site_health_integration' );
includes/adminpages.php CHANGED
@@ -10,6 +10,7 @@ function pmpro_getPMProCaps() {
10
  'pmpro_pagesettings',
11
  'pmpro_paymentsettings',
12
  'pmpro_emailsettings',
 
13
  'pmpro_advancedsettings',
14
  'pmpro_addons',
15
  'pmpro_memberslist',
@@ -68,6 +69,7 @@ function pmpro_add_pages() {
68
  add_submenu_page( 'admin.php', __( 'Page Settings', 'paid-memberships-pro' ), __( 'Page Settings', 'paid-memberships-pro' ), 'pmpro_pagesettings', 'pmpro-pagesettings', 'pmpro_pagesettings' );
69
  add_submenu_page( 'admin.php', __( 'Payment Settings', 'paid-memberships-pro' ), __( 'Payment Settings', 'paid-memberships-pro' ), 'pmpro_paymentsettings', 'pmpro-paymentsettings', 'pmpro_paymentsettings' );
70
  add_submenu_page( 'admin.php', __( 'Email Settings', 'paid-memberships-pro' ), __( 'Email Settings', 'paid-memberships-pro' ), 'pmpro_emailsettings', 'pmpro-emailsettings', 'pmpro_emailsettings' );
 
71
  add_submenu_page( 'admin.php', __( 'Advanced Settings', 'paid-memberships-pro' ), __( 'Advanced Settings', 'paid-memberships-pro' ), 'pmpro_advancedsettings', 'pmpro-advancedsettings', 'pmpro_advancedsettings' );
72
 
73
  add_action( 'load-' . $list_table_hook, 'pmpro_list_table_screen_options' );
@@ -91,6 +93,7 @@ function pmpro_parent_file( $parent_file ) {
91
  'pmpro-pagesettings',
92
  'pmpro-paymentsettings',
93
  'pmpro-emailsettings',
 
94
  'pmpro-advancedsettings',
95
  );
96
 
@@ -130,7 +133,7 @@ function pmpro_admin_bar_menu() {
130
  array(
131
  'id' => 'paid-memberships-pro',
132
  'title' => __( '<span class="ab-icon"></span>Memberships', 'paid-memberships-pro' ),
133
- 'href' => get_admin_url( NULL, '/admin.php?page=' . $top_menu_page )
134
  )
135
  );
136
 
@@ -141,7 +144,7 @@ function pmpro_admin_bar_menu() {
141
  'id' => 'pmpro-dashboard',
142
  'parent' => 'paid-memberships-pro',
143
  'title' => __( 'Dashboard', 'paid-memberships-pro' ),
144
- 'href' => get_admin_url( NULL, '/admin.php?page=pmpro-dashboard' )
145
  )
146
  );
147
  }
@@ -153,7 +156,7 @@ function pmpro_admin_bar_menu() {
153
  'id' => 'pmpro-members-list',
154
  'parent' => 'paid-memberships-pro',
155
  'title' => __( 'Members', 'paid-memberships-pro' ),
156
- 'href' => get_admin_url( NULL, '/admin.php?page=pmpro-memberslist' )
157
  )
158
  );
159
  }
@@ -165,7 +168,7 @@ function pmpro_admin_bar_menu() {
165
  'id' => 'pmpro-orders',
166
  'parent' => 'paid-memberships-pro',
167
  'title' => __( 'Orders', 'paid-memberships-pro' ),
168
- 'href' => get_admin_url( NULL, '/admin.php?page=pmpro-orders' )
169
  )
170
  );
171
  }
@@ -177,7 +180,7 @@ function pmpro_admin_bar_menu() {
177
  'id' => 'pmpro-reports',
178
  'parent' => 'paid-memberships-pro',
179
  'title' => __( 'Reports', 'paid-memberships-pro' ),
180
- 'href' => get_admin_url( NULL, '/admin.php?page=pmpro-reports' )
181
  )
182
  );
183
  }
@@ -189,7 +192,7 @@ function pmpro_admin_bar_menu() {
189
  'id' => 'pmpro-membership-levels',
190
  'parent' => 'paid-memberships-pro',
191
  'title' => __( 'Settings', 'paid-memberships-pro' ),
192
- 'href' => get_admin_url( NULL, '/admin.php?page=pmpro-membershiplevels' )
193
  )
194
  );
195
  }
@@ -201,7 +204,7 @@ function pmpro_admin_bar_menu() {
201
  'id' => 'pmpro-addons',
202
  'parent' => 'paid-memberships-pro',
203
  'title' => __( 'Add Ons', 'paid-memberships-pro' ),
204
- 'href' => get_admin_url( NULL, '/admin.php?page=pmpro-addons' )
205
  )
206
  );
207
  }
@@ -220,7 +223,7 @@ function pmpro_admin_bar_menu() {
220
  'id' => 'pmpro-license',
221
  'parent' => 'paid-memberships-pro',
222
  'title' => __( '<span style="color: ' . $span_color . '; line-height: 26px;">License</span>', 'paid-memberships-pro' ),
223
- 'href' => get_admin_url( NULL, '/admin.php?page=pmpro-license' )
224
  )
225
  );
226
  }
@@ -272,6 +275,10 @@ function pmpro_emailsettings() {
272
  require_once( PMPRO_DIR . '/adminpages/emailsettings.php' );
273
  }
274
 
 
 
 
 
275
  function pmpro_advancedsettings() {
276
  require_once( PMPRO_DIR . '/adminpages/advancedsettings.php' );
277
  }
@@ -392,7 +399,7 @@ function pmpro_add_action_links( $links ) {
392
  }
393
 
394
  $new_links = array(
395
- '<a href="' . get_admin_url( NULL, 'admin.php?page=' . $top_menu_page ) . '">Settings</a>',
396
  );
397
  return array_merge( $new_links, $links );
398
  }
10
  'pmpro_pagesettings',
11
  'pmpro_paymentsettings',
12
  'pmpro_emailsettings',
13
+ 'pmpro_emailtemplates',
14
  'pmpro_advancedsettings',
15
  'pmpro_addons',
16
  'pmpro_memberslist',
69
  add_submenu_page( 'admin.php', __( 'Page Settings', 'paid-memberships-pro' ), __( 'Page Settings', 'paid-memberships-pro' ), 'pmpro_pagesettings', 'pmpro-pagesettings', 'pmpro_pagesettings' );
70
  add_submenu_page( 'admin.php', __( 'Payment Settings', 'paid-memberships-pro' ), __( 'Payment Settings', 'paid-memberships-pro' ), 'pmpro_paymentsettings', 'pmpro-paymentsettings', 'pmpro_paymentsettings' );
71
  add_submenu_page( 'admin.php', __( 'Email Settings', 'paid-memberships-pro' ), __( 'Email Settings', 'paid-memberships-pro' ), 'pmpro_emailsettings', 'pmpro-emailsettings', 'pmpro_emailsettings' );
72
+ add_submenu_page( 'admin.php', __( 'Email Templates', 'paid-memberships-pro' ), __( 'Email Templates', 'paid-memberships-pro' ), 'pmpro_emailtemplates', 'pmpro-emailtemplates', 'pmpro_emailtemplates' );
73
  add_submenu_page( 'admin.php', __( 'Advanced Settings', 'paid-memberships-pro' ), __( 'Advanced Settings', 'paid-memberships-pro' ), 'pmpro_advancedsettings', 'pmpro-advancedsettings', 'pmpro_advancedsettings' );
74
 
75
  add_action( 'load-' . $list_table_hook, 'pmpro_list_table_screen_options' );
93
  'pmpro-pagesettings',
94
  'pmpro-paymentsettings',
95
  'pmpro-emailsettings',
96
+ 'pmpro-emailtemplates',
97
  'pmpro-advancedsettings',
98
  );
99
 
133
  array(
134
  'id' => 'paid-memberships-pro',
135
  'title' => __( '<span class="ab-icon"></span>Memberships', 'paid-memberships-pro' ),
136
+ 'href' => admin_url( '/admin.php?page=' . $top_menu_page )
137
  )
138
  );
139
 
144
  'id' => 'pmpro-dashboard',
145
  'parent' => 'paid-memberships-pro',
146
  'title' => __( 'Dashboard', 'paid-memberships-pro' ),
147
+ 'href' => admin_url( '/admin.php?page=pmpro-dashboard' )
148
  )
149
  );
150
  }
156
  'id' => 'pmpro-members-list',
157
  'parent' => 'paid-memberships-pro',
158
  'title' => __( 'Members', 'paid-memberships-pro' ),
159
+ 'href' => admin_url( '/admin.php?page=pmpro-memberslist' )
160
  )
161
  );
162
  }
168
  'id' => 'pmpro-orders',
169
  'parent' => 'paid-memberships-pro',
170
  'title' => __( 'Orders', 'paid-memberships-pro' ),
171
+ 'href' => admin_url( '/admin.php?page=pmpro-orders' )
172
  )
173
  );
174
  }
180
  'id' => 'pmpro-reports',
181
  'parent' => 'paid-memberships-pro',
182
  'title' => __( 'Reports', 'paid-memberships-pro' ),
183
+ 'href' => admin_url( '/admin.php?page=pmpro-reports' )
184
  )
185
  );
186
  }
192
  'id' => 'pmpro-membership-levels',
193
  'parent' => 'paid-memberships-pro',
194
  'title' => __( 'Settings', 'paid-memberships-pro' ),
195
+ 'href' => admin_url( '/admin.php?page=pmpro-membershiplevels' )
196
  )
197
  );
198
  }
204
  'id' => 'pmpro-addons',
205
  'parent' => 'paid-memberships-pro',
206
  'title' => __( 'Add Ons', 'paid-memberships-pro' ),
207
+ 'href' => admin_url( '/admin.php?page=pmpro-addons' )
208
  )
209
  );
210
  }
223
  'id' => 'pmpro-license',
224
  'parent' => 'paid-memberships-pro',
225
  'title' => __( '<span style="color: ' . $span_color . '; line-height: 26px;">License</span>', 'paid-memberships-pro' ),
226
+ 'href' => admin_url( '/admin.php?page=pmpro-license' )
227
  )
228
  );
229
  }
275
  require_once( PMPRO_DIR . '/adminpages/emailsettings.php' );
276
  }
277
 
278
+ function pmpro_emailtemplates() {
279
+ require_once( PMPRO_DIR . '/adminpages/emailtemplates.php' );
280
+ }
281
+
282
  function pmpro_advancedsettings() {
283
  require_once( PMPRO_DIR . '/adminpages/advancedsettings.php' );
284
  }
399
  }
400
 
401
  $new_links = array(
402
+ '<a href="' . admin_url( 'admin.php?page=' . $top_menu_page ) . '">Settings</a>',
403
  );
404
  return array_merge( $new_links, $links );
405
  }
includes/capabilities.php CHANGED
@@ -57,6 +57,7 @@ function pmpro_get_capability_defs($role)
57
  'pmpro_pagesettings',
58
  'pmpro_paymentsettings',
59
  'pmpro_emailsettings',
 
60
  'pmpro_advancedsettings',
61
  'pmpro_addons',
62
  'pmpro_memberslist',
57
  'pmpro_pagesettings',
58
  'pmpro_paymentsettings',
59
  'pmpro_emailsettings',
60
+ 'pmpro_emailtemplates',
61
  'pmpro_advancedsettings',
62
  'pmpro_addons',
63
  'pmpro_memberslist',
includes/compatibility.php CHANGED
@@ -4,46 +4,113 @@
4
  * Check if certain plugins or themes are installed and activated
5
  * and if found dynamically load the relevant /includes/compatibility/ files.
6
  */
7
- function pmpro_compatibility_checker () {
8
- $compat_checks = array(
9
- array(
10
- 'file' => 'siteorigin.php',
11
- 'check_type' => 'constant',
12
- 'check_value' => 'SITEORIGIN_PANELS_VERSION',
13
- ),
14
- array(
15
- 'file' => 'elementor.php',
16
- 'check_type' => 'constant',
17
- 'check_value' => 'ELEMENTOR_VERSION'
18
- ),
19
- array(
20
- 'file' => 'beaver-builder.php',
21
- 'check_type' => 'constant',
22
- 'check_value' => 'FL_BUILDER_VERSION'
23
- ),
24
- array(
25
- 'file' => 'theme-my-login.php',
26
- 'check_type' => 'class',
27
- 'check_value' => 'Theme_My_Login'
28
- ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  array(
30
- 'file' => 'woocommerce.php',
31
  'check_type' => 'constant',
32
- 'check_value' => 'WC_PLUGIN_FILE'
33
- ),
34
- array(
35
- 'file' => 'wp-engine.php',
36
- 'check_type' => 'function',
37
- 'check_value' => 'wpe_filter_site_url'
38
  )
39
- );
40
-
41
- foreach ( $compat_checks as $key => $value ) {
42
- if ( ( $value['check_type'] == 'constant' && defined( $value['check_value'] ) )
43
- || ( $value['check_type'] == 'function' && function_exists( $value['check_value'] ) )
44
- || ( $value['check_type'] == 'class' && class_exists( $value['check_value'] ) ) ) {
45
- include( PMPRO_DIR . '/includes/compatibility/' . $value['file'] ) ;
46
- }
47
- }
48
  }
49
- add_action( 'plugins_loaded', 'pmpro_compatibility_checker' );
4
  * Check if certain plugins or themes are installed and activated
5
  * and if found dynamically load the relevant /includes/compatibility/ files.
6
  */
7
+ function pmpro_compatibility_checker() {
8
+ $compat_checks = [
9
+ [
10
+ 'file' => 'siteorigin.php',
11
+ 'check_type' => 'constant',
12
+ 'check_value' => 'SITEORIGIN_PANELS_VERSION',
13
+ ],
14
+ [
15
+ 'file' => 'elementor.php',
16
+ 'check_type' => 'constant',
17
+ 'check_value' => 'ELEMENTOR_VERSION',
18
+ ],
19
+ [
20
+ 'file' => 'beaver-builder.php',
21
+ 'check_type' => 'constant',
22
+ 'check_value' => 'FL_BUILDER_VERSION',
23
+ ],
24
+ [
25
+ 'file' => 'theme-my-login.php',
26
+ 'check_type' => 'class',
27
+ 'check_value' => 'Theme_My_Login',
28
+ ],
29
+ [
30
+ 'file' => 'woocommerce.php',
31
+ 'check_type' => 'constant',
32
+ 'check_value' => 'WC_PLUGIN_FILE',
33
+ ],
34
+ [
35
+ 'file' => 'wp-engine.php',
36
+ 'check_type' => 'function',
37
+ 'check_value' => 'wpe_filter_site_url',
38
+ ],
39
+ [
40
+ 'file' => 'divi.php',
41
+ 'check_type' => 'constant',
42
+ 'check_value' => 'ET_BUILDER_PLUGIN_DIR',
43
+ ],
44
+ [
45
+ 'file' => 'jetpack.php',
46
+ 'check_type' => 'class',
47
+ 'check_value' => 'Jetpack',
48
+ ],
49
+ ];
50
+
51
+ foreach ( $compat_checks as $value ) {
52
+ if ( pmpro_compatibility_checker_is_requirement_met( $value ) ) {
53
+ include_once( PMPRO_DIR . '/includes/compatibility/' . $value['file'] ) ;
54
+ }
55
+ }
56
+ }
57
+ add_action( 'plugins_loaded', 'pmpro_compatibility_checker' );
58
+
59
+ /**
60
+ * Check whether the requirement is met.
61
+ *
62
+ * @since 2.6.4
63
+ *
64
+ * @param array $requirement The requirement config (check_type, check_value, check_constant_true).
65
+ *
66
+ * @return bool Whether the requirement is met.
67
+ */
68
+ function pmpro_compatibility_checker_is_requirement_met( $requirement ) {
69
+ // Make sure we have the keys that we expect.
70
+ if ( ! isset( $requirement['check_type'], $requirement['check_value'] ) ) {
71
+ return false;
72
+ }
73
+
74
+ // Check for a constant and maybe check if the constant is true-ish.
75
+ if ( 'constant' === $requirement['check_type'] ) {
76
+ return (
77
+ defined( $requirement['check_value'] )
78
+ && (
79
+ empty( $requirement['check_constant_true'] )
80
+ || constant( $requirement['check_value'] )
81
+ )
82
+ );
83
+ }
84
+
85
+ // Check for a function.
86
+ if ( 'function' === $requirement['check_type'] ) {
87
+ return function_exists( $requirement['check_value'] );
88
+ }
89
+
90
+ // Check for a class.
91
+ if ( 'class' === $requirement['check_type'] ) {
92
+ return class_exists( $requirement['check_value'] );
93
+ }
94
+
95
+ return false;
96
+ }
97
+
98
+ function pmpro_compatibility_checker_themes(){
99
+
100
+ $compat_checks = array(
101
  array(
102
+ 'file' => 'divi.php',
103
  'check_type' => 'constant',
104
+ 'check_value' => 'ET_BUILDER_THEME' //Adds support for the Divi theme.
 
 
 
 
 
105
  )
106
+ );
107
+
108
+ foreach ( $compat_checks as $key => $value ) {
109
+ if ( pmpro_compatibility_checker_is_requirement_met( $value ) ) {
110
+ include_once( PMPRO_DIR . '/includes/compatibility/' . $value['file'] ) ;
111
+ }
112
+ }
113
+
114
+
115
  }
116
+ add_action( 'after_setup_theme', 'pmpro_compatibility_checker_themes' );
includes/compatibility/beaver-builder.php CHANGED
@@ -26,9 +26,11 @@ function pmpro_beaver_builder_settings_form( $form, $id ) {
26
  }
27
  global $membership_levels;
28
  $levels = array();
 
29
  foreach ( $membership_levels as $level ) {
30
  $levels[ $level->id ] = $level->name;
31
  }
 
32
  $row_settings_pmpro = array(
33
  'title' => __( 'PMPro', 'paid-memberships-pro' ),
34
  'sections' => array(
@@ -83,6 +85,7 @@ function pmpro_beaver_builder_check_field_connections( $is_visible, $node ) {
83
  if ( ! defined( 'PMPRO_VERSION' ) ) {
84
  return $is_visible;
85
  }
 
86
  if ( 'row' === $node->type ) {
87
  if ( isset( $node->settings->pmpro_enable ) && 'yes' === $node->settings->pmpro_enable ) {
88
  if ( pmpro_hasMembershipLevel( $node->settings->pmpro_memberships ) || empty( $node->settings->pmpro_memberships ) ) {
@@ -120,6 +123,7 @@ function pmpro_beaver_builder_add_custom_tab_all_modules( $form, $slug ) {
120
  if ( in_array( $slug, $modules, true ) ) {
121
  global $membership_levels;
122
  $levels = array();
 
123
  foreach ( $membership_levels as $level ) {
124
  $levels[ $level->id ] = $level->name;
125
  }
26
  }
27
  global $membership_levels;
28
  $levels = array();
29
+ $levels[0] = __( 'Non-members', 'paid-memberships-pro' );
30
  foreach ( $membership_levels as $level ) {
31
  $levels[ $level->id ] = $level->name;
32
  }
33
+
34
  $row_settings_pmpro = array(
35
  'title' => __( 'PMPro', 'paid-memberships-pro' ),
36
  'sections' => array(
85
  if ( ! defined( 'PMPRO_VERSION' ) ) {
86
  return $is_visible;
87
  }
88
+
89
  if ( 'row' === $node->type ) {
90
  if ( isset( $node->settings->pmpro_enable ) && 'yes' === $node->settings->pmpro_enable ) {
91
  if ( pmpro_hasMembershipLevel( $node->settings->pmpro_memberships ) || empty( $node->settings->pmpro_memberships ) ) {
123
  if ( in_array( $slug, $modules, true ) ) {
124
  global $membership_levels;
125
  $levels = array();
126
+ $levels[0] = __( 'Non-members', 'paid-memberships-pro' );
127
  foreach ( $membership_levels as $level ) {
128
  $levels[ $level->id ] = $level->name;
129
  }
includes/compatibility/divi.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class PMProDivi{
4
+
5
+ function __construct(){
6
+
7
+ if ( empty( $_GET['page'] ) || 'et_divi_role_editor' !== $_GET['page'] ) {
8
+ add_filter( 'et_builder_get_parent_modules', array( $this, 'toggle' ) );
9
+ add_filter( 'et_pb_module_content', array( $this, 'restrict_content' ), 10, 4 );
10
+ add_filter( 'et_pb_all_fields_unprocessed_et_pb_row', array( $this, 'row_settings' ) );
11
+ add_filter( 'et_pb_all_fields_unprocessed_et_pb_section', array( $this, 'section_settings' ) );
12
+ }
13
+
14
+ }
15
+
16
+ public function toggle( $modules ) {
17
+
18
+ if ( isset( $modules['et_pb_row'] ) && is_object( $modules['et_pb_row'] ) ) {
19
+ $modules['et_pb_row']->settings_modal_toggles['custom_css']['toggles']['paid-memberships-pro'] = __( 'Paid Memberships Pro', 'paid-memberships-pro' );
20
+ }
21
+
22
+ if ( isset( $modules['et_pb_section'] ) && is_object( $modules['et_pb_section'] ) ) {
23
+ $modules['et_pb_section']->settings_modal_toggles['custom_css']['toggles']['paid-memberships-pro'] = __( 'Paid Memberships Pro', 'paid-memberships-pro' );
24
+ }
25
+
26
+ return $modules;
27
+
28
+ }
29
+
30
+ public function row_settings( $settings ) {
31
+
32
+ $settings['paid-memberships-pro'] = array(
33
+ 'tab_slug' => 'custom_css',
34
+ 'label' => __( 'Restrict Row by Level', 'paid-memberships-pro' ),
35
+ 'description' => __( 'Enter comma-separated level IDs.', 'paid-memberships-pro' ),
36
+ 'type' => 'text',
37
+ 'default' => '',
38
+ 'option_category' => 'configuration',
39
+ 'toggle_slug' => 'paid-memberships-pro',
40
+ );
41
+
42
+ return $settings;
43
+
44
+ }
45
+
46
+ public function section_settings( $settings ) {
47
+
48
+ $settings['paid-memberships-pro'] = array(
49
+ 'tab_slug' => 'custom_css',
50
+ 'label' => __( 'Restrict Section by Level', 'paid-memberships-pro' ),
51
+ 'description' => __( 'Enter comma-separated level IDs.', 'paid-memberships-pro' ),
52
+ 'type' => 'text',
53
+ 'default' => '',
54
+ 'option_category' => 'configuration',
55
+ 'toggle_slug' => 'paid-memberships-pro',
56
+ );
57
+
58
+ return $settings;
59
+
60
+ }
61
+
62
+ public function restrict_content( $output, $props, $attrs, $slug ) {
63
+
64
+ if ( et_fb_is_enabled() ) {
65
+ return $output;
66
+ }
67
+
68
+ if( !isset( $props['paid-memberships-pro'] ) ){
69
+ return $output;
70
+ }
71
+
72
+ $level = $props['paid-memberships-pro'];
73
+
74
+ if ( empty( trim( $level ) ) || trim( $level ) === '0' ) {
75
+ return $output;
76
+ }
77
+
78
+ if( strpos( $level, "," ) ) {
79
+ //they specified many levels
80
+ $levels = explode( ",", $level );
81
+ } else {
82
+ //they specified just one level
83
+ $levels = array( $level );
84
+ }
85
+
86
+ if( pmpro_hasMembershipLevel( $levels ) ){
87
+ return $output;
88
+ } else {
89
+ return '';
90
+ }
91
+ }
92
+ }
93
+ new PMProDivi();
includes/compatibility/elementor/class-pmpro-elementor-content-restriction.php CHANGED
@@ -54,9 +54,15 @@ class PMPro_Elementor_Content_Restriction extends PMPro_Elementor {
54
 
55
  // Don't hide content in editor mode.
56
  if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
57
- return $should_render;
58
- }
59
 
 
 
 
 
 
 
60
  $should_render = $this->pmpro_elementor_has_access( $element );
61
 
62
  return apply_filters( 'pmpro_elementor_section_access', $should_render, $element );
54
 
55
  // Don't hide content in editor mode.
56
  if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
57
+ return $should_render;
58
+ }
59
 
60
+ // Bypass if it's already hidden.
61
+ if ( $should_render === false ) {
62
+ return $should_render;
63
+ }
64
+
65
+ // Checks if the element is restricted and then if the user has access.
66
  $should_render = $this->pmpro_elementor_has_access( $element );
67
 
68
  return apply_filters( 'pmpro_elementor_section_access', $should_render, $element );
includes/compatibility/jetpack.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Jetpack Compatibility.
4
+ *
5
+ * @since 2.6.5
6
+ */
7
+
8
+ /**
9
+ * Support Jetpack SSO login for WordPress.com.
10
+ *
11
+ * @since 2.6.4
12
+ */
13
+ function pmpro_jetpack_sso_handle_login() {
14
+ global $pmpro_pages, $action;
15
+
16
+ // Only do this if we're on the login page.
17
+ if ( empty( $pmpro_pages['login'] ) || ! is_page( $pmpro_pages['login'] ) ) {
18
+ return;
19
+ }
20
+
21
+ // Only do this if the sso module is active.
22
+ if ( ! Jetpack::is_module_active( 'sso' ) ) {
23
+ return;
24
+ }
25
+
26
+ $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
27
+
28
+ do_action( 'login_init' );
29
+ }
30
+
31
+ add_action( 'wp', 'pmpro_jetpack_sso_handle_login', 20 );
includes/content.php CHANGED
@@ -81,6 +81,9 @@ function pmpro_has_membership_access($post_id = NULL, $user_id = NULL, $return_m
81
  }
82
  else
83
  {
 
 
 
84
  //we need to see if the user has access
85
  foreach($post_membership_levels as $level)
86
  {
@@ -144,10 +147,9 @@ function pmpro_search_filter($query)
144
  //hide pmpro pages from search results
145
  if( ! $query->is_admin && $query->is_search && empty( $query->query['post_parent'] ) ) {
146
  //avoiding post_parent queries for now
147
- if( empty( $query->query_vars['post_parent'] ) ) {
148
- $query->set('post__not_in', $pmpro_pages );
149
  }
150
- $query->set('post__not_in', $pmpro_pages ); // id of page or post
151
  }
152
 
153
  // If this is a post type query, get the queried post types into an array.
@@ -223,7 +225,7 @@ function pmpro_search_filter($query)
223
  if( $hidden_page_ids ) {
224
  //avoiding post_parent queries for now
225
  if( empty( $query->query_vars['post_parent'] ) ) {
226
- $query->set( 'post__not_in', $hidden_page_ids );
227
  }
228
  }
229
 
@@ -248,7 +250,7 @@ function pmpro_search_filter($query)
248
 
249
  //make this work
250
  if( $hidden_cat_ids ) {
251
- $query->set( 'category__not_in', $hidden_cat_ids );
252
 
253
  //filter so posts in this member's categories are allowed
254
  add_action( 'posts_where', 'pmpro_posts_where_unhide_cats' );
@@ -298,6 +300,16 @@ function pmpro_membership_content_filter( $content, $skipcheck = false ) {
298
  $hasaccess = $hasaccess[0];
299
  }
300
  }
 
 
 
 
 
 
 
 
 
 
301
 
302
  if( $hasaccess ) {
303
  //all good, return content
@@ -313,23 +325,23 @@ function pmpro_membership_content_filter( $content, $skipcheck = false ) {
313
  } elseif(strpos($content, "<span id=\"more-" . $post->ID . "\"></span>") !== false) {
314
  //more tag
315
  $pos = strpos($content, "<span id=\"more-" . $post->ID . "\"></span>");
316
- $content = wpautop(substr($content, 0, $pos));
317
  } elseif(strpos($content, 'class="more-link">') !== false) {
318
  //more link
319
  $content = preg_replace("/\<a.*class\=\"more\-link\".*\>.*\<\/a\>/", "", $content);
320
  } elseif(strpos($content, "<!-- wp:more -->") !== false) {
321
  //more block
322
  $pos = strpos($content, "<!-- wp:more -->");
323
- $content = wpautop(substr($content, 0, $pos));
324
  } elseif(strpos($content, "<!--more-->") !== false) {
325
  //more tag
326
  $pos = strpos($content, "<!--more-->");
327
- $content = wpautop(substr($content, 0, $pos));
328
  } else {
329
  //auto generated excerpt. pulled from wp_trim_excerpt
330
  $content = strip_shortcodes( $content );
331
  $content = str_replace(']]>', ']]&gt;', $content);
332
- $content = strip_tags($content);
333
  $excerpt_length = apply_filters('excerpt_length', 55);
334
  $words = preg_split("/[\n\r\t ]+/", $content, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
335
  if ( count($words) > $excerpt_length ) {
@@ -381,15 +393,11 @@ add_filter('get_the_excerpt', 'pmpro_membership_get_excerpt_filter_start', 1);
381
  add_filter('get_the_excerpt', 'pmpro_membership_get_excerpt_filter_end', 100);
382
 
383
  function pmpro_comments_filter($comments, $post_id = NULL) {
384
- global $post, $wpdb, $current_user;
385
- if(!$post_id)
386
- $post_id = $post->ID;
387
 
388
  if(!$comments)
389
  return $comments; //if they are closed anyway, we don't need to check
390
 
391
- global $post, $current_user;
392
-
393
  $hasaccess = pmpro_has_membership_access(NULL, NULL, true);
394
  if( is_array( $hasaccess ) ) {
395
  //returned an array to give us the membership level values
@@ -481,7 +489,9 @@ function pmpro_post_classes( $classes, $class, $post_id ) {
481
  if( ! empty( $post_levels[1] ) ) {
482
  $classes[] = 'pmpro-level-required';
483
  foreach( $post_levels[1] as $post_level ) {
484
- $classes[] = 'pmpro-level-' . $post_level[0];
 
 
485
  }
486
  }
487
  if(!empty($post_levels[0]) && $post_levels[0] == true) {
81
  }
82
  else
83
  {
84
+ // Reorder the $post_membership_levels to match sorted order.
85
+ $post_membership_levels = pmpro_sort_levels_by_order( $post_membership_levels );
86
+
87
  //we need to see if the user has access
88
  foreach($post_membership_levels as $level)
89
  {
147
  //hide pmpro pages from search results
148
  if( ! $query->is_admin && $query->is_search && empty( $query->query['post_parent'] ) ) {
149
  //avoiding post_parent queries for now
150
+ if( empty( $query->query_vars['post_parent'] ) && ! empty( $pmpro_pages ) ) {
151
+ $query->set( 'post__not_in', array_merge( $query->get('post__not_in'), array_values( $pmpro_pages ) ) );
152
  }
 
153
  }
154
 
155
  // If this is a post type query, get the queried post types into an array.
225
  if( $hidden_page_ids ) {
226
  //avoiding post_parent queries for now
227
  if( empty( $query->query_vars['post_parent'] ) ) {
228
+ $query->set( 'post__not_in', array_merge( $query->get('post__not_in'), $hidden_page_ids ) );
229
  }
230
  }
231
 
250
 
251
  //make this work
252
  if( $hidden_cat_ids ) {
253
+ $query->set( 'category__not_in', array_merge( $query->get( 'category__not_in' ), $hidden_cat_ids ) );
254
 
255
  //filter so posts in this member's categories are allowed
256
  add_action( 'posts_where', 'pmpro_posts_where_unhide_cats' );
300
  $hasaccess = $hasaccess[0];
301
  }
302
  }
303
+
304
+ /**
305
+ * Filter to let other plugins change how PMPro filters member content.
306
+ * If anything other than false is returned, that value will overwrite
307
+ * the $content variable and no further processing is done in this function.
308
+ */
309
+ $content_filter = apply_filters( 'pmpro_membership_content_filter', false, $content, $hasaccess );
310
+ if ( $content_filter !== false ) {
311
+ return $content_filter;
312
+ }
313
 
314
  if( $hasaccess ) {
315
  //all good, return content
325
  } elseif(strpos($content, "<span id=\"more-" . $post->ID . "\"></span>") !== false) {
326
  //more tag
327
  $pos = strpos($content, "<span id=\"more-" . $post->ID . "\"></span>");
328
+ $content = substr($content, 0, $pos);
329
  } elseif(strpos($content, 'class="more-link">') !== false) {
330
  //more link
331
  $content = preg_replace("/\<a.*class\=\"more\-link\".*\>.*\<\/a\>/", "", $content);
332
  } elseif(strpos($content, "<!-- wp:more -->") !== false) {
333
  //more block
334
  $pos = strpos($content, "<!-- wp:more -->");
335
+ $content = substr($content, 0, $pos);
336
  } elseif(strpos($content, "<!--more-->") !== false) {
337
  //more tag
338
  $pos = strpos($content, "<!--more-->");
339
+ $content = substr($content, 0, $pos);
340
  } else {
341
  //auto generated excerpt. pulled from wp_trim_excerpt
342
  $content = strip_shortcodes( $content );
343
  $content = str_replace(']]>', ']]&gt;', $content);
344
+ $content = wp_strip_all_tags( $content );
345
  $excerpt_length = apply_filters('excerpt_length', 55);
346
  $words = preg_split("/[\n\r\t ]+/", $content, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
347
  if ( count($words) > $excerpt_length ) {
393
  add_filter('get_the_excerpt', 'pmpro_membership_get_excerpt_filter_end', 100);
394
 
395
  function pmpro_comments_filter($comments, $post_id = NULL) {
396
+ global $current_user;
 
 
397
 
398
  if(!$comments)
399
  return $comments; //if they are closed anyway, we don't need to check
400
 
 
 
401
  $hasaccess = pmpro_has_membership_access(NULL, NULL, true);
402
  if( is_array( $hasaccess ) ) {
403
  //returned an array to give us the membership level values
489
  if( ! empty( $post_levels[1] ) ) {
490
  $classes[] = 'pmpro-level-required';
491
  foreach( $post_levels[1] as $post_level ) {
492
+ if ( isset( $post_level[0] ) ) {
493
+ $classes[] = 'pmpro-level-' . $post_level[0];
494
+ }
495
  }
496
  }
497
  if(!empty($post_levels[0]) && $post_levels[0] == true) {
includes/currencies.php CHANGED
@@ -26,7 +26,7 @@
26
  'CNY' => __('Chinese Yuan', 'paid-memberships-pro' ),
27
  'CZK' => array(
28
  'name' => __('Czech Koruna', 'paid-memberships-pro' ),
29
- 'decimals' => '0',
30
  'thousands_separator' => '&nbsp;',
31
  'decimal_separator' => ',',
32
  'symbol' => '&nbsp;Kč',
@@ -99,6 +99,14 @@
99
  'TWD' => __('Taiwan New Dollars', 'paid-memberships-pro' ),
100
  'THB' => __('Thai Baht', 'paid-memberships-pro' ),
101
  'TRY' => __('Turkish Lira', 'paid-memberships-pro' ),
 
 
 
 
 
 
 
 
102
  'VND' => array(
103
  'name' => __('Vietnamese Dong', 'paid-memberships-pro' ),
104
  'decimals' => 0,
26
  'CNY' => __('Chinese Yuan', 'paid-memberships-pro' ),
27
  'CZK' => array(
28
  'name' => __('Czech Koruna', 'paid-memberships-pro' ),
29
+ 'decimals' => '2',
30
  'thousands_separator' => '&nbsp;',
31
  'decimal_separator' => ',',
32
  'symbol' => '&nbsp;Kč',
99
  'TWD' => __('Taiwan New Dollars', 'paid-memberships-pro' ),
100
  'THB' => __('Thai Baht', 'paid-memberships-pro' ),
101
  'TRY' => __('Turkish Lira', 'paid-memberships-pro' ),
102
+ 'UAH' => array(
103
+ 'name' => __('Ukrainian Hryvnia (&#8372;)', 'paid-memberships-pro' ),
104
+ 'decimals' => 0,
105
+ 'thousands_separator' => '',
106
+ 'decimal_separator' => ',',
107
+ 'symbol' => '&#8372;',
108
+ 'position' => 'right'
109
+ ),
110
  'VND' => array(
111
  'name' => __('Vietnamese Dong', 'paid-memberships-pro' ),
112
  'decimals' => 0,
includes/deprecated.php CHANGED
@@ -38,7 +38,7 @@ add_action( 'init', 'pmpro_init_check_for_deprecated_filters', 99 );
38
  *
39
  */
40
  function pmpro_getClassForField( $field ) {
41
- pmpro_get_element_class( '', $field );
42
  }
43
 
44
  /**
@@ -52,4 +52,135 @@ function pmpro_admin_init_redirect_old_menu_items() {
52
  exit;
53
  }
54
  }
55
- add_action( 'init', 'pmpro_admin_init_redirect_old_menu_items' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  *
39
  */
40
  function pmpro_getClassForField( $field ) {
41
+ return pmpro_get_element_class( '', $field );
42
  }
43
 
44
  /**
52
  exit;
53
  }
54
  }
55
+ add_action( 'init', 'pmpro_admin_init_redirect_old_menu_items' );
56
+
57
+ // Check if installed, deactivate it and show a notice now.
58
+ function pmpro_check_for_deprecated_add_ons() {
59
+
60
+ $deprecated = array(
61
+ 'pmpro-member-history' => array(
62
+ 'file' => 'pmpro-member-history.php',
63
+ 'label' => 'Member History'
64
+ ),
65
+ 'pmpro-email-templates' => array(
66
+ 'file' => 'pmpro-email-templates.php',
67
+ 'label' => 'Email Templates'
68
+ ),
69
+ 'pmpro-email-templates-addon' => array(
70
+ 'file' => 'pmpro-email-templates.php',
71
+ 'label' => 'Email Templates'
72
+ )
73
+ );
74
+
75
+ $deprecated = apply_filters( 'pmpro_deprecated_add_ons_list', $deprecated );
76
+
77
+ // If the list is empty or not an array, just bail.
78
+ if ( empty( $deprecated ) || ! is_array( $deprecated ) ) {
79
+ return;
80
+ }
81
+
82
+ $deprecated_active = array();
83
+ foreach( $deprecated as $key => $values ) {
84
+ $path = '/' . $key . '/' . $values['file'];
85
+ if ( file_exists( WP_PLUGIN_DIR . $path ) ) {
86
+ $deprecated_active[] = $values['label'];
87
+
88
+ // Try to deactivate it if it's enabled.
89
+ if ( is_plugin_active( plugin_basename( $path ) ) ) {
90
+ deactivate_plugins( $path );
91
+ }
92
+ }
93
+ }
94
+
95
+ // If any deprecated add ons are active, show warning.
96
+ if ( is_array( $deprecated_active ) && ! empty( $deprecated_active ) ) {
97
+ // Only show on certain pages.
98
+ if ( ! isset( $_REQUEST['page'] ) || strpos( $_REQUEST['page'], 'pmpro' ) === false ) {
99
+ return;
100
+ }
101
+ ?>
102
+ <div class="notice notice-warning">
103
+ <p>
104
+ <?php
105
+ // translators: %s: The list of deprecated plugins that are active.
106
+ printf(
107
+ __( 'Some Add Ons are now merged into the Paid Memberships Pro core plugin. The features of the following plugins are now included in PMPro by default. You should <strong>delete these unnecessary plugins</strong> from your site: <em><strong>%s</strong></em>.', 'paid-memberships-pro' ),
108
+ implode( ', ', $deprecated_active )
109
+ );
110
+ ?>
111
+ </p>
112
+ </div>
113
+ <?php
114
+ }
115
+ }
116
+ add_action( 'admin_notices', 'pmpro_check_for_deprecated_add_ons' );
117
+
118
+ /**
119
+ * The 2Checkout gateway was deprecated in v2.6.
120
+ * This code will add it back if it was the selected gateway.
121
+ * In future versions, we will remove the 2Checkout code entirely.
122
+ * And you will have to use a stand alone add on for 2Checkout support
123
+ * or choose a new gateway.
124
+ */
125
+ function pmpro_check_for_deprecated_gateways() {
126
+ $undprepcated_gateways = (array)pmpro_getOption( 'undeprecated_gateways' );
127
+ $default_gateway = pmpro_getOption( 'gateway' );
128
+
129
+ if ( $default_gateway === 'twocheckout' || in_array( 'twocheckout', $undprepcated_gateways ) ) {
130
+ require_once( PMPRO_DIR . '/classes/gateways/class.pmprogateway_twocheckout.php' );
131
+
132
+ if ( ! in_array( 'twocheckout', $undprepcated_gateways ) ) {
133
+ $undeprecated_gateways[] = 'twocheckout';
134
+ pmpro_setOption( 'undeprecated_gateways', $undeprecated_gateways );
135
+ }
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Disable uninstall script for duplicates
141
+ */
142
+ function pmpro_disable_uninstall_script_for_duplicates( $file ) {
143
+ // bail if not a duplicate
144
+ if ( ! in_array( $file, array_keys( pmpro_get_plugin_duplicates() ) ) ) {
145
+ return;
146
+ }
147
+
148
+ // disable uninstall script
149
+ if ( file_exists( WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php' ) ) {
150
+ rename(
151
+ WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php',
152
+ WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall-disabled.php'
153
+ );
154
+ }
155
+ }
156
+ add_action( 'pre_uninstall_plugin', 'pmpro_disable_uninstall_script_for_duplicates' );
157
+
158
+ /**
159
+ * @return array
160
+ */
161
+ function pmpro_get_plugin_duplicates() {
162
+ $all_plugins = get_plugins();
163
+ $active_plugins_names = get_option( 'active_plugins' );
164
+
165
+ $multiple_installations = array();
166
+ foreach ( $all_plugins as $plugin_name => $plugin_headers ) {
167
+ // skip all active plugins
168
+ if ( in_array( $plugin_name, $active_plugins_names ) ) {
169
+ continue;
170
+ }
171
+
172
+ // skip plugins without a folder
173
+ if ( false === strpos( $plugin_name, '/' ) ) {
174
+ continue;
175
+ }
176
+
177
+ // check if plugin file is paid-memberships-pro.php
178
+ // or Plugin Name: Paid Memberships Pro
179
+ list( $plugin_folder, $plugin_mainfile_php ) = explode( '/', $plugin_name );
180
+ if ( 'paid-memberships-pro.php' === $plugin_mainfile_php || 'Paid Memberships Pro' === $plugin_headers['Name'] ) {
181
+ $multiple_installations[ $plugin_name ] = $plugin_headers;
182
+ }
183
+ }
184
+
185
+ return $multiple_installations;
186
+ }
includes/email-templates.php ADDED
@@ -0,0 +1,536 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File used to setup default email templates data.
4
+ */
5
+
6
+ global $pmpro_email_templates_defaults;
7
+ /**
8
+ * Default email templates.
9
+ */
10
+ $pmpro_email_templates_defaults = array(
11
+ 'default' => array(
12
+ 'subject' => __( "An email from !!sitename!!", 'paid-memberships-pro' ),
13
+ 'description' => __( 'Default Email', 'paid-memberships-pro'),
14
+ 'body' => __( '!!body!!', 'paid-memberships-pro' ),
15
+ 'help_text' => __( 'This email is sent when there is a general message that needs to be communicated to the site administrator.', 'paid-memberships-pro' )
16
+ ),
17
+ 'footer' => array(
18
+ 'subject' => __( '', 'paid-memberships-pro' ),
19
+ 'description' => __( 'Email Footer', 'paid-memberships-pro'),
20
+ 'body' => __( '<p>
21
+ Respectfully,<br />
22
+ !!sitename!!
23
+ </p>', 'paid-memberships-pro' ),
24
+ 'help_text' => __( 'This is the closing message included in every email sent to members and the site administrator through Paid Memberships Pro.', 'paid-memberships-pro' )
25
+ ),
26
+ 'header' => array(
27
+ 'subject' => __( '', 'paid-memberships-pro' ),
28
+ 'description' => __( 'Email Header', 'paid-memberships-pro'),
29
+ 'body' => __( '<p>Dear !!name!!,</p>', 'paid-memberships-pro' ),
30
+ 'help_text' => __( 'This is the opening message included in every email sent to members and the site administrator through Paid Memberships Pro.', 'paid-memberships-pro' )
31
+ ),
32
+ 'admin_change' => array(
33
+ 'subject' => __( "Your membership at !!sitename!! has been changed", 'paid-memberships-pro' ),
34
+ 'description' => __( 'Admin Change', 'paid-memberships-pro'),
35
+ 'body' => __( '<p>An administrator at !!sitename!! has changed your membership level.</p>
36
+
37
+ <p>!!membership_change!!</p>
38
+
39
+ <p>If you did not request this membership change and would like more information please contact us at !!siteemail!!</p>
40
+
41
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
42
+ 'help_text' => __( 'The site administrator can manually update a user\'s membership level through the WordPress admin. This email notifies the member of the level update.', 'paid-memberships-pro' )
43
+ ),
44
+ 'admin_change_admin' => array(
45
+ 'subject' => __( "Membership for !!user_login!! at !!sitename!! has been changed", 'paid-memberships-pro' ),
46
+ 'description' => __('Admin Change (admin)', 'paid-memberships-pro'),
47
+ 'body' => __( '<p>An administrator at !!sitename!! has changed a membership level for !!name!!.</p>
48
+
49
+ <p>!!membership_change!!</p>
50
+
51
+ <p>Log in to your WordPress admin here: !!login_url!!</p>', 'paid-memberships-pro' ),
52
+ 'help_text' => __( 'The site administrator can manually update a user\'s membership level through the WordPress admin. This email notifies the site administrator of the level update.', 'paid-memberships-pro' )
53
+ ),
54
+ 'billable_invoice' => array(
55
+ 'subject' => __( 'Invoice for order #: !!order_code!!', 'paid-memberships-pro' ),
56
+ 'description' => __( 'Billable Invoice', 'paid-membershps-pro' ),
57
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Below is your invoice for order #: !!order_code!!</p>
58
+
59
+ !!invoice!!
60
+
61
+ <p>Log in to your membership account here: !!login_url!!</p>
62
+
63
+ <p>To view an online version of this invoice, click here: !!invoice_url!!</p>', 'paid-memberships-pro' ),
64
+ 'help_text' => __( 'The site administrator can manually send a user a copy of this invoice email via the Memberships > Orders admin page.', 'paid-memberships-pro' )
65
+ ),
66
+ 'billing' => array(
67
+ 'subject' => __( "Your billing information has been updated at !!sitename!!", 'paid-memberships-pro' ),
68
+ 'description' => __('Billing', 'paid-memberships-pro'),
69
+ 'body' => __( '<p>Your billing information at !!sitename!! has been changed.</p><p>Account: !!display_name!! (!!user_email!!)</p>
70
+ <p>
71
+ Billing Information:<br />
72
+ !!billing_address!!
73
+ </p><p>
74
+ !!cardtype!!: !!accountnumber!!<br />
75
+ Expires: !!expirationmonth!!/!!expirationyear!!
76
+ </p><p>If you did not request a billing information change please contact us at !!siteemail!!</p><p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
77
+ 'help_text' => __( 'Members can update the payment method associated with their recurring subscription. This email is sent to the member as a confirmation of a payment method update.', 'paid-memberships-pro' )
78
+ ),
79
+ 'billing_admin' => array(
80
+ 'subject' => __( "Billing information has been updated for !!user_login!! at !!sitename!!", 'paid-memberships-pro' ),
81
+ 'description' => __('Billing (admin)', 'paid-memberships-pro'),
82
+ 'body' => __( '<p>The billing information for !!display_name!! at !!sitename!! has been changed.</p>
83
+
84
+ <p>Account: !!display_name!! (!!user_email!!)</p>
85
+ <p>
86
+ Billing Information:<br />
87
+ !!billing_name!!<br />
88
+ !!billing_street!!<br />
89
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
90
+ !!billing_phone!!
91
+ </p>
92
+
93
+ <p>
94
+ !!cardtype!!: !!accountnumber!!<br />
95
+ Expires: !!expirationmonth!!/!!expirationyear!!
96
+ </p>
97
+
98
+ <p>Log in to your WordPress dashboard here: !!login_url!!</p>', 'paid-memberships-pro' ),
99
+ 'help_text' => __( 'Members can update the payment method associated with their recurring subscription. This email is sent to the site administrator as a confirmation of a payment method update.', 'paid-memberships-pro' )
100
+ ),
101
+ 'billing_failure' => array(
102
+ 'subject' => __( "Membership payment failed at !!sitename!!", 'paid-memberships-pro' ),
103
+ 'description' => __('Billing Failure', 'paid-memberships-pro'),
104
+ 'body' => __( '<p>The current subscription payment for your !!sitename!! membership has failed. <strong>Please click the following link to log in and update your billing information to avoid account suspension. !!login_url!!</strong></p>
105
+
106
+ <p>Account: !!display_name!! (!!user_email!!)</p>', 'paid-memberships-pro' ),
107
+ 'help_text' => __( 'This email is sent out if a recurring payment has failed, usually due to an expired or cancelled credit card. This email is sent to the member to allowing them time to update payment information without a disruption in access to your site.', 'paid-memberships-pro' )
108
+ ),
109
+ 'billing_failure_admin' => array(
110
+ 'subject' => __( "Membership payment failed for !!display_name!! at !!sitename!!", 'paid-memberships-pro' ),
111
+ 'description' => __('Billing Failure (admin)', 'paid-memberships-pro'),
112
+ 'body' => __( '<p>The subscription payment for !!user_login!! at !!sitename!! has failed.</p>
113
+
114
+ <p>Account: !!display_name!! (!!user_email!!)</p>
115
+ <p>Membership Level: !!membership_level_name!!</p>
116
+
117
+ <p>Log in to your WordPress admin here: !!login_url!!</p>
118
+ ', 'paid-memberships-pro' ),
119
+ 'help_text' => __( 'This email is sent out if a recurring payment has failed for a member, usually due to an expired or cancelled credit card. This email is sent to the site administrator.', 'paid-memberships-pro' )
120
+ ),
121
+ 'cancel' => array(
122
+ 'subject' => __( "Your membership at !!sitename!! has been CANCELLED", 'paid-memberships-pro' ),
123
+ 'description' => __('Cancel', 'paid-memberships-pro'),
124
+ 'body' => __( '<p>Your membership at !!sitename!! has been cancelled.</p>
125
+
126
+ <p>Account: !!display_name!! (!!user_email!!)</p>
127
+ <p>Membership Level: !!membership_level_name!!</p>
128
+
129
+ <p>If you did not request this cancellation and would like more information please contact us at !!siteemail!!</p>', 'paid-memberships-pro' ),
130
+ 'help_text' => __( 'The site administrator can manually cancel a user\'s membership through the WordPress admin or the member can cancel their own membership through your site. This email is sent to the member as confirmation of a cancelled membership.', 'paid-memberships-pro' )
131
+ ),
132
+ 'cancel_admin' => array(
133
+ 'subject' => __( "Membership for !!user_login!! at !!sitename!! has been CANCELLED", 'paid-memberships-pro' ),
134
+ 'description' => __('Cancel (admin)', 'paid-memberships-pro'),
135
+ 'body' => __( '<p>The membership for !!user_login!! at !!sitename!! has been cancelled.</p>
136
+
137
+ <p>Account: !!display_name!! (!!user_email!!)</p>
138
+ <p>Membership Level: !!membership_level_name!!</p>
139
+ <p>Start Date: !!startdate!!</p>
140
+ <p>End Date: !!enddate!!</p>
141
+
142
+ <p>Log in to your WordPress admin here: !!login_url!!</p>', 'paid-memberships-pro' ),
143
+ 'help_text' => __( 'The site administrator can manually cancel a user\'s membership through the WordPress admin or the member can cancel their own membership through your site. This email is sent to the site administrator as confirmation of a cancelled membership.', 'paid-memberships-pro' )
144
+ ),
145
+ 'checkout_check' => array(
146
+ 'subject' => __( "Your membership confirmation for !!sitename!!", 'paid-memberships-pro' ),
147
+ 'description' => __('Checkout - Check', 'paid-memberships-pro'),
148
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
149
+
150
+ !!membership_level_confirmation_message!!
151
+
152
+ !!instructions!!
153
+
154
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
155
+
156
+ <p>Account: !!display_name!! (!!user_email!!)</p>
157
+ <p>Membership Level: !!membership_level_name!!</p>
158
+ <p>Membership Fee: !!membership_cost!!</p>
159
+ !!membership_expiration!! !!discount_code!!
160
+
161
+ <p>
162
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
163
+ Total Billed: !!invoice_total!!
164
+ </p>
165
+
166
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
167
+ 'help_text' => __( 'This is a membership confirmation welcome email sent to a new member or to existing members that change their level using the "Pay by Check" gateway.', 'paid-memberships-pro' )
168
+ ),
169
+ 'checkout_check_admin' => array(
170
+ 'subject' => __( "Member checkout for !!membership_level_name!! at !!sitename!!", 'paid-memberships-pro' ),
171
+ 'description' => __('Checkout - Check (admin)', 'paid-memberships-pro'),
172
+ 'body' => __( '<p>There was a new member checkout at !!sitename!!.</p>
173
+
174
+ <p><strong>They have chosen to pay by check.</strong></p>
175
+
176
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
177
+
178
+ <p>Account: !!display_name!! (!!user_email!!)</p>
179
+ <p>Membership Level: !!membership_level_name!!</p>
180
+ <p>Membership Fee: !!membership_cost!!</p>
181
+ !!membership_expiration!! !!discount_code!!
182
+
183
+ <p>
184
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
185
+ Total Billed: !!invoice_total!!
186
+ </p>
187
+
188
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
189
+ 'help_text' => __( 'This is the membership confirmation email sent to the site administrator for every membership checkout using the "Pay by Check" gateway.', 'paid-memberships-pro' )
190
+ ),
191
+ 'checkout_express' => array(
192
+ 'subject' => __( "Your membership confirmation for !!sitename!!", 'paid-memberships-pro' ),
193
+ 'description' => __('Checkout - PayPal Express', 'paid-memberships-pro'),
194
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
195
+ !!membership_level_confirmation_message!!
196
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
197
+
198
+ <p>Account: !!display_name!! (!!user_email!!)</p>
199
+ <p>Membership Level: !!membership_level_name!!</p>
200
+ <p>Membership Fee: !!membership_cost!!</p>
201
+ !!membership_expiration!! !!discount_code!!
202
+
203
+ <p>
204
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
205
+ Total Billed: !!invoice_total!!
206
+ </p>
207
+
208
+ <p>Log in to your membership account here: !!login_url!!</p>
209
+ ', 'paid-memberships-pro' ),
210
+ 'help_text' => __( 'This is a membership confirmation welcome email sent to a new member or to existing members that change their level using the "PayPal Express" gateway.', 'paid-memberships-pro' )
211
+
212
+ ),
213
+ 'checkout_express_admin' => array(
214
+ 'subject' => __( "Member checkout for !!membership_level_name!! at !!sitename!!", 'paid-memberships-pro' ),
215
+ 'description' => __('Checkout - PayPal Express (admin)', 'paid-memberships-pro'),
216
+ 'body' => __( '<p>There was a new member checkout at !!sitename!!.</p>
217
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
218
+
219
+ <p>Account: !!display_name!! (!!user_email!!)</p>
220
+ <p>Membership Level: !!membership_level_name!!</p>
221
+ <p>Membership Fee: !!membership_cost!!</p>
222
+ !!membership_expiration!! !!discount_code!!
223
+
224
+ <p>
225
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
226
+ Total Billed: !!invoice_total!!
227
+ </p>
228
+
229
+ <p>Log in to your membership account here: !!login_url!!</p>
230
+ ', 'paid-memberships-pro' ),
231
+ 'help_text' => __( 'This is the membership confirmation email sent to the site administrator for every membership checkout using the "PayPal Express" gateway.', 'paid-memberships-pro' )
232
+ ),
233
+ 'checkout_free' => array(
234
+ 'subject' => __( "Your membership confirmation for !!sitename!!", 'paid-memberships-pro' ),
235
+ 'description' => __('Checkout - Free', 'paid-memberships-pro'),
236
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
237
+ !!membership_level_confirmation_message!!
238
+ <p>Below are details about your membership account.</p>
239
+
240
+ <p>Account: !!display_name!! (!!user_email!!)</p>
241
+ <p>Membership Level: !!membership_level_name!!</p>
242
+ !!membership_expiration!! !!discount_code!!
243
+
244
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
245
+ 'help_text' => __( 'This is a membership confirmation welcome email sent to a new member or to existing members that change their level when the level has no charge.', 'paid-memberships-pro' )
246
+ ),
247
+ 'checkout_free_admin' => array(
248
+ 'subject' => __( "Member checkout for !!membership_level_name!! at !!sitename!!", 'paid-memberships-pro' ),
249
+ 'description' => __('Checkout - Free (admin)', 'paid-memberships-pro'),
250
+ 'body' => __( '<p>There was a new member checkout at !!sitename!!.</p>
251
+ <p>Below are details about the new membership account.</p>
252
+
253
+ <p>Account: !!display_name!! (!!user_email!!)</p>
254
+ <p>Membership Level: !!membership_level_name!!</p>
255
+ !!membership_expiration!! !!discount_code!!
256
+
257
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
258
+ 'help_text' => __( 'This is the membership confirmation email sent to the site administrator for every membership checkout that has no charge.', 'paid-memberships-pro' )
259
+ ),
260
+ 'checkout_freetrial' => array(
261
+ 'subject' => __( "Your membership confirmation for !!sitename!!", 'paid-memberships-pro' ),
262
+ 'description' => __('Checkout - Free Trial', 'paid-memberships-pro'),
263
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
264
+ !!membership_level_confirmation_message!!
265
+ <p>Below are details about your membership account.</p>
266
+
267
+ <p>Account: !!display_name!! (!!user_email!!)</p>
268
+ <p>Membership Level: !!membership_level_name!!</p>
269
+ <p>Membership Fee: !!membership_cost!!</p>
270
+ !!membership_expiration!! !!discount_code!!
271
+
272
+ <p>
273
+ Billing Information on File:<br />
274
+ !!billing_address!!
275
+ </p>
276
+
277
+ <p>
278
+ !!cardtype!!: !!accountnumber!!<br />
279
+ Expires: !!expirationmonth!!/!!expirationyear!!
280
+ </p>
281
+
282
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
283
+ 'help_text' => __( 'This is a membership confirmation welcome email sent to a new member or to existing members that change their level with a trial period.', 'paid-memberships-pro' )
284
+ ),
285
+ 'checkout_freetrial_admin' => array(
286
+ 'subject' => __( "Member checkout for !!membership_level_name!! at !!sitename!!", 'paid-memberships-pro' ),
287
+ 'description' => __('Checkout - Free Trial (admin)', 'paid-memberships-pro'),
288
+ 'body' => __( '<p>There was a new member checkout at !!sitename!!.</p>
289
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
290
+
291
+ <p>Account: !!display_name!! (!!user_email!!)</p>
292
+ <p>Membership Level: !!membership_level_name!!</p>
293
+ <p>Membership Fee: !!membership_cost!!</p>
294
+ !!membership_expiration!! !!discount_code!!
295
+
296
+ <p>
297
+ Billing Information on File:<br />
298
+ !!billing_address!!
299
+ </p>
300
+
301
+ <p>
302
+ !!cardtype!!: !!accountnumber!!<br />
303
+ Expires: !!expirationmonth!!/!!expirationyear!!
304
+ </p>
305
+
306
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
307
+ 'help_text' => __( 'This is the membership confirmation email sent to the site administrator for every membership checkout with a trial period.', 'paid-memberships-pro' )
308
+ ),
309
+ 'checkout_paid' => array(
310
+ 'subject' => __( "Your membership confirmation for !!sitename!!", 'paid-memberships-pro' ),
311
+ 'description' => __('Checkout - Paid', 'paid-memberships-pro'),
312
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
313
+ !!membership_level_confirmation_message!!
314
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
315
+
316
+ <p>Account: !!display_name!! (!!user_email!!)</p>
317
+ <p>Membership Level: !!membership_level_name!!</p>
318
+ <p>Membership Fee: !!membership_cost!!</p>
319
+ !!membership_expiration!! !!discount_code!!
320
+
321
+ <p>
322
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
323
+ Total Billed: !!invoice_total!!
324
+ </p>
325
+ <p>
326
+ Billing Information:<br />
327
+ !!billing_address!!
328
+ </p>
329
+
330
+ <p>
331
+ !!cardtype!!: !!accountnumber!!<br />
332
+ Expires: !!expirationmonth!!/!!expirationyear!!
333
+ </p>
334
+
335
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
336
+ 'help_text' => __( 'This is a membership confirmation welcome email sent to a new member or to existing members that change their level and complete a paid checkout on the site.', 'paid-memberships-pro' )
337
+ ),
338
+ 'checkout_paid_admin' => array(
339
+ 'subject' => __( "Member checkout for !!membership_level_name!! at !!sitename!!", 'paid-memberships-pro' ),
340
+ 'description' => __('Checkout - Paid (admin)', 'paid-memberships-pro'),
341
+ 'body' => __( '<p>There was a new member checkout at !!sitename!!.</p>
342
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
343
+
344
+ <p>Account: !!display_name!! (!!user_email!!)</p>
345
+ <p>Membership Level: !!membership_level_name!!</p>
346
+ <p>Membership Fee: !!membership_cost!!</p>
347
+ !!membership_expiration!! !!discount_code!!
348
+
349
+ <p>
350
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
351
+ Total Billed: !!invoice_total!!
352
+ </p>
353
+ <p>
354
+ Billing Information:<br />
355
+ !!billing_address!!
356
+ </p>
357
+
358
+ <p>
359
+ !!cardtype!!: !!accountnumber!!<br />
360
+ Expires: !!expirationmonth!!/!!expirationyear!!
361
+ </p>
362
+
363
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
364
+ 'help_text' => __( 'This is the membership confirmation email sent to the site administrator for every paid membership checkout on the site.', 'paid-memberships-pro' )
365
+ ),
366
+ 'checkout_trial' => array(
367
+ 'subject' => __( "Your membership confirmation for !!sitename!!", 'paid-memberships-pro' ),
368
+ 'description' => __('Checkout - Trial', 'paid-memberships-pro'),
369
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
370
+ !!membership_level_confirmation_message!!
371
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
372
+
373
+ <p>Account: !!display_name!! (!!user_email!!)</p>
374
+ <p>Membership Level: !!membership_level_name!!</p>
375
+ <p>Membership Fee: !!membership_cost!!</p>
376
+ !!membership_expiration!! !!discount_code!!
377
+
378
+ <p>
379
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
380
+ Total Billed: !!invoice_total!!
381
+ </p>
382
+ <p>
383
+ Billing Information:<br />
384
+ !!billing_address!!
385
+ </p>
386
+
387
+ <p>
388
+ !!cardtype!!: !!accountnumber!!<br />
389
+ Expires: !!expirationmonth!!/!!expirationyear!!
390
+ </p>
391
+
392
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
393
+ 'help_text' => __( 'This is a membership confirmation welcome email sent to a new member or to existing members that change their level with a trial period.', 'paid-memberships-pro' )
394
+ ),
395
+ 'checkout_trial_admin' => array(
396
+ 'subject' => __( "Member checkout for !!membership_level_name!! at !!sitename!!", 'paid-memberships-pro' ),
397
+ 'description' => __('Checkout - Trial (admin)', 'paid-memberships-pro'),
398
+ 'body' => __( '<p>There was a new member checkout at !!sitename!!.</p>
399
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
400
+
401
+ <p>Account: !!display_name!! (!!user_email!!)</p>
402
+ <p>Membership Level: !!membership_level_name!!</p>
403
+ <p>Membership Fee: !!membership_cost!!</p>
404
+ !!membership_expiration!! !!discount_code!!
405
+
406
+ <p>
407
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
408
+ Total Billed: !!invoice_total!!
409
+ </p>
410
+ <p>
411
+ Billing Information:<br />
412
+ !!billing_address!!
413
+ </p>
414
+
415
+ <p>
416
+ !!cardtype!!: !!accountnumber!!<br />
417
+ Expires: !!expirationmonth!!/!!expirationyear!!
418
+ </p>
419
+
420
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
421
+ 'help_text' => __( 'This is the membership confirmation email sent to the site administrator for every membership checkout with a trial period.', 'paid-memberships-pro' )
422
+ ),
423
+ 'credit_card_expiring' => array(
424
+ 'subject' => __( "Credit card on file expiring soon at !!sitename!!", 'paid-memberships-pro' ),
425
+ 'description' => __('Credit Card Expiring', 'paid-memberships-pro'),
426
+ 'body' => __( '<p>The payment method used for your membership at !!sitename!! will expire soon. <strong>Please click the following link to log in and update your billing information to avoid account suspension. !!login_url!!</strong></p>
427
+
428
+ <p>Account: !!display_name!! (!!user_email!!)</p>
429
+ <p>The most recent account information we have on file is:</p>
430
+
431
+ <p>!!billing_name!!</br />
432
+ !!billing_address!!
433
+ </p>
434
+
435
+ <p>
436
+ !!cardtype!!: !!accountnumber!!<br />
437
+ Expires: !!expirationmonth!!/!!expirationyear!!
438
+ </p>', 'paid-memberships-pro' ),
439
+ 'help_text' => __( 'This email is sent when a member\'s payment method will be expiring soon. This allows the member to update their payment method before a payment failure, which may result in lost access to member features.', 'paid-memberships-pro' )
440
+ ),
441
+ 'invoice' => array(
442
+ 'subject' => __( "Invoice for !!sitename!! membership", 'paid-memberships-pro' ),
443
+ 'description' => __('Invoice', 'paid-memberships-pro'),
444
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Below is a receipt for your most recent membership invoice.</p>
445
+
446
+ <p>Account: !!display_name!! (!!user_email!!)</p>
447
+ <p>
448
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
449
+ Total Billed: !!invoice_total!!
450
+ </p>
451
+ <p>
452
+ Billing Information:<br />
453
+ !!billing_address!!
454
+ </p>
455
+
456
+ <p>
457
+ !!cardtype!!: !!accountnumber!!<br />
458
+ Expires: !!expirationmonth!!/!!expirationyear!!
459
+ </p>
460
+
461
+ <p>Log in to your membership account here: !!login_url!!</p>
462
+ <p>To view an online version of this invoice, click here: !!invoice_url!!</p>', 'paid-memberships-pro' ),
463
+ 'help_text' => __( 'This email is sent to the member each time a new subscription payment is made.', 'paid-memberships-pro' )
464
+ ),
465
+ 'membership_expired' => array(
466
+ 'subject' => __( "Your membership at !!sitename!! has ended", 'paid-memberships-pro' ),
467
+ 'description' => __('Membership Expired', 'paid-memberships-pro'),
468
+ 'body' => __( '<p>Your membership at !!sitename!! has ended.</p>
469
+
470
+ <p>Thank you for your support.</p>
471
+
472
+ <p>View our current membership offerings here: !!levels_url!!</p>
473
+
474
+ <p>Log in to manage your account here: !!login_url!!</p>', 'paid-memberships-pro' ),
475
+ 'help_text' => __( 'This email is sent to the member when their membership expires.', 'paid-memberships-pro' )
476
+ ),
477
+ 'membership_expiring' => array(
478
+ 'subject' => __( "Your membership at !!sitename!! will end soon", 'paid-memberships-pro' ),
479
+ 'description' => __('Membership Expiring', 'paid-memberships-pro'),
480
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. This is just a reminder that your membership will end on !!enddate!!.</p>
481
+
482
+ <p>Account: !!display_name!! (!!user_email!!)</p>
483
+ <p>Membership Level: !!membership_level_name!!</p>
484
+
485
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
486
+ 'help_text' => __( 'This email is sent to the member when their expiration date is approaching, at an interval based on the term of the membership.', 'paid-memberships-pro' )
487
+ ),
488
+ 'trial_ending' => array(
489
+ 'subject' => __( "Your trial at !!sitename!! is ending soon", 'paid-memberships-pro' ),
490
+ 'description' => __('Trial Ending', 'paid-memberships-pro'),
491
+ 'body' => __( '<p>Thank you for your membership to !!sitename!!. Your trial period is ending on !!trial_end!!.</p>
492
+
493
+ <p>Account: !!display_name!! (!!user_email!!)</p>
494
+ <p>Membership Level: !!membership_level_name!!</p>
495
+
496
+ <p>Your fee will be changing from !!trial_amount!! to !!billing_amount!! every !!cycle_number!! !!cycle_period!!(s).</p>
497
+
498
+ <p>Log in to your membership account here: !!login_url!!</p>', 'paid-memberships-pro' ),
499
+ 'help_text' => __( 'This email is sent to the member when the trial portion of their membership level is approaching, at an interval based on the term of the trial.', 'paid-memberships-pro' )
500
+ ),
501
+
502
+ );
503
+
504
+ // add SCA payment action required emails if we're using PMPro 2.1 or later
505
+ if( defined( 'PMPRO_VERSION' ) && version_compare( PMPRO_VERSION, '2.1' ) >= 0 ) {
506
+ $pmpro_email_templates_defaults = array_merge( $pmpro_email_templates_defaults, array(
507
+ 'payment_action' => array(
508
+ 'subject' => __( "Payment action required for your !!sitename!! membership", 'paid-memberships-pro' ),
509
+ 'description' => __('Payment Action Required', 'paid-memberships-pro' ),
510
+ 'body' => __( '<p>Customer authentication is required to finish setting up your subscription at !!sitename!!.</p>
511
+
512
+ <p>Please complete the verification steps issued by your payment provider at the following link:</p>
513
+ <p>!!invoice_url!!</p>', 'paid-memberships-pro' ),
514
+ 'help_text' => __( 'This email is sent to the user when an attempted membership checkout requires additional customer authentication.', 'paid-memberships-pro' )
515
+ ),
516
+ 'payment_action_admin' => array(
517
+ 'subject' => __( "Payment action required: membership for !!user_login!! at !!sitename!!", 'paid-memberships-pro' ),
518
+ 'description' => __('Payment Action Required (admin)', 'paid-memberships-pro'),
519
+ 'body' => __( '<p>A payment at !!sitename!! for !!user_login!! requires additional customer authentication to complete.</p>
520
+ <p>Below is a copy of the email we sent to !!user_email!! to notify them that they need to complete their payment:</p>
521
+
522
+ <p>Customer authentication is required to finish setting up your subscription at !!sitename!!.</p>
523
+
524
+ <p>Please complete the verification steps issued by your payment provider at the following link:</p>
525
+ <p>!!invoice_url!!</p>', 'paid-memberships-pro' ),
526
+ 'help_text' => __( 'This email is sent to the site administrator when an attempted membership checkout requires additional customer authentication.', 'paid-memberships-pro' )
527
+ )
528
+ ));
529
+ }
530
+
531
+ /**
532
+ * Filter default template settings and add new templates.
533
+ *
534
+ * @since 0.5.7
535
+ */
536
+ $pmpro_email_templates_defaults = apply_filters( 'pmproet_templates', $pmpro_email_templates_defaults );
includes/email.php CHANGED
@@ -1,4 +1,7 @@
1
  <?php
 
 
 
2
  /**
3
  * The default name for WP emails is WordPress.
4
  * Use our setting instead.
@@ -6,15 +9,15 @@
6
  function pmpro_wp_mail_from_name($from_name)
7
  {
8
  $default_from_name = 'WordPress';
9
-
10
  //make sure it's the default from name
11
  if($from_name == $default_from_name)
12
- {
13
  $pmpro_from_name = pmpro_getOption("from_name");
14
  if ($pmpro_from_name)
15
  $from_name = stripslashes($pmpro_from_name);
16
  }
17
-
18
  return $from_name;
19
  }
20
 
@@ -30,15 +33,15 @@ function pmpro_wp_mail_from($from_email)
30
  $sitename = substr( $sitename, 4 );
31
  }
32
  $default_from_email = 'wordpress@' . $sitename;
33
-
34
  //make sure it's the default email address
35
  if($from_email == $default_from_email)
36
- {
37
  $pmpro_from_email = pmpro_getOption("from_email");
38
  if ($pmpro_from_email && is_email( $pmpro_from_email ) )
39
  $from_email = $pmpro_from_email;
40
  }
41
-
42
  return $from_email;
43
  }
44
 
@@ -63,22 +66,22 @@ if(empty($email_member_notification))
63
  * Adds template files and changes content type to html if using PHPMailer directly.
64
  */
65
  function pmpro_send_html( $phpmailer ) {
66
-
67
  //to check if we should wpautop later
68
  $original_body = $phpmailer->Body;
69
-
70
  // Set the original plain text message
71
  $phpmailer->AltBody = wp_specialchars_decode($phpmailer->Body, ENT_QUOTES);
72
  // Clean < and > around text links in WP 3.1
73
- $phpmailer->Body = preg_replace('#<(https?://[^*]+)>#', '$1', $phpmailer->Body);
74
-
75
- // If there is no HTML, run through wpautop
76
  if($phpmailer->Body == strip_tags($phpmailer->Body))
77
  $phpmailer->Body = wpautop($phpmailer->Body);
78
-
79
  // Convert line breaks & make links clickable
80
  $phpmailer->Body = make_clickable ($phpmailer->Body);
81
-
82
  // Get header for message if found
83
  if(file_exists(get_stylesheet_directory() . "/email_header.html"))
84
  $header = file_get_contents(get_stylesheet_directory() . "/email_header.html");
@@ -86,11 +89,11 @@ function pmpro_send_html( $phpmailer ) {
86
  $header = file_get_contents(get_template_directory() . "/email_header.html");
87
  else
88
  $header = "";
89
-
90
  //wpautop header if needed
91
  if(!empty($header) && $header == strip_tags($header))
92
  $header = wpautop($header);
93
-
94
  // Get footer for message if found
95
  if(file_exists(get_stylesheet_directory() . "/email_footer.html"))
96
  $footer = file_get_contents(get_stylesheet_directory() . "/email_footer.html");
@@ -98,7 +101,7 @@ function pmpro_send_html( $phpmailer ) {
98
  $footer = file_get_contents(get_template_directory() . "/email_footer.html");
99
  else
100
  $footer = "";
101
-
102
  //wpautop header if needed
103
  if(!empty($footer) && $footer == strip_tags($footer))
104
  $footer = wpautop($footer);
@@ -111,13 +114,14 @@ function pmpro_send_html( $phpmailer ) {
111
  $phpmailer->Body = $header . "\n" . $phpmailer->Body;
112
  if(!empty($footer))
113
  $phpmailer->Body = $phpmailer->Body . "\n" . $footer;
114
-
115
  // Replace variables in email
116
  global $current_user;
117
  $data = array(
118
  "name" => $current_user->display_name,
119
  "sitename" => get_option("blogname"),
120
  "login_link" => pmpro_url("account"),
 
121
  "display_name" => $current_user->display_name,
122
  "user_email" => $current_user->user_email,
123
  "subject" => $phpmailer->Subject
@@ -125,8 +129,8 @@ function pmpro_send_html( $phpmailer ) {
125
  foreach($data as $key => $value)
126
  {
127
  $phpmailer->Body = str_replace("!!" . $key . "!!", $value, $phpmailer->Body);
128
- }
129
-
130
  do_action("pmpro_after_phpmailer_init", $phpmailer);
131
  do_action("pmpro_after_pmpmailer_init", $phpmailer); //typo left in for backwards compatibility
132
  }
@@ -134,14 +138,14 @@ function pmpro_send_html( $phpmailer ) {
134
  /**
135
  * Change the content type of emails to HTML.
136
  */
137
- function pmpro_wp_mail_content_type( $content_type ) {
138
  add_action('phpmailer_init', 'pmpro_send_html');
139
 
140
  // Change to html if not already.
141
- if( $content_type == 'text/plain') {
142
  $content_type = 'text/html';
143
  }
144
-
145
  return $content_type;
146
  }
147
  add_filter('wp_mail_content_type', 'pmpro_wp_mail_content_type');
@@ -152,15 +156,442 @@ add_filter('wp_mail_content_type', 'pmpro_wp_mail_content_type');
152
  * We check if there are already <br /> tags before running nl2br.
153
  * Running make_clickable() multiple times has no effect.
154
  */
155
- function pmpro_retrieve_password_message( $message ) {
156
- if ( has_filter( 'wp_mail_content_type', 'pmpro_wp_mail_content_type' ) ) {
157
  $message = make_clickable( $message );
158
-
159
- if ( strpos( '<br />', $message ) === false ) {
160
  $message = nl2br( $message );
161
- }
162
- }
163
 
164
  return $message;
165
  }
166
- add_filter( 'retrieve_password_message', 'pmpro_retrieve_password_message', 10, 1 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ // Sanitize all PMPro email bodies. @since 2.6.1
3
+ add_filter( 'pmpro_email_body', 'pmpro_kses', 11 );
4
+
5
  /**
6
  * The default name for WP emails is WordPress.
7
  * Use our setting instead.
9
  function pmpro_wp_mail_from_name($from_name)
10
  {
11
  $default_from_name = 'WordPress';
12
+
13
  //make sure it's the default from name
14
  if($from_name == $default_from_name)
15
+ {
16
  $pmpro_from_name = pmpro_getOption("from_name");
17
  if ($pmpro_from_name)
18
  $from_name = stripslashes($pmpro_from_name);
19
  }
20
+
21
  return $from_name;
22
  }
23
 
33
  $sitename = substr( $sitename, 4 );
34
  }
35
  $default_from_email = 'wordpress@' . $sitename;
36
+
37
  //make sure it's the default email address
38
  if($from_email == $default_from_email)
39
+ {
40
  $pmpro_from_email = pmpro_getOption("from_email");
41
  if ($pmpro_from_email && is_email( $pmpro_from_email ) )
42
  $from_email = $pmpro_from_email;
43
  }
44
+
45
  return $from_email;
46
  }
47
 
66
  * Adds template files and changes content type to html if using PHPMailer directly.
67
  */
68
  function pmpro_send_html( $phpmailer ) {
69
+
70
  //to check if we should wpautop later
71
  $original_body = $phpmailer->Body;
72
+
73
  // Set the original plain text message
74
  $phpmailer->AltBody = wp_specialchars_decode($phpmailer->Body, ENT_QUOTES);
75
  // Clean < and > around text links in WP 3.1
76
+ $phpmailer->Body = preg_replace('#<(https?://[^*]+)>#', '$1', $phpmailer->Body);
77
+
78
+ // If there is no HTML, run through wpautop
79
  if($phpmailer->Body == strip_tags($phpmailer->Body))
80
  $phpmailer->Body = wpautop($phpmailer->Body);
81
+
82
  // Convert line breaks & make links clickable
83
  $phpmailer->Body = make_clickable ($phpmailer->Body);
84
+
85
  // Get header for message if found
86
  if(file_exists(get_stylesheet_directory() . "/email_header.html"))
87
  $header = file_get_contents(get_stylesheet_directory() . "/email_header.html");
89
  $header = file_get_contents(get_template_directory() . "/email_header.html");
90
  else
91
  $header = "";
92
+
93
  //wpautop header if needed
94
  if(!empty($header) && $header == strip_tags($header))
95
  $header = wpautop($header);
96
+
97
  // Get footer for message if found
98
  if(file_exists(get_stylesheet_directory() . "/email_footer.html"))
99
  $footer = file_get_contents(get_stylesheet_directory() . "/email_footer.html");
101
  $footer = file_get_contents(get_template_directory() . "/email_footer.html");
102
  else
103
  $footer = "";
104
+
105
  //wpautop header if needed
106
  if(!empty($footer) && $footer == strip_tags($footer))
107
  $footer = wpautop($footer);
114
  $phpmailer->Body = $header . "\n" . $phpmailer->Body;
115
  if(!empty($footer))
116
  $phpmailer->Body = $phpmailer->Body . "\n" . $footer;
117
+
118
  // Replace variables in email
119
  global $current_user;
120
  $data = array(
121
  "name" => $current_user->display_name,
122
  "sitename" => get_option("blogname"),
123
  "login_link" => pmpro_url("account"),
124
+ "login_url" => pmpro_url("account"),
125
  "display_name" => $current_user->display_name,
126
  "user_email" => $current_user->user_email,
127
  "subject" => $phpmailer->Subject
129
  foreach($data as $key => $value)
130
  {
131
  $phpmailer->Body = str_replace("!!" . $key . "!!", $value, $phpmailer->Body);
132
+ }
133
+
134
  do_action("pmpro_after_phpmailer_init", $phpmailer);
135
  do_action("pmpro_after_pmpmailer_init", $phpmailer); //typo left in for backwards compatibility
136
  }
138
  /**
139
  * Change the content type of emails to HTML.
140
  */
141
+ function pmpro_wp_mail_content_type( $content_type ) {
142
  add_action('phpmailer_init', 'pmpro_send_html');
143
 
144
  // Change to html if not already.
145
+ if( $content_type == 'text/plain') {
146
  $content_type = 'text/html';
147
  }
148
+
149
  return $content_type;
150
  }
151
  add_filter('wp_mail_content_type', 'pmpro_wp_mail_content_type');
156
  * We check if there are already <br /> tags before running nl2br.
157
  * Running make_clickable() multiple times has no effect.
158
  */
159
+ function pmpro_retrieve_password_message( $message ) {
160
+ if ( has_filter( 'wp_mail_content_type', 'pmpro_wp_mail_content_type' ) ) {
161
  $message = make_clickable( $message );
162
+
163
+ if ( strpos( '<br', strtolower( $message ) ) === false ) {
164
  $message = nl2br( $message );
165
+ }
166
+ }
167
 
168
  return $message;
169
  }
170
+ add_filter( 'retrieve_password_message', 'pmpro_retrieve_password_message', 10, 1 );
171
+
172
+ //get template data
173
+ function pmpro_email_templates_get_template_data() {
174
+
175
+ check_ajax_referer('pmproet', 'security');
176
+
177
+ if ( ! current_user_can( 'pmpro_emailtemplates' ) ) {
178
+ die( __( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) );
179
+ }
180
+
181
+ global $pmpro_email_templates_defaults;
182
+
183
+ $template = sanitize_text_field( $_REQUEST['template'] );
184
+
185
+ //get template data
186
+ $template_data['body'] = pmpro_getOption('email_' . $template . '_body');
187
+ $template_data['subject'] = pmpro_getOption('email_' . $template . '_subject');
188
+ $template_data['disabled'] = pmpro_getOption('email_' . $template . '_disabled');
189
+
190
+ if (empty($template_data['body'])) {
191
+ //if not found, load template
192
+ $template_data['body'] = pmpro_email_templates_get_template_body($template);
193
+ }
194
+
195
+ if (empty($template_data['subject']) && $template != "header" && $template != "footer") {
196
+ $template_data['subject'] = $pmpro_email_templates_defaults[$template]['subject'];
197
+ }
198
+
199
+ // Get template help text from defaults.
200
+ $template_data['help_text'] = $pmpro_email_templates_defaults[$template]['help_text'];
201
+
202
+ echo json_encode($template_data);
203
+
204
+ exit;
205
+ }
206
+ add_action('wp_ajax_pmpro_email_templates_get_template_data', 'pmpro_email_templates_get_template_data');
207
+
208
+ //save template data
209
+ function pmpro_email_templates_save_template_data() {
210
+
211
+ check_ajax_referer('pmproet', 'security');
212
+
213
+ if ( ! current_user_can( 'pmpro_emailtemplates' ) ) {
214
+ die( __( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) );
215
+ }
216
+
217
+ $template = sanitize_text_field( $_REQUEST['template'] );
218
+ $subject = sanitize_text_field( wp_unslash( $_REQUEST['subject'] ) );
219
+ $body = pmpro_kses( wp_unslash( $_REQUEST['body'] ), 'email' );
220
+
221
+ //update this template's settings
222
+ pmpro_setOption( 'email_' . $template . '_subject', $subject );
223
+ pmpro_setOption( 'email_' . $template . '_body', $body );
224
+ delete_transient( 'pmproet_' . $template );
225
+ esc_html_e( 'Template Saved', 'paid-memberships-pro' );
226
+
227
+ exit;
228
+ }
229
+ add_action('wp_ajax_pmpro_email_templates_save_template_data', 'pmpro_email_templates_save_template_data');
230
+
231
+ //reset template data
232
+ function pmpro_email_templates_reset_template_data() {
233
+
234
+ check_ajax_referer('pmproet', 'security');
235
+
236
+ if ( ! current_user_can( 'pmpro_emailtemplates' ) ) {
237
+ die( __( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) );
238
+ }
239
+
240
+ global $pmpro_email_templates_defaults;
241
+
242
+ $template = sanitize_text_field( $_REQUEST['template'] );
243
+
244
+ delete_option('pmpro_email_' . $template . '_subject');
245
+ delete_option('pmpro_email_' . $template . '_body');
246
+ delete_transient( 'pmproet_' . $template );
247
+
248
+ $template_data['subject'] = $pmpro_email_templates_defaults[$template]['subject'];
249
+ $template_data['body'] = pmpro_email_templates_get_template_body($template);
250
+
251
+ echo json_encode($template_data);
252
+ exit;
253
+ }
254
+ add_action('wp_ajax_pmpro_email_templates_reset_template_data', 'pmpro_email_templates_reset_template_data');
255
+
256
+ // disable template
257
+ function pmpro_email_templates_disable_template() {
258
+
259
+ check_ajax_referer('pmproet', 'security');
260
+
261
+ if ( ! current_user_can( 'pmpro_emailtemplates' ) ) {
262
+ die( __( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) );
263
+ }
264
+
265
+ $template = sanitize_text_field( $_REQUEST['template'] );
266
+ $disabled = sanitize_text_field( $_REQUEST['disabled'] );
267
+ $response['result'] = update_option('pmpro_email_' . $template . '_disabled', $disabled );
268
+ $response['status'] = $disabled;
269
+ echo json_encode($response);
270
+ exit;
271
+ }
272
+ add_action('wp_ajax_pmpro_email_templates_disable_template', 'pmpro_email_templates_disable_template');
273
+
274
+ //send test email
275
+ function pmpro_email_templates_send_test() {
276
+
277
+ check_ajax_referer('pmproet', 'security');
278
+
279
+ if ( ! current_user_can( 'pmpro_emailtemplates' ) ) {
280
+ die( __( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) );
281
+ }
282
+
283
+ global $current_user;
284
+
285
+ //setup test email
286
+ $test_email = new PMProEmail();
287
+ $test_email->to = sanitize_email( $_REQUEST['email'] );
288
+ $test_email->template = sanitize_text_field( str_replace('email_', '', $_REQUEST['template']) );
289
+
290
+ //add filter to change recipient
291
+ add_filter('pmpro_email_recipient', 'pmpro_email_templates_test_recipient', 10, 2);
292
+
293
+ //load test order
294
+ $test_order = new MemberOrder();
295
+ $test_order->get_test_order();
296
+
297
+ $test_user = $current_user;
298
+
299
+ // Grab the first membership level defined as a "test level" to use
300
+ $all_levels = pmpro_getAllLevels( true);
301
+ $test_user->membership_level = array_pop( $all_levels );
302
+
303
+ //add notice to email body
304
+ add_filter('pmpro_email_body', 'pmpro_email_templates_test_body', 10, 2);
305
+
306
+ //force the template
307
+ add_filter('pmpro_email_filter', 'pmpro_email_templates_test_template', 5, 1);
308
+
309
+ //figure out how to send the email
310
+ switch($test_email->template) {
311
+ case 'cancel':
312
+ $send_email = 'sendCancelEmail';
313
+ $params = array($test_user);
314
+ break;
315
+ case 'cancel_admin':
316
+ $send_email = 'sendCancelAdminEmail';
317
+ $params = array($current_user, $current_user->membership_level->id);
318
+ break;
319
+ case 'checkout_check':
320
+ case 'checkout_express':
321
+ case 'checkout_free':
322
+ case 'checkout_freetrial':
323
+ case 'checkout_paid':
324
+ case 'checkout_trial':
325
+ $send_email = 'sendCheckoutEmail';
326
+ $params = array($test_user, $test_order);
327
+ break;
328
+ case 'checkout_check_admin':
329
+ case 'checkout_express_admin':
330
+ case 'checkout_free_admin':
331
+ case 'checkout_freetrial_admin':
332
+ case 'checkout_paid_admin':
333
+ case 'checkout_trial_admin':
334
+ $send_email = 'sendCheckoutAdminEmail';
335
+ $params = array($test_user, $test_order);
336
+ break;
337
+ case 'billing':
338
+ $send_email = 'sendBillingEmail';
339
+ $params = array($test_user, $test_order);
340
+ break;
341
+ case 'billing_admin':
342
+ $send_email = 'sendBillingAdminEmail';
343
+ $params = array($test_user, $test_order);
344
+ break;
345
+ case 'billing_failure':
346
+ $send_email = 'sendBillingFailureEmail';
347
+ $params = array($test_user, $test_order);
348
+ break;
349
+ case 'billing_failure_admin':
350
+ $send_email = 'sendBillingFailureAdminEmail';
351
+ $params = array($test_user->user_email, $test_order);
352
+ break;
353
+ case 'credit_card_expiring':
354
+ $send_email = 'sendCreditCardExpiringEmail';
355
+ $params = array($test_user, $test_order);
356
+ break;
357
+ case 'invoice':
358
+ $send_email = 'sendInvoiceEmail';
359
+ $params = array($test_user, $test_order);
360
+ break;
361
+ case 'trial_ending':
362
+ $send_email = 'sendTrialEndingEmail';
363
+ $params = array($test_user);
364
+ break;
365
+ case 'membership_expired';
366
+ $send_email = 'sendMembershipExpiredEmail';
367
+ $params = array($test_user);
368
+ break;
369
+ case 'membership_expiring';
370
+ $send_email = 'sendMembershipExpiringEmail';
371
+ $params = array($test_user);
372
+ break;
373
+ case 'payment_action':
374
+ $send_email = 'sendPaymentActionRequiredEmail';
375
+ $params = array($test_user, $test_order, "http://www.example-notification-url.com/not-a-real-site");
376
+ break;
377
+ case 'payment_action_admin':
378
+ $send_email = 'sendPaymentActionRequiredAdminEmail';
379
+ $params = array($test_user, $test_order, "http://www.example-notification-url.com/not-a-real-site");
380
+ break;
381
+ default:
382
+ $send_email = 'sendEmail';
383
+ $params = array();
384
+ }
385
+
386
+ //send the email
387
+ $response = call_user_func_array(array($test_email, $send_email), $params);
388
+
389
+ //return the response
390
+ echo $response;
391
+ exit;
392
+ }
393
+ add_action('wp_ajax_pmpro_email_templates_send_test', 'pmpro_email_templates_send_test');
394
+
395
+ function pmpro_email_templates_test_recipient($email) {
396
+ if(!empty($_REQUEST['email']))
397
+ $email = sanitize_email( $_REQUEST['email'] );
398
+ return $email;
399
+ }
400
+
401
+ //for test emails
402
+ function pmpro_email_templates_test_body($body, $email = null) {
403
+ $body .= '<br /><br /><b>-- ' . __('THIS IS A TEST EMAIL', 'paid-memberships-pro') . ' --</b>';
404
+ return $body;
405
+ }
406
+
407
+ function pmpro_email_templates_test_template($email)
408
+ {
409
+ if( ! empty( $_REQUEST['template'] ) ) {
410
+ $email->template = sanitize_text_field( str_replace('email_', '', $_REQUEST['template']) );
411
+ }
412
+
413
+ return $email;
414
+ }
415
+
416
+ /* Filter for Variables */
417
+ function pmpro_email_templates_email_data($data, $email) {
418
+
419
+ global $pmpro_currency_symbol;
420
+
421
+ if ( ! empty( $data ) && ! empty( $data['user_login'] ) ) {
422
+ $user = get_user_by( 'login', $data['user_login'] );
423
+ } elseif ( ! empty( $email ) ) {
424
+ $user = get_user_by( 'email', $email->email );
425
+ } else {
426
+ $user = wp_get_current_user();
427
+ }
428
+
429
+ // Make sure we have the current membership level data.
430
+ if ( $user instanceof WP_User ) {
431
+ $user->membership_level = pmpro_getMembershipLevelForUser(
432
+ $user->ID,
433
+ true
434
+ );
435
+ }
436
+
437
+ //make sure data is an array
438
+ if(!is_array($data))
439
+ $data = array();
440
+
441
+ //general data
442
+ $new_data['sitename'] = get_option("blogname");
443
+ $new_data['siteemail'] = pmpro_getOption("from_email");
444
+ if(empty($new_data['login_link'])) {
445
+ $new_data['login_link'] = wp_login_url();
446
+ $new_data['login_url'] = wp_login_url();
447
+ }
448
+ $new_data['levels_link'] = pmpro_url("levels");
449
+
450
+ // User Data.
451
+ if ( ! empty( $user ) ) {
452
+ $new_data['name'] = $user->display_name;
453
+ $new_data['user_login'] = $user->user_login;
454
+ $new_data['display_name'] = $user->display_name;
455
+ $new_data['user_email'] = $user->user_email;
456
+
457
+ // Membership Information.
458
+ $new_data['membership_expiration'] = '';
459
+ $new_data["membership_change"] = __("Your membership has been cancelled.", "paid-memberships-pro");
460
+ if ( empty( $user->membership_level ) ) {
461
+ $user->membership_level = pmpro_getMembershipLevelForUser($user->ID, true);
462
+ }
463
+ if ( ! empty( $user->membership_level ) ) {
464
+ if ( ! empty( $user->membership_level->name ) ) {
465
+ $new_data["membership_change"] = sprintf(__("The new level is %s.", "paid-memberships-pro"), $user->membership_level->name);
466
+ }
467
+ if ( ! empty($user->membership_level->startdate) ) {
468
+ $new_data['startdate'] = date_i18n( get_option( 'date_format' ), $user->membership_level->startdate );
469
+ }
470
+ if ( ! empty($user->membership_level->enddate) ) {
471
+ $new_data['enddate'] = date_i18n( get_option( 'date_format' ), $user->membership_level->enddate );
472
+ $new_data['membership_expiration'] = "<p>" . sprintf( __("This membership will expire on %s.", "paid-memberships-pro"), date_i18n( get_option( 'date_format' ), $user->membership_level->enddate ) ) . "</p>\n";
473
+ $new_data["membership_change"] .= " " . sprintf(__("This membership will expire on %s.", "paid-memberships-pro"), date_i18n( get_option( 'date_format' ), $user->membership_level->enddate ) );
474
+ } else if ( ! empty( $email->expiration_changed ) ) {
475
+ $new_data["membership_change"] .= " " . __("This membership does not expire.", "paid-memberships-pro");
476
+ }
477
+ }
478
+ }
479
+
480
+ //invoice data
481
+ if(!empty($data['invoice_id']))
482
+ {
483
+ $invoice = new MemberOrder($data['invoice_id']);
484
+ if(!empty($invoice) && !empty($invoice->code))
485
+ {
486
+ $new_data['billing_name'] = $invoice->billing->name;
487
+ $new_data['billing_street'] = $invoice->billing->street;
488
+ $new_data['billing_city'] = $invoice->billing->city;
489
+ $new_data['billing_state'] = $invoice->billing->state;
490
+ $new_data['billing_zip'] = $invoice->billing->zip;
491
+ $new_data['billing_country'] = $invoice->billing->country;
492
+ $new_data['billing_phone'] = $invoice->billing->phone;
493
+ $new_data['cardtype'] = $invoice->cardtype;
494
+ $new_data['accountnumber'] = hideCardNumber($invoice->accountnumber);
495
+ $new_data['expirationmonth'] = $invoice->expirationmonth;
496
+ $new_data['expirationyear'] = $invoice->expirationyear;
497
+ $new_data['instructions'] = wpautop(pmpro_getOption('instructions'));
498
+ $new_data['invoice_id'] = $invoice->code;
499
+ $new_data['invoice_total'] = $pmpro_currency_symbol . number_format($invoice->total, 2);
500
+ $new_data['invoice_date'] = date_i18n( get_option( 'date_format' ), $invoice->getTimestamp() );
501
+ $new_data['invoice_link'] = pmpro_url('invoice', '?invoice=' . $invoice->code);
502
+
503
+ //billing address
504
+ $new_data["billing_address"] = pmpro_formatAddress($invoice->billing->name,
505
+ $invoice->billing->street,
506
+ "", //address 2
507
+ $invoice->billing->city,
508
+ $invoice->billing->state,
509
+ $invoice->billing->zip,
510
+ $invoice->billing->country,
511
+ $invoice->billing->phone);
512
+ }
513
+ }
514
+
515
+ //if others are used in the email look in usermeta
516
+ $et_body = pmpro_getOption('email_' . $email->template . '_body');
517
+ $templates_in_email = preg_match_all("/!!([^!]+)!!/", $et_body, $matches);
518
+ if ( ! empty( $templates_in_email ) && ! empty( $user->ID ) ) {
519
+ $matches = $matches[1];
520
+ foreach($matches as $match) {
521
+ if ( empty( $new_data[ $match ] ) ) {
522
+ $usermeta = get_user_meta($user->ID, $match, true);
523
+ if ( ! empty( $usermeta ) ) {
524
+ if( is_array( $usermeta ) && ! empty( $usermeta['fullurl'] ) ) {
525
+ $new_data[$match] = $usermeta['fullurl'];
526
+ } elseif( is_array($usermeta ) ) {
527
+ $new_data[$match] = implode(", ", $usermeta);
528
+ } else {
529
+ $new_data[$match] = $usermeta;
530
+ }
531
+ }
532
+ }
533
+ }
534
+ }
535
+
536
+ //now replace any new_data not already in data
537
+ foreach($new_data as $key => $value)
538
+ {
539
+ if(!isset($data[$key]))
540
+ $data[$key] = $value;
541
+ }
542
+
543
+ return $data;
544
+ }
545
+ add_filter('pmpro_email_data', 'pmpro_email_templates_email_data', 10, 2);
546
+
547
+
548
+ /**
549
+ * Load the default email template.
550
+ *
551
+ * Checks theme, then template, then PMPro directory.
552
+ *
553
+ * @since 0.6
554
+ *
555
+ * @param $template string
556
+ *
557
+ * @return string
558
+ */
559
+ function pmpro_email_templates_get_template_body($template) {
560
+
561
+ global $pmpro_email_templates_defaults;
562
+
563
+ // Defaults
564
+ $body = "";
565
+ $file = false;
566
+
567
+
568
+ // Load the template.
569
+ if ( get_transient( 'pmproet_' . $template ) === false ) {
570
+ // Load template
571
+ if ( ! empty( pmpro_getOption('email_' . $template . '_body') ) ) {
572
+ $body = pmpro_getOption('email_' . $template . '_body');
573
+ }elseif( ! empty($pmpro_email_templates_defaults[$template]['body'])) {
574
+ $body = $pmpro_email_templates_defaults[$template]['body'];
575
+ } elseif ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/email/' . $template . '.html' ) ) {
576
+ $file = get_stylesheet_directory() . '/paid-memberships-pro/email/' . $template . '.html';
577
+ } elseif ( file_exists( get_template_directory() . '/paid-memberships-pro/email/' . $template . '.html') ) {
578
+ $file = get_template_directory() . '/paid-memberships-pro/email/' . $template . '.html';
579
+ }
580
+
581
+ if( $file && ! $body ) {
582
+ ob_start();
583
+ require_once( $file );
584
+ $body = ob_get_contents();
585
+ ob_end_clean();
586
+ }
587
+
588
+ if ( ! empty( $body ) ) {
589
+ set_transient( 'pmproet_' . $template, $body, 300 );
590
+ }
591
+ } else {
592
+ $body = get_transient( 'pmproet_' . $template );
593
+ }
594
+
595
+
596
+ return $body;
597
+ }
includes/functions.php CHANGED
@@ -39,6 +39,7 @@ function pmpro_setDBTables() {
39
  $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels';
40
  $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses';
41
  $wpdb->pmpro_membership_levelmeta = $wpdb->prefix . 'pmpro_membership_levelmeta';
 
42
  }
43
  pmpro_setDBTables();
44
 
@@ -83,7 +84,7 @@ function pmpro_getOption( $s, $force = false ) {
83
  }
84
  }
85
 
86
- function pmpro_setOption( $s, $v = null, $sanitize_function = 'sanitize_text_field' ) {
87
  // no value is given, set v to the p var
88
  if ( $v === null && isset( $_POST[ $s ] ) ) {
89
  if ( is_array( $_POST[ $s ] ) ) {
@@ -99,7 +100,7 @@ function pmpro_setOption( $s, $v = null, $sanitize_function = 'sanitize_text_fie
99
  $v = trim( $v );
100
  }
101
 
102
- return update_option( 'pmpro_' . $s, $v );
103
  }
104
 
105
  function pmpro_get_slug( $post_id ) {
@@ -131,15 +132,19 @@ function pmpro_url( $page = null, $querystring = '', $scheme = null ) {
131
 
132
  global $pmpro_pages;
133
 
134
- // start with the permalink
135
- $url = get_permalink( $pmpro_pages[ $page ] );
 
136
 
137
- // WPML/etc support
138
- if ( function_exists( 'icl_object_id' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
139
- $trans_id = icl_object_id( $pmpro_pages[ $page ], 'page', false, ICL_LANGUAGE_CODE );
140
- if ( ! empty( $trans_id ) ) {
141
- $url = get_permalink( $trans_id );
 
142
  }
 
 
143
  }
144
 
145
  // figure out querystring
@@ -155,7 +160,7 @@ function pmpro_url( $page = null, $querystring = '', $scheme = null ) {
155
  $url = str_replace( 'http:', 'https:', $url );
156
  }
157
  }
158
-
159
  /**
160
  * Filter the URL before returning.
161
  */
@@ -170,7 +175,7 @@ function pmpro_isLevelFree( &$level ) {
170
  } else {
171
  $r = false;
172
  }
173
-
174
  $r = apply_filters( 'pmpro_is_level_free', $r, $level );
175
  return $r;
176
  }
@@ -196,7 +201,7 @@ function pmpro_areLevelsFree( $levelarr ) {
196
  function pmpro_onlyFreeLevels() {
197
  // Get levels that are available for checkout only.
198
  $levels = pmpro_getAllLevels( false, true );
199
-
200
  return apply_filters( 'pmpro_only_free_levels', pmpro_areLevelsFree( $levels ) );
201
  }
202
 
@@ -206,7 +211,7 @@ function pmpro_isLevelRecurring( &$level ) {
206
  } else {
207
  $r = false;
208
  }
209
-
210
  $r = apply_filters( 'pmpro_is_level_recurring', $r, $level );
211
  return $r;
212
  }
@@ -219,26 +224,26 @@ function pmpro_isLevelRecurring( &$level ) {
219
  */
220
  function pmpro_has_recurring_level( $user_id = null ) {
221
  global $current_user;
222
-
223
  if ( empty( $user_id ) ) {
224
  $user_id = $current_user->ID;
225
  }
226
-
227
  if ( empty( $user_id ) ) {
228
  return false;
229
  }
230
 
231
- $levels = pmpro_getMembershipLevelsForUser( $user_id );
232
-
233
  if ( empty( $levels ) ) {
234
  return false;
235
  }
236
-
237
- foreach( $levels as $level ) {
238
  if ( pmpro_isLevelRecurring( $level ) ) {
239
  return true;
240
  }
241
- }
242
 
243
  return false;
244
  }
@@ -249,18 +254,19 @@ function pmpro_isLevelTrial( &$level ) {
249
  } else {
250
  $r = false;
251
  }
252
-
253
  $r = apply_filters( 'pmpro_is_level_trial', $r, $level );
254
  return $r;
255
  }
256
 
257
  function pmpro_isLevelExpiring( &$level ) {
258
  if ( ! empty( $level ) && ( ! empty( $level->expiration_number ) && $level->expiration_number > 0 ) || ! empty( $level->enddate ) ) {
 
259
  $r = true;
260
  } else {
261
  $r = false;
262
  }
263
-
264
  $r = apply_filters( 'pmpro_is_level_expiring', $r, $level );
265
  return $r;
266
  }
@@ -280,7 +286,9 @@ function pmpro_isLevelExpiringSoon( &$level ) {
280
  $standard = pmpro_getLevel( $level->id );
281
 
282
  if ( ! empty( $standard->expiration_number ) ) {
283
- if ( $standard->expiration_period == 'Day' ) {
 
 
284
  $days = $level->expiration_number;
285
  } elseif ( $standard->expiration_period == 'Week' ) {
286
  $days = $level->expiration_number * 7;
@@ -296,7 +304,13 @@ function pmpro_isLevelExpiringSoon( &$level ) {
296
  // are we within the days til expiration?
297
  $now = current_time( 'timestamp' );
298
 
299
- if ( $now + ( $days * 3600 * 24 ) >= $level->enddate ) {
 
 
 
 
 
 
300
  $r = true;
301
  } else {
302
  $r = false;
@@ -378,11 +392,15 @@ function pmpro_getLevelCost( &$level, $tags = true, $short = false ) {
378
  if ( ! $short ) {
379
  $r = sprintf( __( 'The price for membership is <strong>%s</strong> now', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ) );
380
  } else {
381
- $r = sprintf( __( '<strong>%s</strong> now', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ) );
 
 
 
 
382
  }
383
 
384
  // recurring part
385
- if ( $level->billing_amount != '0.00' ) {
386
  if ( $level->billing_limit > 1 ) {
387
  if ( $level->cycle_number == '1' ) {
388
  $r .= sprintf( __( ' and then <strong>%1$s per %2$s for %3$d more %4$s</strong>.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->billing_amount ), pmpro_translate_billing_period( $level->cycle_period ), $level->billing_limit, pmpro_translate_billing_period( $level->cycle_period, $level->billing_limit ) );
@@ -423,7 +441,7 @@ function pmpro_getLevelCost( &$level, $tags = true, $short = false ) {
423
 
424
  // trial part
425
  if ( $level->trial_limit ) {
426
- if ( $level->trial_amount == '0.00' ) {
427
  if ( $level->trial_limit == '1' ) {
428
  $r .= ' ' . __( 'After your initial payment, your first payment is Free.', 'paid-memberships-pro' );
429
  } else {
@@ -463,7 +481,7 @@ function pmpro_getLevelsCost( &$levels, $tags = true, $short = false ) {
463
  $trialperiods = 0;
464
  foreach ( $levels as $curlevel ) {
465
  $initpmt += $curlevel->initial_payment;
466
- if ( $curlevel->billing_amount != '0.00' ) {
467
  if ( array_key_exists( $curlevel->cycle_period, $recurpmts ) ) {
468
  if ( array_key_exists( $curlevel->cycle_number, $recurpmts[ $curlevel->cycle_period ] ) ) {
469
  if ( array_key_exists( $curlevel->billing_limit, $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ] ) ) {
@@ -559,6 +577,7 @@ function pmpro_getLevelsCost( &$levels, $tags = true, $short = false ) {
559
  }
560
 
561
  function pmpro_getLevelExpiration( &$level ) {
 
562
  if ( $level->expiration_number ) {
563
  $expiration_text = sprintf( __( 'Membership expires after %1$d %2$s.', 'paid-memberships-pro' ), $level->expiration_number, pmpro_translate_billing_period( $level->expiration_period, $level->expiration_number ) );
564
  } else {
@@ -607,7 +626,7 @@ function pmpro_getLevelsExpiration( &$levels ) {
607
  * Filter the levels expiration text. Note the s in levels. Similar to pmpro_levels_expiration_text
608
  */
609
  $expiration_text = apply_filters( 'pmpro_levels_expiration_text', $expiration_text, $levels );
610
-
611
  // Backwards compatible
612
  if ( ! empty( $levels ) ) {
613
  $first_level = reset($levels);
@@ -615,7 +634,7 @@ function pmpro_getLevelsExpiration( &$levels ) {
615
  $first_level = false;
616
  }
617
  $expiration_text = apply_filters( 'pmpro_level_expiration_text', $expiration_text, $first_level );
618
-
619
  return $expiration_text;
620
  }
621
 
@@ -628,7 +647,7 @@ function add_pmpro_membership_level_meta( $level_id, $meta_key, $meta_value, $un
628
  return add_metadata( 'pmpro_membership_level', $level_id, $meta_key, $meta_value, $unique );
629
  }
630
 
631
- function get_pmpro_membership_level_meta( $level_id, $key, $single = false ) {
632
  return get_metadata( 'pmpro_membership_level', $level_id, $key, $single );
633
  }
634
 
@@ -640,6 +659,25 @@ function delete_pmpro_membership_level_meta( $level_id, $meta_key, $meta_value =
640
  return delete_metadata( 'pmpro_membership_level', $level_id, $meta_key, $meta_value );
641
  }
642
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
643
  function pmpro_hideAds() {
644
  global $pmpro_display_ads;
645
  return ! $pmpro_display_ads;
@@ -922,11 +960,12 @@ function pmpro_cancelMembershipLevel( $cancel_level, $user_id = null, $old_level
922
  * @param int $level ID of level to set as new level, use 0 to cancel membership
923
  * @param int $user_id ID of the user to change levels for
924
  * @param string $old_level_status The status to set for the row in the memberships users table. (e.g. inactive, cancelled, admin_cancelled, expired) Defaults to 'inactive'.
925
- * $param int $cancel_level If set cancel just this one level instead of all active levels (to support Multiple Memberships per User)
926
  *
927
  * Return values:
928
  * Success returns boolean true.
929
  * Failure returns boolean false.
 
930
  */
931
  function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status = 'inactive', $cancel_level = null ) {
932
  global $wpdb;
@@ -944,6 +983,12 @@ function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status
944
  // make sure user id is int for security
945
  $user_id = intval( $user_id );
946
 
 
 
 
 
 
 
947
  if ( empty( $level ) ) {
948
  $level = 0;
949
  } else if ( is_array( $level ) ) {
@@ -974,14 +1019,21 @@ function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status
974
  if ( ! is_array( $level ) ) {
975
  // are they even changing?
976
  if ( pmpro_hasMembershipLevel( $level, $user_id ) ) {
977
- $pmpro_error = __( 'not changing?', 'paid-memberships-pro' );
978
- return false; // not changing
979
  }
980
  }
981
 
982
  // get all active membershipships for this user
983
  $old_levels = pmpro_getMembershipLevelsForUser( $user_id );
984
 
 
 
 
 
 
 
 
 
985
  // get level id
986
  if ( is_array( $level ) ) {
987
  $level_id = $level['membership_id']; // custom level
@@ -995,7 +1047,7 @@ function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status
995
  * @param int $level_id ID of the level changed to.
996
  * @param int $user_id ID of the user changed.
997
  * @param array $old_levels array of prior levels the user belonged to.
998
- * $param int $cancel_level ID of the level being cancelled if specified
999
  */
1000
  do_action( 'pmpro_before_change_membership_level', $level_id, $user_id, $old_levels, $cancel_level );
1001
 
@@ -1074,10 +1126,10 @@ function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status
1074
 
1075
  if ( ! empty( $c_order->error ) ) {
1076
  $pmpro_error = $c_order->error;
1077
- } else {
1078
- if( $old_level_status == 'error' ) {
1079
- $c_order->updateStatus("error");
1080
- }
1081
  }
1082
  }
1083
  }
@@ -1139,15 +1191,48 @@ function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status
1139
  $pmpro_error = sprintf( __( 'Error interacting with database: %s', 'paid-memberships-pro' ), ( ! empty( $wpdb->last_error ) ? $wpdb->last_error : 'unavailable' ) );
1140
  return false;
1141
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1142
  }
1143
 
1144
  // remove cached level
1145
  global $all_membership_levels;
1146
  unset( $all_membership_levels[ $user_id ] );
1147
-
1148
  // remove levels cache for user
1149
  $cache_key = 'user_' . $user_id . '_levels';
1150
  wp_cache_delete( $cache_key, 'pmpro' );
 
 
1151
 
1152
  // update user data and call action
1153
  pmpro_set_current_user();
@@ -1157,12 +1242,44 @@ function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status
1157
  *
1158
  * @param int $level_id ID of the level changed to.
1159
  * @param int $user_id ID of the user changed.
1160
- * $param int $cancel_level ID of the level being cancelled if specified.
1161
  */
1162
  do_action( 'pmpro_after_change_membership_level', $level_id, $user_id, $cancel_level );
1163
  return true;
1164
  }
1165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1166
  /**
1167
  * Function to list WordPress categories in hierarchical format.
1168
  *
@@ -1200,8 +1317,9 @@ function pmpro_listCategories( $parent_id = 0, $level_categories = array() ) {
1200
  /*
1201
  pmpro_toggleMembershipCategory() creates or deletes a linking entry between the membership level and post category tables.
1202
  *
1203
- * $level may either be the ID or name of the desired membership_level.
1204
- * $category must be a valid post category ID.
 
1205
  *
1206
  * Return values:
1207
  * Success returns boolean true.
@@ -1239,8 +1357,8 @@ function pmpro_toggleMembershipCategory( $level, $category, $value ) {
1239
  pmpro_updateMembershipCategories() ensures that all those and only those categories given
1240
  * are associated with the given membership level.
1241
  *
1242
- * $level is a valid membership level ID or name
1243
- * $categories is an array of post category IDs
1244
  *
1245
  * Return values:
1246
  * Success returns boolean true.
@@ -1278,7 +1396,7 @@ function pmpro_updateMembershipCategories( $level, $categories ) {
1278
  /*
1279
  pmpro_getMembershipCategories() returns the categories for a given level
1280
  *
1281
- * $level_id is a valid membership level ID
1282
  *
1283
  * Return values:
1284
  * Success returns boolean true.
@@ -1291,7 +1409,7 @@ function pmpro_getMembershipCategories( $level_id ) {
1291
  $categories = $wpdb->get_col(
1292
  "SELECT c.category_id
1293
  FROM {$wpdb->pmpro_memberships_categories} AS c
1294
- WHERE c.membership_id = '" . $level_id . "'"
1295
  );
1296
 
1297
  return $categories;
@@ -1299,7 +1417,7 @@ function pmpro_getMembershipCategories( $level_id ) {
1299
 
1300
 
1301
  function pmpro_isAdmin( $user_id = null ) {
1302
- global $current_user, $wpdb;
1303
  if ( ! $user_id ) {
1304
  $user_id = $current_user->ID;
1305
  }
@@ -1355,7 +1473,7 @@ function pmpro_getMetavalues( $query ) {
1355
  }
1356
 
1357
  // function to return the pagination string
1358
- function pmpro_getPaginationString( $page = 1, $totalitems, $limit = 15, $adjacents = 1, $targetpage = '/', $pagestring = '&pn=' ) {
1359
  // defaults
1360
  if ( ! $adjacents ) {
1361
  $adjacents = 1;
@@ -1515,6 +1633,8 @@ function pmpro_calculateRecurringRevenue( $s, $l ) {
1515
  UNION
1516
  SELECT SUM((365/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Day' AND cycle_number <> 365 $user_ids_query
1517
  UNION
 
 
1518
  SELECT SUM((52/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Week' AND cycle_number <> 52 $user_ids_query
1519
  UNION
1520
  SELECT SUM(billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Year' $user_ids_query
@@ -1560,7 +1680,7 @@ function pmpro_generateUsername( $firstname = '', $lastname = '', $email = '' )
1560
  $username = $lastname;
1561
  }
1562
 
1563
- // If no username yet or one based on name exisdts,
1564
  // try to create username using email address.
1565
  if ( ( empty( $username ) || username_exists( $username ) )
1566
  && ! empty( $email ) && is_email( $email ) ) {
@@ -1605,8 +1725,12 @@ function pmpro_generateUsername( $firstname = '', $lastname = '', $email = '' )
1605
  function pmpro_getDiscountCode( $seed = null ) {
1606
  global $wpdb;
1607
 
 
 
 
 
1608
  while ( empty( $code ) ) {
1609
- $scramble = md5( AUTH_KEY . current_time( 'timestamp' ) . $seed . SECURE_AUTH_KEY );
1610
  $code = substr( $scramble, 0, 10 );
1611
  $check = $wpdb->get_var( "SELECT code FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql( $code ) . "' LIMIT 1" );
1612
  if ( $check || is_numeric( $code ) ) {
@@ -1646,7 +1770,7 @@ function pmpro_checkDiscountCode( $code, $level_id = null, $return_errors = fals
1646
  $dbcode->expires = strtotime( date_i18n( 'm/d/Y', $dbcode->expires ) );
1647
 
1648
  // today
1649
- $today = strtotime( date_i18n( 'm/d/Y 00:00:00', current_time( 'timestamp' ) ) );
1650
 
1651
  // has this code started yet?
1652
  if ( ! empty( $dbcode->starts ) && $dbcode->starts > $today ) {
@@ -1664,7 +1788,7 @@ function pmpro_checkDiscountCode( $code, $level_id = null, $return_errors = fals
1664
  // have we run out of uses?
1665
  if ( ! $error ) {
1666
  if ( $dbcode->uses > 0 ) {
1667
- $used = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . $dbcode->id . "'" );
1668
  if ( $used >= $dbcode->uses ) {
1669
  $error = __( 'This discount code is no longer valid.', 'paid-memberships-pro' );
1670
  }
@@ -1682,7 +1806,7 @@ function pmpro_checkDiscountCode( $code, $level_id = null, $return_errors = fals
1682
  } else {
1683
  $level_id = intval( $level_id );
1684
  }
1685
- $code_level = $wpdb->get_row( "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id WHERE cl.code_id = '" . $dbcode->id . "' AND cl.level_id IN (" . $level_id . ") LIMIT 1" );
1686
 
1687
  if ( empty( $code_level ) ) {
1688
  $error = __( 'This discount code does not apply to this membership level.', 'paid-memberships-pro' );
@@ -1697,7 +1821,7 @@ function pmpro_checkDiscountCode( $code, $level_id = null, $return_errors = fals
1697
  *
1698
  * @param bool $okay true if code check is okay or false if there was an error
1699
  * @param object $dbcode Object containing code data from the database row
1700
- * @param int $level_id ID of the level the user is checking out for.
1701
  * @param string $code Discount code string.
1702
  *
1703
  * @return mixed $okay true if okay, false or error message string if not okay
@@ -1907,7 +2031,7 @@ function pmpro_getMembershipLevelForUser( $user_id = null, $force = false ) {
1907
  if ( empty( $all_membership_levels[ $user_id ] ) ) {
1908
  $all_membership_levels[ $user_id ] = false;
1909
  }
1910
-
1911
  // Round off prices
1912
  if ( ! empty( $all_membership_levels[$user_id] ) ) {
1913
  if ( isset( $all_membership_levels[$user_id]->initial_payment ) ) {
@@ -1960,19 +2084,19 @@ function pmpro_getMembershipLevelsForUser( $user_id = null, $include_inactive =
1960
  $user_id = intval( $user_id );
1961
 
1962
  global $wpdb;
1963
-
1964
  /**
1965
  * We are going to see if cache is set before doing the query and use that if it is.
1966
- *
1967
  * In a default environment with no external object cache, the value is cached in that request and
1968
  * reduces future MySQL requests. If there is an external object cache like Redis then it will be
1969
  * persisted until the user level changes.
1970
  **/
1971
- $cache_key = 'user_' . $user_id . '_levels';
1972
  $levels = wp_cache_get( $cache_key, 'pmpro' );
1973
-
1974
  if ( $levels === false ) {
1975
-
1976
  $levels = $wpdb->get_results(
1977
  "SELECT
1978
  l.id AS ID,
@@ -2000,7 +2124,7 @@ function pmpro_getMembershipLevelsForUser( $user_id = null, $include_inactive =
2000
  );
2001
  wp_cache_set( $cache_key, $levels, 'pmpro', 3600 );
2002
  }
2003
-
2004
  // Round off prices
2005
  if ( ! empty( $levels ) ) {
2006
  foreach( $levels as $key => $level ) {
@@ -2009,7 +2133,7 @@ function pmpro_getMembershipLevelsForUser( $user_id = null, $include_inactive =
2009
  $levels[$key]->trial_amount = pmpro_round_price( $level->trial_amount );
2010
  }
2011
  }
2012
-
2013
  /**
2014
  * pmpro_get_membership_levels_for_user filter.
2015
  *
@@ -2028,7 +2152,7 @@ function pmpro_getMembershipLevelsForUser( $user_id = null, $include_inactive =
2028
  * Get a specific membership level for a user if they have that level.
2029
  * This is better to use when MMPU is enabled on the site.
2030
  *
2031
- * If $user_id is omitted, the value will be retrieved from $current_user.
2032
  *
2033
  * Return values:
2034
  * Success returns the level object.
@@ -2037,7 +2161,7 @@ function pmpro_getMembershipLevelsForUser( $user_id = null, $include_inactive =
2037
  * @param int $user_id User ID to check for
2038
  * @param int $level_id Level ID to check for.
2039
  */
2040
- function pmpro_getSpecificMembershipLevelForUser( $user_id = null, $level_id ) {
2041
  if ( empty( $user_id ) ) {
2042
  global $current_user;
2043
  $user_id = $current_user->ID;
@@ -2061,7 +2185,7 @@ function pmpro_getSpecificMembershipLevelForUser( $user_id = null, $level_id ) {
2061
  /*
2062
  pmpro_getLevel() returns the level object for a level
2063
  *
2064
- * $level may be the level id or name
2065
  *
2066
  * Return values:
2067
  * Success returns the level object.
@@ -2081,7 +2205,7 @@ function pmpro_getLevel( $level ) {
2081
  return $pmpro_levels[ $level_id ];
2082
  } else {
2083
  global $wpdb;
2084
- $pmpro_levels[ $level_id ] = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . $level_id . "' LIMIT 1" );
2085
  }
2086
  } else {
2087
  global $wpdb;
@@ -2093,22 +2217,22 @@ function pmpro_getLevel( $level ) {
2093
  return false;
2094
  }
2095
 
2096
- $pmpro_levels[ $level_id ] = $level_obj;
2097
  }
2098
-
2099
  // Round prices
2100
  if ( ! empty( $pmpro_levels[ $level_id ] ) ) {
2101
  $pmpro_levels[ $level_id ]->initial_payment = pmpro_round_price( $pmpro_levels[ $level_id ]->initial_payment );
2102
  $pmpro_levels[ $level_id ]->billing_amount = pmpro_round_price( $pmpro_levels[ $level_id ]->billing_amount );
2103
  $pmpro_levels[ $level_id ]->trial_amount = pmpro_round_price( $pmpro_levels[ $level_id ]->trial_amount );
2104
  }
2105
-
2106
  return $pmpro_levels[ $level_id ];
2107
  }
2108
 
2109
  /*
2110
  Get all PMPro membership levels.
2111
-
2112
  @param bool $include_hidden Include levels marked as hidden/inactive.
2113
  @param bool $use_cache If false, use $pmpro_levels global. If true use other caches.
2114
  @param bool $force Resets the static var caches as well.
@@ -2155,7 +2279,7 @@ function pmpro_getAllLevels( $include_hidden = false, $use_cache = false, $force
2155
  $raw_level->trial_amount = pmpro_round_price( $raw_level->trial_amount );
2156
  $pmpro_levels[ $raw_level->id ] = $raw_level;
2157
  }
2158
-
2159
  // Store an extra cache specific to the include_hidden param.
2160
  if ( $include_hidden ) {
2161
  $pmpro_all_levels = $pmpro_levels;
@@ -2184,6 +2308,10 @@ function pmpro_are_any_visible_levels() {
2184
 
2185
  /**
2186
  * Get level at checkout and place into $pmpro_level global.
 
 
 
 
2187
  */
2188
  function pmpro_getLevelAtCheckout( $level_id = null, $discount_code = null ) {
2189
  global $pmpro_level, $wpdb, $post;
@@ -2205,11 +2333,15 @@ function pmpro_getLevelAtCheckout( $level_id = null, $discount_code = null ) {
2205
  if ( empty( $level_id ) ) {
2206
  $all_levels = pmpro_getAllLevels( false, false );
2207
 
2208
- // Get lowest level ID.
2209
- $default_level = min( array_keys( $all_levels ) );
 
 
 
 
2210
 
2211
  $level_id = apply_filters( 'pmpro_default_level', intval( $default_level ) );
2212
-
2213
  // Bail back to levels page if level ID is empty or less than 1.
2214
  if ( empty( $level_id ) || $level_id < 1 || ! is_int( $level_id ) ) {
2215
  return;
@@ -2224,17 +2356,22 @@ function pmpro_getLevelAtCheckout( $level_id = null, $discount_code = null ) {
2224
 
2225
  // what level are they purchasing? (discount code passed)
2226
  if ( ! empty( $level_id ) && ! empty( $discount_code ) ) {
2227
- $discount_code_id = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . $discount_code . "' LIMIT 1" );
2228
 
2229
  // check code
2230
- $code_check = pmpro_checkDiscountCode( $discount_code, $level_id, true );
 
 
 
 
 
2231
  if ( $code_check[0] != false ) {
2232
- $sqlQuery = "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id LEFT JOIN $wpdb->pmpro_discount_codes dc ON dc.id = cl.code_id WHERE dc.code = '" . $discount_code . "' AND cl.level_id = '" . $level_id . "' LIMIT 1";
2233
  $pmpro_level = $wpdb->get_row( $sqlQuery );
2234
 
2235
  // if the discount code doesn't adjust the level, let's just get the straight level
2236
  if ( empty( $pmpro_level ) ) {
2237
- $pmpro_level = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . $level_id . "' LIMIT 1" );
2238
  }
2239
 
2240
  // filter adjustments to the level
@@ -2257,18 +2394,52 @@ function pmpro_getLevelAtCheckout( $level_id = null, $discount_code = null ) {
2257
  return $pmpro_level;
2258
  }
2259
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2260
  function pmpro_getCheckoutButton( $level_id, $button_text = null, $classes = null ) {
2261
  if ( ! empty( $level_id ) ) {
2262
  // get level
2263
  $level = pmpro_getLevel( $level_id );
2264
 
2265
  if( ! empty( $level ) ) {
2266
-
2267
  // default button text with name field for replacement
2268
  if ( empty( $button_text ) ) {
2269
  $button_text = __( 'Sign Up for !!name!! Now', 'paid-memberships-pro' );
2270
  }
2271
-
2272
  // replace vars
2273
  $replacements = array(
2274
  '!!id!!' => $level->id,
@@ -2288,21 +2459,21 @@ function pmpro_getCheckoutButton( $level_id, $button_text = null, $classes = nul
2288
  $button_text = str_replace( array_keys( $replacements ), $replacements, $button_text );
2289
  }
2290
  }
2291
-
2292
  if ( empty( $button_text ) ) {
2293
  $button_text = __( 'Sign Up Now', 'paid-memberships-pro' );
2294
  }
2295
-
2296
  if ( empty( $classes ) ) {
2297
  $classes = 'pmpro_btn';
2298
  }
2299
-
2300
  if ( ! empty( $level_id ) ) {
2301
  $r = '<a href="' . pmpro_url( 'checkout', '?level=' . $level_id ) . '" class="' . $classes . '">' . $button_text . '</a>';
2302
  } else {
2303
  $r = '<a href="' . pmpro_url( 'checkout' ) . '" class="' . $classes . '">' . $button_text . '</a>';
2304
  }
2305
-
2306
  return $r;
2307
  }
2308
 
@@ -2361,7 +2532,7 @@ if ( ! function_exists( 'pmpro_getMemberStartdate' ) ) {
2361
  if ( ! empty( $level_id ) ) {
2362
  $sqlQuery = "SELECT UNIX_TIMESTAMP(CONVERT_TZ(startdate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND membership_id IN(" . esc_sql( $level_id ) . ") AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
2363
  } else {
2364
- $sqlQuery = "SELECT UNIX_TIMESTAMP(CONVERT_TZ(startdate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
2365
  }
2366
 
2367
  $startdate = apply_filters( 'pmpro_member_startdate', $wpdb->get_var( $sqlQuery ), $user_id, $level_id );
@@ -2588,21 +2759,22 @@ function pmpro_is_ready() {
2588
  // no paid membership level now or attached to a user. we don't need the gateway setup
2589
  $pmpro_gateway_ready = true;
2590
  } else {
2591
- $gateway = pmpro_getOption( 'gateway' );
 
2592
  if ( $gateway == 'authorizenet' ) {
2593
- if ( pmpro_getOption( 'gateway_environment' ) && pmpro_getOption( 'loginname' ) && pmpro_getOption( 'transactionkey' ) ) {
2594
  $pmpro_gateway_ready = true;
2595
  } else {
2596
  $pmpro_gateway_ready = false;
2597
  }
2598
  } elseif ( $gateway == 'paypal' || $gateway == 'paypalexpress' ) {
2599
- if ( pmpro_getOption( 'gateway_environment' ) && pmpro_getOption( 'gateway_email' ) && pmpro_getOption( 'apiusername' ) && pmpro_getOption( 'apipassword' ) && pmpro_getOption( 'apisignature' ) ) {
2600
  $pmpro_gateway_ready = true;
2601
  } else {
2602
  $pmpro_gateway_ready = false;
2603
  }
2604
  } elseif ( $gateway == 'paypalstandard' ) {
2605
- if ( pmpro_getOption( 'gateway_environment' ) && pmpro_getOption( 'gateway_email' ) ) {
2606
  $pmpro_gateway_ready = true;
2607
  } else {
2608
  $pmpro_gateway_ready = false;
@@ -2614,25 +2786,29 @@ function pmpro_is_ready() {
2614
  $pmpro_gateway_ready = false;
2615
  }
2616
  } elseif ( $gateway == 'stripe' ) {
2617
- if ( pmpro_getOption( 'gateway_environment' ) && pmpro_getOption( 'stripe_secretkey' ) && pmpro_getOption( 'stripe_publishablekey' ) ) {
 
 
 
 
2618
  $pmpro_gateway_ready = true;
2619
  } else {
2620
  $pmpro_gateway_ready = false;
2621
  }
2622
  } elseif ( $gateway == 'braintree' ) {
2623
- if ( pmpro_getOption( 'gateway_environment' ) && pmpro_getOption( 'braintree_merchantid' ) && pmpro_getOption( 'braintree_publickey' ) && pmpro_getOption( 'braintree_privatekey' ) ) {
2624
  $pmpro_gateway_ready = true;
2625
  } else {
2626
  $pmpro_gateway_ready = false;
2627
  }
2628
  } elseif ( $gateway == 'twocheckout' ) {
2629
- if ( pmpro_getOption( 'gateway_environment' ) && pmpro_getOption( 'twocheckout_apiusername' ) && pmpro_getOption( 'twocheckout_apipassword' ) ) {
2630
  $pmpro_gateway_ready = true;
2631
  } else {
2632
  $pmpro_gateway_ready = false;
2633
  }
2634
  } elseif ( $gateway == 'cybersource' ) {
2635
- if ( pmpro_getOption( 'gateway_environment' ) && pmpro_getOption( 'cybersource_merchantid' ) && pmpro_getOption( 'cybersource_securitykey' ) ) {
2636
  $pmpro_gateway_ready = true;
2637
  } else {
2638
  $pmpro_gateway_ready = false;
@@ -2680,6 +2856,97 @@ function pmpro_is_ready() {
2680
  return $r;
2681
  }
2682
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2683
  /**
2684
  * Format a price per the currency settings.
2685
  *
@@ -2724,6 +2991,35 @@ function pmpro_formatPrice( $price ) {
2724
  return apply_filters( 'pmpro_format_price', $formatted, $price, $pmpro_currency, $pmpro_currency_symbol );
2725
  }
2726
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2727
 
2728
  /**
2729
  * Function to trim trailing zeros from an amount.
@@ -2808,18 +3104,18 @@ function pmpro_round_price( $price, $currency = '' ) {
2808
  }
2809
 
2810
  if ( ! empty( $pmpro_currencies[ $currency ] )
2811
- && is_array( $pmpro_currencies[ $pmpro_currency ] )
2812
- && ! empty( $pmpro_currencies[ $currency ]['decimals'] ) ) {
2813
  $decimals = intval( $pmpro_currencies[ $currency ]['decimals'] );
2814
  }
2815
 
2816
  $rounded = round( (double) $price, $decimals );
2817
-
2818
  /**
2819
  * Filter for result of pmpro_round_price.
2820
  */
2821
  $rounded = apply_filters( 'pmpro_round_price', $rounded );
2822
-
2823
  return $rounded;
2824
  }
2825
 
@@ -2942,7 +3238,7 @@ function pmpro_isDateThisWeek( $str ) {
2942
  return false;
2943
  }
2944
  }
2945
-
2946
  /**
2947
  * Does the dave provided fall within the current year?
2948
  * Merged in from the Better Logins Report Add On.
@@ -3010,7 +3306,7 @@ function pmpro_generatePages( $pages ) {
3010
  if ( ! in_array( $name, $top_level_pages ) ) {
3011
  $insert['post_parent'] = $pmpro_pages['account'];
3012
  }
3013
-
3014
  // tweak the login slug
3015
  if ( $name == 'login' ) {
3016
  $insert['post_name'] = 'login';
@@ -3184,7 +3480,7 @@ function pmpro_is_checkout() {
3184
  function pmpro_show_discount_code() {
3185
  global $wpdb;
3186
  static $show;
3187
-
3188
  // check DB if we haven't yet
3189
  if ( !isset( $show ) ) {
3190
  if ( $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_discount_codes LIMIT 1" ) ) {
@@ -3193,9 +3489,9 @@ function pmpro_show_discount_code() {
3193
  $show = false;
3194
  }
3195
  }
3196
-
3197
  $show = apply_filters( "pmpro_show_discount_code", $show );
3198
-
3199
  return $show;
3200
  }
3201
 
@@ -3208,35 +3504,35 @@ function pmpro_show_discount_code() {
3208
  function pmpro_was_checkout_form_submitted() {
3209
  // Default to false.
3210
  $submit = false;
3211
-
3212
  // Basic check for a field called submit-checkout.
3213
  if ( isset( $_REQUEST['submit-checkout'] ) ) {
3214
  $submit = true;
3215
  }
3216
-
3217
  // _x stuff in case they clicked on the image button with their mouse
3218
  if ( empty( $submit ) && isset( $_REQUEST['submit-checkout_x'] ) ) {
3219
  $submit = true;
3220
  }
3221
-
3222
  return $submit;
3223
  }
3224
-
3225
  /**
3226
  * Build the order object used at checkout.
3227
  * @since 2.1
3228
  * @return mixed $order Order object.
3229
  */
3230
- function pmpro_build_order_for_checkout() {
3231
  global $post, $gateway, $wpdb, $besecure, $discount_code, $discount_code_id, $pmpro_level, $pmpro_levels, $pmpro_msg, $pmpro_msgt, $pmpro_review, $skip_account_fields, $pmpro_paypal_token, $pmpro_show_discount_code, $pmpro_error_fields, $pmpro_required_billing_fields, $pmpro_required_user_fields, $wp_version, $current_user, $pmpro_requirebilling, $tospage, $username, $password, $password2, $bfirstname, $blastname, $baddress1, $baddress2, $bcity, $bstate, $bzipcode, $bcountry, $bphone, $bemail, $bconfirmemail, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear, $pmpro_states, $recaptcha, $recaptcha_privatekey, $CVV;
3232
-
3233
  $morder = new MemberOrder();
3234
  $morder->membership_id = $pmpro_level->id;
3235
  $morder->membership_name = $pmpro_level->name;
3236
  $morder->discount_code = $discount_code;
3237
  $morder->InitialPayment = pmpro_round_price( $pmpro_level->initial_payment );
3238
  $morder->PaymentAmount = pmpro_round_price( $pmpro_level->billing_amount );
3239
- $morder->ProfileStartDate = date_i18n( "Y-m-d", current_time( "timestamp" ) ) . "T0:0:0";
3240
  $morder->BillingPeriod = $pmpro_level->cycle_period;
3241
  $morder->BillingFrequency = $pmpro_level->cycle_number;
3242
  if ( $pmpro_level->billing_limit ) {
@@ -3248,7 +3544,7 @@ function pmpro_show_discount_code() {
3248
  $morder->TrialBillingCycles = $pmpro_level->trial_limit;
3249
  $morder->TrialAmount = pmpro_round_price( $pmpro_level->trial_amount );
3250
  }
3251
-
3252
  // Credit card values.
3253
  $morder->cardtype = $CardType;
3254
  $morder->accountnumber = $AccountNumber;
@@ -3257,21 +3553,21 @@ function pmpro_show_discount_code() {
3257
  $morder->ExpirationDate = $ExpirationMonth . $ExpirationYear;
3258
  $morder->ExpirationDate_YdashM = $ExpirationYear . "-" . $ExpirationMonth;
3259
  $morder->CVV2 = $CVV;
3260
-
3261
  // Not saving email in order table, but the sites need it.
3262
  $morder->Email = $bemail;
3263
-
3264
  // Save the user ID if logged in.
3265
  if ( $current_user->ID ) {
3266
  $morder->user_id = $current_user->ID;
3267
  }
3268
-
3269
  // Sometimes we need these split up.
3270
  $morder->FirstName = $bfirstname;
3271
  $morder->LastName = $blastname;
3272
  $morder->Address1 = $baddress1;
3273
  $morder->Address2 = $baddress2;
3274
-
3275
  // Set other values.
3276
  $morder->billing = new stdClass();
3277
  $morder->billing->name = $bfirstname . " " . $blastname;
@@ -3280,24 +3576,24 @@ function pmpro_show_discount_code() {
3280
  $morder->billing->state = $bstate;
3281
  $morder->billing->country = $bcountry;
3282
  $morder->billing->zip = $bzipcode;
3283
- $morder->billing->phone = $bphone;
3284
  $morder->gateway = $gateway;
3285
  $morder->setGateway();
3286
-
3287
  // Set up level var.
3288
  $morder->getMembershipLevelAtCheckout();
3289
-
3290
  // Set tax.
3291
  $initial_tax = $morder->getTaxForPrice( $morder->InitialPayment );
3292
  $recurring_tax = $morder->getTaxForPrice( $morder->PaymentAmount );
3293
-
3294
  // Set amounts.
3295
  $morder->initial_amount = pmpro_round_price((float)$morder->InitialPayment + (float)$initial_tax);
3296
  $morder->subscription_amount = pmpro_round_price((float)$morder->PaymentAmount + (float)$recurring_tax);
3297
-
3298
  // Filter for order, since v1.8
3299
  $morder = apply_filters( 'pmpro_checkout_order', $morder );
3300
-
3301
  return $morder;
3302
  }
3303
 
@@ -3319,7 +3615,7 @@ function pmpro_check_plugin_version( $plugin_file, $comparison, $version ) {
3319
  $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file, false, true );
3320
 
3321
  // Return false if there is no plugin data.
3322
- if ( empty( $plugin_data ) ) {
3323
  return false;
3324
  }
3325
 
@@ -3340,7 +3636,7 @@ function pmpro_check_plugin_version( $plugin_file, $comparison, $version ) {
3340
  * @param string $operator Operator to use, e.g. >, <, >=, <=, =.
3341
  * @return bool true or false based on the operator passed in. Returns null for invalid operators.
3342
  */
3343
- function pmpro_int_compare( $a, $b, $operator ) {
3344
  switch ( $operator ) {
3345
  case '>':
3346
  $r = (int)$a > (int)$b;
@@ -3355,13 +3651,13 @@ function pmpro_int_compare( $a, $b, $operator ) {
3355
  $r = (int)$a <= (int)$b;
3356
  break;
3357
  case '=':
3358
- case '==':
3359
  $r = (int)$a == (int)$b;
3360
  break;
3361
  default:
3362
  $r = null;
3363
  }
3364
-
3365
  return $r;
3366
  }
3367
 
@@ -3375,7 +3671,7 @@ function pmpro_int_compare( $a, $b, $operator ) {
3375
  */
3376
  function pmpro_insert_or_replace( $table, $data, $format, $primary_key = 'id' ) {
3377
  global $wpdb;
3378
-
3379
  if ( empty( $data[$primary_key] ) ) {
3380
  // Insert. Remove keys first.
3381
  $index = array_search( $primary_key, array_keys( $data ) );
@@ -3386,6 +3682,285 @@ function pmpro_insert_or_replace( $table, $data, $format, $primary_key = 'id' )
3386
  return $wpdb->insert( $table, $data, $format );
3387
  } else {
3388
  // Replace.
3389
- return $wpdb->replace( $table, $data, $format );
3390
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3391
  }
39
  $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels';
40
  $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses';
41
  $wpdb->pmpro_membership_levelmeta = $wpdb->prefix . 'pmpro_membership_levelmeta';
42
+ $wpdb->pmpro_membership_ordermeta = $wpdb->prefix . 'pmpro_membership_ordermeta';
43
  }
44
  pmpro_setDBTables();
45
 
84
  }
85
  }
86
 
87
+ function pmpro_setOption( $s, $v = null, $sanitize_function = 'sanitize_text_field', $autoload = false ) {
88
  // no value is given, set v to the p var
89
  if ( $v === null && isset( $_POST[ $s ] ) ) {
90
  if ( is_array( $_POST[ $s ] ) ) {
100
  $v = trim( $v );
101
  }
102
 
103
+ return update_option( 'pmpro_' . $s, $v, $autoload );
104
  }
105
 
106
  function pmpro_get_slug( $post_id ) {
132
 
133
  global $pmpro_pages;
134
 
135
+ if ( ! empty( $pmpro_pages[ $page ] ) ) {
136
+ // start with the permalink
137
+ $url = get_permalink( $pmpro_pages[ $page ] );
138
 
139
+ // WPML/etc support
140
+ if ( function_exists( 'icl_object_id' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
141
+ $trans_id = icl_object_id( $pmpro_pages[ $page ], 'page', false, ICL_LANGUAGE_CODE );
142
+ if ( ! empty( $trans_id ) ) {
143
+ $url = get_permalink( $trans_id );
144
+ }
145
  }
146
+ } else {
147
+ $url = '';
148
  }
149
 
150
  // figure out querystring
160
  $url = str_replace( 'http:', 'https:', $url );
161
  }
162
  }
163
+
164
  /**
165
  * Filter the URL before returning.
166
  */
175
  } else {
176
  $r = false;
177
  }
178
+
179
  $r = apply_filters( 'pmpro_is_level_free', $r, $level );
180
  return $r;
181
  }
201
  function pmpro_onlyFreeLevels() {
202
  // Get levels that are available for checkout only.
203
  $levels = pmpro_getAllLevels( false, true );
204
+
205
  return apply_filters( 'pmpro_only_free_levels', pmpro_areLevelsFree( $levels ) );
206
  }
207
 
211
  } else {
212
  $r = false;
213
  }
214
+
215
  $r = apply_filters( 'pmpro_is_level_recurring', $r, $level );
216
  return $r;
217
  }
224
  */
225
  function pmpro_has_recurring_level( $user_id = null ) {
226
  global $current_user;
227
+
228
  if ( empty( $user_id ) ) {
229
  $user_id = $current_user->ID;
230
  }
231
+
232
  if ( empty( $user_id ) ) {
233
  return false;
234
  }
235
 
236
+ $levels = pmpro_getMembershipLevelsForUser( $user_id );
237
+
238
  if ( empty( $levels ) ) {
239
  return false;
240
  }
241
+
242
+ foreach( $levels as $level ) {
243
  if ( pmpro_isLevelRecurring( $level ) ) {
244
  return true;
245
  }
246
+ }
247
 
248
  return false;
249
  }
254
  } else {
255
  $r = false;
256
  }
257
+
258
  $r = apply_filters( 'pmpro_is_level_trial', $r, $level );
259
  return $r;
260
  }
261
 
262
  function pmpro_isLevelExpiring( &$level ) {
263
  if ( ! empty( $level ) && ( ! empty( $level->expiration_number ) && $level->expiration_number > 0 ) || ! empty( $level->enddate ) ) {
264
+
265
  $r = true;
266
  } else {
267
  $r = false;
268
  }
269
+
270
  $r = apply_filters( 'pmpro_is_level_expiring', $r, $level );
271
  return $r;
272
  }
286
  $standard = pmpro_getLevel( $level->id );
287
 
288
  if ( ! empty( $standard->expiration_number ) ) {
289
+ if ( $standard->expiration_period == 'Hour' ) {
290
+ $days = $level->expiration_number;
291
+ } else if ( $standard->expiration_period == 'Day' ) {
292
  $days = $level->expiration_number;
293
  } elseif ( $standard->expiration_period == 'Week' ) {
294
  $days = $level->expiration_number * 7;
304
  // are we within the days til expiration?
305
  $now = current_time( 'timestamp' );
306
 
307
+ if( $standard->expiration_period == 'Hour' ){
308
+ if( $now + ( $days * 60 ) >= $level->enddate ){
309
+ $r = true;
310
+ } else {
311
+ $r = false;
312
+ }
313
+ } else if ( $now + ( $days * 3600 * 24 ) >= $level->enddate ) {
314
  $r = true;
315
  } else {
316
  $r = false;
392
  if ( ! $short ) {
393
  $r = sprintf( __( 'The price for membership is <strong>%s</strong> now', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ) );
394
  } else {
395
+ if ( pmpro_isLevelFree( $level ) ) {
396
+ $r = '<strong>' . __('Free', 'paid-memberships-pro' ) . '</strong>';
397
+ } else {
398
+ $r = sprintf( __( '<strong>%s</strong> now', 'paid-memberships-pro' ), pmpro_formatPrice( $level->initial_payment ) );
399
+ }
400
  }
401
 
402
  // recurring part
403
+ if ( (float)$level->billing_amount > 0 ) {
404
  if ( $level->billing_limit > 1 ) {
405
  if ( $level->cycle_number == '1' ) {
406
  $r .= sprintf( __( ' and then <strong>%1$s per %2$s for %3$d more %4$s</strong>.', 'paid-memberships-pro' ), pmpro_formatPrice( $level->billing_amount ), pmpro_translate_billing_period( $level->cycle_period ), $level->billing_limit, pmpro_translate_billing_period( $level->cycle_period, $level->billing_limit ) );
441
 
442
  // trial part
443
  if ( $level->trial_limit ) {
444
+ if ( (float)$level->trial_amount > 0 ) {
445
  if ( $level->trial_limit == '1' ) {
446
  $r .= ' ' . __( 'After your initial payment, your first payment is Free.', 'paid-memberships-pro' );
447
  } else {
481
  $trialperiods = 0;
482
  foreach ( $levels as $curlevel ) {
483
  $initpmt += $curlevel->initial_payment;
484
+ if ( (float)$curlevel->billing_amount > 0 ) {
485
  if ( array_key_exists( $curlevel->cycle_period, $recurpmts ) ) {
486
  if ( array_key_exists( $curlevel->cycle_number, $recurpmts[ $curlevel->cycle_period ] ) ) {
487
  if ( array_key_exists( $curlevel->billing_limit, $recurpmts[ $curlevel->cycle_period ][ $curlevel->cycle_number ] ) ) {
577
  }
578
 
579
  function pmpro_getLevelExpiration( &$level ) {
580
+
581
  if ( $level->expiration_number ) {
582
  $expiration_text = sprintf( __( 'Membership expires after %1$d %2$s.', 'paid-memberships-pro' ), $level->expiration_number, pmpro_translate_billing_period( $level->expiration_period, $level->expiration_number ) );
583
  } else {
626
  * Filter the levels expiration text. Note the s in levels. Similar to pmpro_levels_expiration_text
627
  */
628
  $expiration_text = apply_filters( 'pmpro_levels_expiration_text', $expiration_text, $levels );
629
+
630
  // Backwards compatible
631
  if ( ! empty( $levels ) ) {
632
  $first_level = reset($levels);
634
  $first_level = false;
635
  }
636
  $expiration_text = apply_filters( 'pmpro_level_expiration_text', $expiration_text, $first_level );
637
+
638
  return $expiration_text;
639
  }
640
 
647
  return add_metadata( 'pmpro_membership_level', $level_id, $meta_key, $meta_value, $unique );
648
  }
649
 
650
+ function get_pmpro_membership_level_meta( $level_id, $key = '', $single = false ) {
651
  return get_metadata( 'pmpro_membership_level', $level_id, $key, $single );
652
  }
653
 
659
  return delete_metadata( 'pmpro_membership_level', $level_id, $meta_key, $meta_value );
660
  }
661
 
662
+ /**
663
+ * pmpro_membership_order Meta Functions
664
+ */
665
+ function add_pmpro_membership_order_meta( $order_id, $meta_key, $meta_value, $unique = false ) {
666
+ return add_metadata( 'pmpro_membership_order', $order_id, $meta_key, $meta_value, $unique );
667
+ }
668
+
669
+ function get_pmpro_membership_order_meta( $order_id, $key = '', $single = false ) {
670
+ return get_metadata( 'pmpro_membership_order', $order_id, $key, $single );
671
+ }
672
+
673
+ function update_pmpro_membership_order_meta( $order_id, $meta_key, $meta_value, $prev_value = '' ) {
674
+ return update_metadata( 'pmpro_membership_order', $order_id, $meta_key, $meta_value, $prev_value );
675
+ }
676
+
677
+ function delete_pmpro_membership_order_meta( $order_id, $meta_key, $meta_value = '' ) {
678
+ return delete_metadata( 'pmpro_membership_order', $order_id, $meta_key, $meta_value );
679
+ }
680
+
681
  function pmpro_hideAds() {
682
  global $pmpro_display_ads;
683
  return ! $pmpro_display_ads;
960
  * @param int $level ID of level to set as new level, use 0 to cancel membership
961
  * @param int $user_id ID of the user to change levels for
962
  * @param string $old_level_status The status to set for the row in the memberships users table. (e.g. inactive, cancelled, admin_cancelled, expired) Defaults to 'inactive'.
963
+ * @param int $cancel_level If set cancel just this one level instead of all active levels (to support Multiple Memberships per User)
964
  *
965
  * Return values:
966
  * Success returns boolean true.
967
  * Failure returns boolean false.
968
+ * No change returns null.
969
  */
970
  function pmpro_changeMembershipLevel( $level, $user_id = null, $old_level_status = 'inactive', $cancel_level = null ) {
971
  global $wpdb;
983
  // make sure user id is int for security
984
  $user_id = intval( $user_id );
985
 
986
+ /**
987
+ * Filter the level passed in.
988
+ * @since 2.5.8
989
+ */
990
+ $level = apply_filters( 'pmpro_change_level', $level, $user_id, $old_level_status, $cancel_level );
991
+
992
  if ( empty( $level ) ) {
993
  $level = 0;
994
  } else if ( is_array( $level ) ) {
1019
  if ( ! is_array( $level ) ) {
1020
  // are they even changing?
1021
  if ( pmpro_hasMembershipLevel( $level, $user_id ) ) {
1022
+ return;
 
1023
  }
1024
  }
1025
 
1026
  // get all active membershipships for this user
1027
  $old_levels = pmpro_getMembershipLevelsForUser( $user_id );
1028
 
1029
+ global $pmpro_old_user_levels;
1030
+ if ( empty( $pmpro_old_user_levels ) ) {
1031
+ $pmpro_old_user_levels = array();
1032
+ }
1033
+ if ( ! array_key_exists( $user_id, $pmpro_old_user_levels ) ) {
1034
+ $pmpro_old_user_levels[$user_id] = empty( $old_levels ) ? array() : $old_levels;
1035
+ }
1036
+
1037
  // get level id
1038
  if ( is_array( $level ) ) {
1039
  $level_id = $level['membership_id']; // custom level
1047
  * @param int $level_id ID of the level changed to.
1048
  * @param int $user_id ID of the user changed.
1049
  * @param array $old_levels array of prior levels the user belonged to.
1050
+ * @param int $cancel_level ID of the level being cancelled if specified
1051
  */
1052
  do_action( 'pmpro_before_change_membership_level', $level_id, $user_id, $old_levels, $cancel_level );
1053
 
1126
 
1127
  if ( ! empty( $c_order->error ) ) {
1128
  $pmpro_error = $c_order->error;
1129
+ }
1130
+
1131
+ if( $old_level_status == 'error' ) {
1132
+ $c_order->updateStatus("error");
1133
  }
1134
  }
1135
  }
1191
  $pmpro_error = sprintf( __( 'Error interacting with database: %s', 'paid-memberships-pro' ), ( ! empty( $wpdb->last_error ) ? $wpdb->last_error : 'unavailable' ) );
1192
  return false;
1193
  }
1194
+
1195
+ /**
1196
+ * Allow filtering whether to remove duplicate "active" memberships by setting them to "changed".
1197
+ *
1198
+ * @since 2.6.6
1199
+ *
1200
+ * @param bool $remove_duplicate_memberships Whether to remove duplicate "active" memberships by setting them to "changed".
1201
+ */
1202
+ $remove_duplicate_memberships = apply_filters( 'pmpro_remove_duplicate_membership_entries', true );
1203
+
1204
+ if ( $remove_duplicate_memberships ) {
1205
+ $wpdb->query(
1206
+ $wpdb->prepare(
1207
+ "
1208
+ UPDATE {$wpdb->pmpro_memberships_users}
1209
+ SET status = %s,
1210
+ enddate = %s
1211
+ WHERE user_id = %d
1212
+ AND membership_id = %d
1213
+ AND status = %s
1214
+ AND id != %d
1215
+ ",
1216
+ 'changed',
1217
+ current_time( 'mysql' ),
1218
+ $user_id,
1219
+ $level_id,
1220
+ 'active',
1221
+ $wpdb->insert_id // Ignore the membership that we just added.
1222
+ )
1223
+ );
1224
+ }
1225
  }
1226
 
1227
  // remove cached level
1228
  global $all_membership_levels;
1229
  unset( $all_membership_levels[ $user_id ] );
1230
+
1231
  // remove levels cache for user
1232
  $cache_key = 'user_' . $user_id . '_levels';
1233
  wp_cache_delete( $cache_key, 'pmpro' );
1234
+ wp_cache_delete( $cache_key . '_all', 'pmpro' );
1235
+ wp_cache_delete( $cache_key . '_active', 'pmpro' );
1236
 
1237
  // update user data and call action
1238
  pmpro_set_current_user();
1242
  *
1243
  * @param int $level_id ID of the level changed to.
1244
  * @param int $user_id ID of the user changed.
1245
+ * @param int $cancel_level ID of the level being cancelled if specified.
1246
  */
1247
  do_action( 'pmpro_after_change_membership_level', $level_id, $user_id, $cancel_level );
1248
  return true;
1249
  }
1250
 
1251
+ /**
1252
+ * Runs after all membership level changes have been performed.
1253
+ *
1254
+ * @param mixed $filter_contents to not break the wp_redirect filter.
1255
+ */
1256
+ function pmpro_do_action_after_all_membership_level_changes( $filter_contents = null ) {
1257
+ global $pmpro_old_user_levels;
1258
+ if ( empty( $pmpro_old_user_levels ) ) {
1259
+ // No level changes occured, return.
1260
+ return $filter_contents;
1261
+ }
1262
+
1263
+ // Clear global so that we don't run twice for same level changes
1264
+ $pmpro_old_user_levels_copy = $pmpro_old_user_levels;
1265
+ $pmpro_old_user_levels = null;
1266
+
1267
+ /**
1268
+ * Run code after all membership level changes have occured. Users who have had changes
1269
+ * will be stored in the global $pmpro_old_user_levels array.
1270
+ *
1271
+ * @since 2.6
1272
+ * @param array $pmpro_old_user_levels_copy array of user_id => array( old_level_objs )
1273
+ */
1274
+ do_action( 'pmpro_after_all_membership_level_changes', $pmpro_old_user_levels_copy );
1275
+
1276
+ return $filter_contents;
1277
+ }
1278
+ add_action( 'template_redirect', 'pmpro_do_action_after_all_membership_level_changes', 2 );
1279
+ add_filter( 'wp_redirect', 'pmpro_do_action_after_all_membership_level_changes', 100 );
1280
+ add_action( 'pmpro_membership_post_membership_expiry', 'pmpro_do_action_after_all_membership_level_changes' );
1281
+ add_action( 'shutdown', 'pmpro_do_action_after_all_membership_level_changes' );
1282
+
1283
  /**
1284
  * Function to list WordPress categories in hierarchical format.
1285
  *
1317
  /*
1318
  pmpro_toggleMembershipCategory() creates or deletes a linking entry between the membership level and post category tables.
1319
  *
1320
+ * @param $level may either be the ID or name of the desired membership_level.
1321
+ * @param $category must be a valid post category ID.
1322
+ * @param $value
1323
  *
1324
  * Return values:
1325
  * Success returns boolean true.
1357
  pmpro_updateMembershipCategories() ensures that all those and only those categories given
1358
  * are associated with the given membership level.
1359
  *
1360
+ * @param $level is a valid membership level ID or name
1361
+ * @param $categories is an array of post category IDs
1362
  *
1363
  * Return values:
1364
  * Success returns boolean true.
1396
  /*
1397
  pmpro_getMembershipCategories() returns the categories for a given level
1398
  *
1399
+ * @param $level_id is a valid membership level ID
1400
  *
1401
  * Return values:
1402
  * Success returns boolean true.
1409
  $categories = $wpdb->get_col(
1410
  "SELECT c.category_id
1411
  FROM {$wpdb->pmpro_memberships_categories} AS c
1412
+ WHERE c.membership_id = '" . esc_sql( $level_id ) . "'"
1413
  );
1414
 
1415
  return $categories;
1417
 
1418
 
1419
  function pmpro_isAdmin( $user_id = null ) {
1420
+ global $current_user;
1421
  if ( ! $user_id ) {
1422
  $user_id = $current_user->ID;
1423
  }
1473
  }
1474
 
1475
  // function to return the pagination string
1476
+ function pmpro_getPaginationString( $page = 1, $totalitems = 0, $limit = 15, $adjacents = 1, $targetpage = '/', $pagestring = '&pn=' ) {
1477
  // defaults
1478
  if ( ! $adjacents ) {
1479
  $adjacents = 1;
1633
  UNION
1634
  SELECT SUM((365/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Day' AND cycle_number <> 365 $user_ids_query
1635
  UNION
1636
+ SELECT SUM((24/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Hour' AND cycle_number <> 24 $user_ids_query
1637
+ UNION
1638
  SELECT SUM((52/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Week' AND cycle_number <> 52 $user_ids_query
1639
  UNION
1640
  SELECT SUM(billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Year' $user_ids_query
1680
  $username = $lastname;
1681
  }
1682
 
1683
+ // If no username yet or one based on name exists,
1684
  // try to create username using email address.
1685
  if ( ( empty( $username ) || username_exists( $username ) )
1686
  && ! empty( $email ) && is_email( $email ) ) {
1725
  function pmpro_getDiscountCode( $seed = null ) {
1726
  global $wpdb;
1727
 
1728
+ // We mix this with the seed to make sure we get unique codes.
1729
+ static $count = 0;
1730
+ $count++;
1731
+
1732
  while ( empty( $code ) ) {
1733
+ $scramble = md5( AUTH_KEY . microtime() . $seed . SECURE_AUTH_KEY . $count );
1734
  $code = substr( $scramble, 0, 10 );
1735
  $check = $wpdb->get_var( "SELECT code FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql( $code ) . "' LIMIT 1" );
1736
  if ( $check || is_numeric( $code ) ) {
1770
  $dbcode->expires = strtotime( date_i18n( 'm/d/Y', $dbcode->expires ) );
1771
 
1772
  // today
1773
+ $today = strtotime( date_i18n( 'm/d/Y H:i:00', current_time( 'timestamp' ) ) );
1774
 
1775
  // has this code started yet?
1776
  if ( ! empty( $dbcode->starts ) && $dbcode->starts > $today ) {
1788
  // have we run out of uses?
1789
  if ( ! $error ) {
1790
  if ( $dbcode->uses > 0 ) {
1791
+ $used = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . esc_sql( $dbcode->id ) . "'" );
1792
  if ( $used >= $dbcode->uses ) {
1793
  $error = __( 'This discount code is no longer valid.', 'paid-memberships-pro' );
1794
  }
1806
  } else {
1807
  $level_id = intval( $level_id );
1808
  }
1809
+ $code_level = $wpdb->get_row( "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id WHERE cl.code_id = '" . esc_sql( $dbcode->id ) . "' AND cl.level_id IN (" . esc_sql( $level_id ) . ") LIMIT 1" );
1810
 
1811
  if ( empty( $code_level ) ) {
1812
  $error = __( 'This discount code does not apply to this membership level.', 'paid-memberships-pro' );
1821
  *
1822
  * @param bool $okay true if code check is okay or false if there was an error
1823
  * @param object $dbcode Object containing code data from the database row
1824
+ * @param int|array $level_id ID of the level the user is checking out for.
1825
  * @param string $code Discount code string.
1826
  *
1827
  * @return mixed $okay true if okay, false or error message string if not okay
2031
  if ( empty( $all_membership_levels[ $user_id ] ) ) {
2032
  $all_membership_levels[ $user_id ] = false;
2033
  }
2034
+
2035
  // Round off prices
2036
  if ( ! empty( $all_membership_levels[$user_id] ) ) {
2037
  if ( isset( $all_membership_levels[$user_id]->initial_payment ) ) {
2084
  $user_id = intval( $user_id );
2085
 
2086
  global $wpdb;
2087
+
2088
  /**
2089
  * We are going to see if cache is set before doing the query and use that if it is.
2090
+ *
2091
  * In a default environment with no external object cache, the value is cached in that request and
2092
  * reduces future MySQL requests. If there is an external object cache like Redis then it will be
2093
  * persisted until the user level changes.
2094
  **/
2095
+ $cache_key = 'user_' . $user_id . '_levels' . ( $include_inactive ? '_all' : '_active' );
2096
  $levels = wp_cache_get( $cache_key, 'pmpro' );
2097
+
2098
  if ( $levels === false ) {
2099
+
2100
  $levels = $wpdb->get_results(
2101
  "SELECT
2102
  l.id AS ID,
2124
  );
2125
  wp_cache_set( $cache_key, $levels, 'pmpro', 3600 );
2126
  }
2127
+
2128
  // Round off prices
2129
  if ( ! empty( $levels ) ) {
2130
  foreach( $levels as $key => $level ) {
2133
  $levels[$key]->trial_amount = pmpro_round_price( $level->trial_amount );
2134
  }
2135
  }
2136
+
2137
  /**
2138
  * pmpro_get_membership_levels_for_user filter.
2139
  *
2152
  * Get a specific membership level for a user if they have that level.
2153
  * This is better to use when MMPU is enabled on the site.
2154
  *
2155
+ * If $user_id is null, the value will be retrieved from $current_user.
2156
  *
2157
  * Return values:
2158
  * Success returns the level object.
2161
  * @param int $user_id User ID to check for
2162
  * @param int $level_id Level ID to check for.
2163
  */
2164
+ function pmpro_getSpecificMembershipLevelForUser( $user_id, $level_id ) {
2165
  if ( empty( $user_id ) ) {
2166
  global $current_user;
2167
  $user_id = $current_user->ID;
2185
  /*
2186
  pmpro_getLevel() returns the level object for a level
2187
  *
2188
+ * @param $level may be the level id or name
2189
  *
2190
  * Return values:
2191
  * Success returns the level object.
2205
  return $pmpro_levels[ $level_id ];
2206
  } else {
2207
  global $wpdb;
2208
+ $pmpro_levels[ $level_id ] = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql( $level_id ) . "' LIMIT 1" );
2209
  }
2210
  } else {
2211
  global $wpdb;
2217
  return false;
2218
  }
2219
 
2220
+ $pmpro_levels[ $level_id ] = $level_obj;
2221
  }
2222
+
2223
  // Round prices
2224
  if ( ! empty( $pmpro_levels[ $level_id ] ) ) {
2225
  $pmpro_levels[ $level_id ]->initial_payment = pmpro_round_price( $pmpro_levels[ $level_id ]->initial_payment );
2226
  $pmpro_levels[ $level_id ]->billing_amount = pmpro_round_price( $pmpro_levels[ $level_id ]->billing_amount );
2227
  $pmpro_levels[ $level_id ]->trial_amount = pmpro_round_price( $pmpro_levels[ $level_id ]->trial_amount );
2228
  }
2229
+
2230
  return $pmpro_levels[ $level_id ];
2231
  }
2232
 
2233
  /*
2234
  Get all PMPro membership levels.
2235
+
2236
  @param bool $include_hidden Include levels marked as hidden/inactive.
2237
  @param bool $use_cache If false, use $pmpro_levels global. If true use other caches.
2238
  @param bool $force Resets the static var caches as well.
2279
  $raw_level->trial_amount = pmpro_round_price( $raw_level->trial_amount );
2280
  $pmpro_levels[ $raw_level->id ] = $raw_level;
2281
  }
2282
+
2283
  // Store an extra cache specific to the include_hidden param.
2284
  if ( $include_hidden ) {
2285
  $pmpro_all_levels = $pmpro_levels;
2308
 
2309
  /**
2310
  * Get level at checkout and place into $pmpro_level global.
2311
+ * If no level is passed or found in the URL parameters, global vars,
2312
+ * or in the post options, then this will return the first level found.
2313
+ * @param int $level_id (optional) Pass a level ID to force that level.
2314
+ * @param string $discount_code (optional) Pass a discount code to force that code.
2315
  */
2316
  function pmpro_getLevelAtCheckout( $level_id = null, $discount_code = null ) {
2317
  global $pmpro_level, $wpdb, $post;
2333
  if ( empty( $level_id ) ) {
2334
  $all_levels = pmpro_getAllLevels( false, false );
2335
 
2336
+ if ( ! empty( $all_levels ) ) {
2337
+ // Get lowest level ID.
2338
+ $default_level = min( array_keys( $all_levels ) );
2339
+ } else {
2340
+ $default_level = null;
2341
+ }
2342
 
2343
  $level_id = apply_filters( 'pmpro_default_level', intval( $default_level ) );
2344
+
2345
  // Bail back to levels page if level ID is empty or less than 1.
2346
  if ( empty( $level_id ) || $level_id < 1 || ! is_int( $level_id ) ) {
2347
  return;
2356
 
2357
  // what level are they purchasing? (discount code passed)
2358
  if ( ! empty( $level_id ) && ! empty( $discount_code ) ) {
2359
+ $discount_code_id = $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_discount_codes WHERE code = '" . esc_sql( $discount_code ) . "' LIMIT 1" );
2360
 
2361
  // check code
2362
+ global $pmpro_checkout_level_ids; // Set by MMPU.
2363
+ if ( isset( $pmpro_checkout_level_ids ) ) {
2364
+ $code_check = pmpro_checkDiscountCode( $discount_code, $pmpro_checkout_level_ids, true );
2365
+ } else {
2366
+ $code_check = pmpro_checkDiscountCode( $discount_code, $level_id, true );
2367
+ }
2368
  if ( $code_check[0] != false ) {
2369
+ $sqlQuery = "SELECT l.id, cl.*, l.name, l.description, l.allow_signups, l.confirmation FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id LEFT JOIN $wpdb->pmpro_discount_codes dc ON dc.id = cl.code_id WHERE dc.code = '" . esc_sql( $discount_code ) . "' AND cl.level_id = '" . esc_sql( $level_id ) . "' LIMIT 1";
2370
  $pmpro_level = $wpdb->get_row( $sqlQuery );
2371
 
2372
  // if the discount code doesn't adjust the level, let's just get the straight level
2373
  if ( empty( $pmpro_level ) ) {
2374
+ $pmpro_level = $wpdb->get_row( "SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql( $level_id ) . "' LIMIT 1" );
2375
  }
2376
 
2377
  // filter adjustments to the level
2394
  return $pmpro_level;
2395
  }
2396
 
2397
+ /**
2398
+ * Get an ordered list of level objects or level IDs.
2399
+ *
2400
+ * @param array $pmpro_levels An array of level objects or level IDs to be reordered.
2401
+ * @return array $pmpro_levels An ordered array of level objects or level IDs.
2402
+ *
2403
+ */
2404
+ function pmpro_sort_levels_by_order( $pmpro_levels ) {
2405
+ $pmpro_level_order = pmpro_getOption( 'level_order' );
2406
+
2407
+ // No custom sort order, just return.
2408
+ if ( empty( $pmpro_level_order ) ) {
2409
+ return $pmpro_levels;
2410
+ }
2411
+
2412
+ // Convert the level order option to an array.
2413
+ $sort_order = explode( ',',$pmpro_level_order );
2414
+
2415
+ // Reorder the array.
2416
+ $reordered_levels = array();
2417
+ foreach ( $sort_order as $level_id ) {
2418
+ foreach ( $pmpro_levels as $key => $level ) {
2419
+ if ( ! empty ( $level->id ) && $level_id == $level->id ) {
2420
+ $reordered_levels[$level_id] = $pmpro_levels[$key];
2421
+ } elseif ( ! empty( $level ) && is_string( $level ) && $level_id == $level ) {
2422
+ $reordered_levels[$level_id] = $pmpro_levels[$key];
2423
+ }
2424
+ }
2425
+ }
2426
+ $pmpro_levels = $reordered_levels;
2427
+
2428
+ return $pmpro_levels;
2429
+ }
2430
+
2431
  function pmpro_getCheckoutButton( $level_id, $button_text = null, $classes = null ) {
2432
  if ( ! empty( $level_id ) ) {
2433
  // get level
2434
  $level = pmpro_getLevel( $level_id );
2435
 
2436
  if( ! empty( $level ) ) {
2437
+
2438
  // default button text with name field for replacement
2439
  if ( empty( $button_text ) ) {
2440
  $button_text = __( 'Sign Up for !!name!! Now', 'paid-memberships-pro' );
2441
  }
2442
+
2443
  // replace vars
2444
  $replacements = array(
2445
  '!!id!!' => $level->id,
2459
  $button_text = str_replace( array_keys( $replacements ), $replacements, $button_text );
2460
  }
2461
  }
2462
+
2463
  if ( empty( $button_text ) ) {
2464
  $button_text = __( 'Sign Up Now', 'paid-memberships-pro' );
2465
  }
2466
+
2467
  if ( empty( $classes ) ) {
2468
  $classes = 'pmpro_btn';
2469
  }
2470
+
2471
  if ( ! empty( $level_id ) ) {
2472
  $r = '<a href="' . pmpro_url( 'checkout', '?level=' . $level_id ) . '" class="' . $classes . '">' . $button_text . '</a>';
2473
  } else {
2474
  $r = '<a href="' . pmpro_url( 'checkout' ) . '" class="' . $classes . '">' . $button_text . '</a>';
2475
  }
2476
+
2477
  return $r;
2478
  }
2479
 
2532
  if ( ! empty( $level_id ) ) {
2533
  $sqlQuery = "SELECT UNIX_TIMESTAMP(CONVERT_TZ(startdate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND membership_id IN(" . esc_sql( $level_id ) . ") AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
2534
  } else {
2535
+ $sqlQuery = "SELECT UNIX_TIMESTAMP(CONVERT_TZ(startdate, '+00:00', @@global.time_zone)) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND user_id = '" . esc_sql( $user_id ) . "' ORDER BY id LIMIT 1";
2536
  }
2537
 
2538
  $startdate = apply_filters( 'pmpro_member_startdate', $wpdb->get_var( $sqlQuery ), $user_id, $level_id );
2759
  // no paid membership level now or attached to a user. we don't need the gateway setup
2760
  $pmpro_gateway_ready = true;
2761
  } else {
2762
+ $gateway = pmpro_getOption( 'gateway' );
2763
+ $gateway_environment = pmpro_getOption( 'gateway_environment' );
2764
  if ( $gateway == 'authorizenet' ) {
2765
+ if ( $gateway_environment && pmpro_getOption( 'loginname' ) && pmpro_getOption( 'transactionkey' ) ) {
2766
  $pmpro_gateway_ready = true;
2767
  } else {
2768
  $pmpro_gateway_ready = false;
2769
  }
2770
  } elseif ( $gateway == 'paypal' || $gateway == 'paypalexpress' ) {
2771
+ if ( $gateway_environment && pmpro_getOption( 'gateway_email' ) && pmpro_getOption( 'apiusername' ) && pmpro_getOption( 'apipassword' ) && pmpro_getOption( 'apisignature' ) ) {
2772
  $pmpro_gateway_ready = true;
2773
  } else {
2774
  $pmpro_gateway_ready = false;
2775
  }
2776
  } elseif ( $gateway == 'paypalstandard' ) {
2777
+ if ( $gateway_environment && pmpro_getOption( 'gateway_email' ) ) {
2778
  $pmpro_gateway_ready = true;
2779
  } else {
2780
  $pmpro_gateway_ready = false;
2786
  $pmpro_gateway_ready = false;
2787
  }
2788
  } elseif ( $gateway == 'stripe' ) {
2789
+ if ( $gateway_environment && pmpro_getOption( 'stripe_secretkey' ) && pmpro_getOption( 'stripe_publishablekey' ) ) {
2790
+ // Using legacy keys.
2791
+ $pmpro_gateway_ready = true;
2792
+ } elseif ( $gateway_environment && pmpro_getOption( $gateway_environment . '_stripe_connect_secretkey' ) && pmpro_getOption( $gateway_environment . '_stripe_connect_publishablekey' ) ) {
2793
+ // Using connect.
2794
  $pmpro_gateway_ready = true;
2795
  } else {
2796
  $pmpro_gateway_ready = false;
2797
  }
2798
  } elseif ( $gateway == 'braintree' ) {
2799
+ if ( $gateway_environment && pmpro_getOption( 'braintree_merchantid' ) && pmpro_getOption( 'braintree_publickey' ) && pmpro_getOption( 'braintree_privatekey' ) ) {
2800
  $pmpro_gateway_ready = true;
2801
  } else {
2802
  $pmpro_gateway_ready = false;
2803
  }
2804
  } elseif ( $gateway == 'twocheckout' ) {
2805
+ if ( $gateway_environment && pmpro_getOption( 'twocheckout_apiusername' ) && pmpro_getOption( 'twocheckout_apipassword' ) ) {
2806
  $pmpro_gateway_ready = true;
2807
  } else {
2808
  $pmpro_gateway_ready = false;
2809
  }
2810
  } elseif ( $gateway == 'cybersource' ) {
2811
+ if ( $gateway_environment && pmpro_getOption( 'cybersource_merchantid' ) && pmpro_getOption( 'cybersource_securitykey' ) ) {
2812
  $pmpro_gateway_ready = true;
2813
  } else {
2814
  $pmpro_gateway_ready = false;
2856
  return $r;
2857
  }
2858
 
2859
+ /**
2860
+ * Display Invoice Price Data with Parts
2861
+ *
2862
+ * @param object $pmpro_invoice The full order object.
2863
+ * @param string $format Format of the return value. Accepts array, span, list, or line_breaks.
2864
+ *
2865
+ * @return array|string $price_parts The array or formatted HTML string to display price parts and total.
2866
+ *
2867
+ */
2868
+ function pmpro_get_price_parts( $pmpro_invoice, $format = 'array' ) {
2869
+ $pmpro_price_parts = array();
2870
+
2871
+ if ( ! empty( $pmpro_invoice->subtotal ) && $pmpro_invoice->subtotal != $pmpro_invoice->total ) {
2872
+ $pmpro_price_parts['subtotal'] = array(
2873
+ 'label' => __( 'Subtotal', 'paid-memberships-pro' ),
2874
+ 'value' => pmpro_escape_price( pmpro_formatPrice( $pmpro_invoice->subtotal ) ),
2875
+ );
2876
+ }
2877
+
2878
+ if ( ! empty( $pmpro_invoice->tax ) ) {
2879
+ $pmpro_price_parts['tax'] = array(
2880
+ 'label' => __( 'Tax', 'paid-memberships-pro' ),
2881
+ 'value' => pmpro_escape_price( pmpro_formatPrice( $pmpro_invoice->tax ) ),
2882
+ );
2883
+ }
2884
+
2885
+ if ( ! empty( $pmpro_invoice->couponamount ) ) {
2886
+ // We don't even use this but it is in the database so it could be shown here.
2887
+ $pmpro_price_parts['couponamount'] = array(
2888
+ 'label' => __( 'Coupon', 'paid-memberships-pro' ),
2889
+ 'value' => pmpro_escape_price( pmpro_formatPrice( $pmpro_invoice->couponamount ) ),
2890
+ );
2891
+ }
2892
+
2893
+ /**
2894
+ * Filter to modify the price parts, add parts, or modify the display. Does not include the order total.
2895
+ *
2896
+ * @param array $pmpro_price_parts The array of price parts not including the total.
2897
+ * @param string $format Format of the return value passed to the function.
2898
+ * @param object $pmpro_invoice The full order object.
2899
+ *
2900
+ * @return array $pmpro_price_parts Filtered array of price parts not including the total.
2901
+ *
2902
+ */
2903
+ $pmpro_price_parts = apply_filters( 'pmpro_get_price_parts', $pmpro_price_parts, $pmpro_invoice );
2904
+
2905
+ $pmpro_price_parts_with_total = $pmpro_price_parts;
2906
+
2907
+ if ( ! empty( $pmpro_invoice->total ) ) {
2908
+ $pmpro_price_parts_with_total['total'] = array(
2909
+ 'label' => __( 'Total', 'paid-memberships-pro' ),
2910
+ 'value' => pmpro_escape_price( pmpro_formatPrice( $pmpro_invoice->total ) ),
2911
+ );
2912
+ }
2913
+
2914
+ /**
2915
+ * Filter including the total price to modify the price parts, add parts, or modify the display.
2916
+ *
2917
+ * @param array $pmpro_price_parts The array of price parts including the total.
2918
+ * @param string $format Format of the return value passed to the function.
2919
+ * @param object $pmpro_invoice The full order object.
2920
+ *
2921
+ * @return array $pmpro_price_parts Filtered array of price parts not including the total.
2922
+ *
2923
+ */
2924
+ $pmpro_price_parts_with_total = apply_filters( 'pmpro_get_price_parts_with_total', $pmpro_price_parts_with_total, $pmpro_invoice );
2925
+
2926
+ if ( $format == 'array' ) {
2927
+ return $pmpro_price_parts_with_total;
2928
+ } else {
2929
+ // Start building our formatted return string.
2930
+ $pmpro_price = '';
2931
+ if ( $format == 'span' ) {
2932
+ foreach ( $pmpro_price_parts_with_total as $key => $pmpro_price_part ) {
2933
+ $pmpro_price .= '<span class="' . pmpro_get_element_class( 'pmpro_price_part_span pmpro_price_part-' . sanitize_html_class( $key ), 'pmpro_price_part-' . sanitize_html_class( $key ) ) . '"><span class="' . pmpro_get_element_class( 'pmpro_price_part_label' ) . '">' . esc_html( $pmpro_price_part['label'] ) . '</span> <span class="' . pmpro_get_element_class( 'pmpro_price_part_price' ) . '">' . esc_html( $pmpro_price_part['value'] ) . '</span></span>';
2934
+ }
2935
+ } elseif ( $format == 'list' ) {
2936
+ $pmpro_price .= '<ul class="' . pmpro_get_element_class( 'pmpro_price_part_list' ) . '">';
2937
+ foreach ( $pmpro_price_parts_with_total as $key => $pmpro_price_part ) {
2938
+ $pmpro_price .= '<li class="' . pmpro_get_element_class( 'pmpro_price_part-' . sanitize_html_class( $key ), 'pmpro_price_part-' . sanitize_html_class( $key ) ) . '"><span class="' . pmpro_get_element_class( 'pmpro_price_part_label' ) . '">' . esc_html( $pmpro_price_part['label'] ) . '</span> <span class="' . pmpro_get_element_class( 'pmpro_price_part_price' ) . '">' . esc_html( $pmpro_price_part['value'] ) . '</span></li>';
2939
+ }
2940
+ } else {
2941
+ // Default to each line separate by breaks.
2942
+ foreach ( $pmpro_price_parts_with_total as $key => $pmpro_price_part ) {
2943
+ $pmpro_price .= '<span class="' . pmpro_get_element_class( 'pmpro_price_part-' . sanitize_html_class( $key ), 'pmpro_price_part-' . sanitize_html_class( $key ) ) . '"><span class="' . pmpro_get_element_class( 'pmpro_price_part_label' ) . '">' . esc_html( $pmpro_price_part['label'] ) . '</span> <span class="' . pmpro_get_element_class( 'pmpro_price_part_price' ) . '">' . esc_html( $pmpro_price_part['value'] ) . '</span></span><br />';
2944
+ }
2945
+ }
2946
+ }
2947
+ return $pmpro_price;
2948
+ }
2949
+
2950
  /**
2951
  * Format a price per the currency settings.
2952
  *
2991
  return apply_filters( 'pmpro_format_price', $formatted, $price, $pmpro_currency, $pmpro_currency_symbol );
2992
  }
2993
 
2994
+ /**
2995
+ * Filter a sanitized price for display with only the allowed HTML.
2996
+ *
2997
+ * @since 2.5.7
2998
+ *
2999
+ * @param string $price A price value.
3000
+ * @return string $price The escaped price with allowed HTML.
3001
+ *
3002
+ */
3003
+ function pmpro_escape_price( $price ) {
3004
+ $allowed_price_html = apply_filters(
3005
+ 'pmpro_escape_price_html',
3006
+ array(
3007
+ 'div' => array (
3008
+ 'class' => array(),
3009
+ 'id' => array(),
3010
+ ),
3011
+ 'span' => array (
3012
+ 'class' => array(),
3013
+ 'id' => array(),
3014
+ ),
3015
+ 'sup' => array (
3016
+ 'class' => array(),
3017
+ 'id' => array(),
3018
+ ),
3019
+ )
3020
+ );
3021
+ return wp_kses( $price, $allowed_price_html );
3022
+ }
3023
 
3024
  /**
3025
  * Function to trim trailing zeros from an amount.
3104
  }
3105
 
3106
  if ( ! empty( $pmpro_currencies[ $currency ] )
3107
+ && is_array( $pmpro_currencies[ $currency ] )
3108
+ && isset( $pmpro_currencies[ $currency ]['decimals'] ) ) {
3109
  $decimals = intval( $pmpro_currencies[ $currency ]['decimals'] );
3110
  }
3111
 
3112
  $rounded = round( (double) $price, $decimals );
3113
+
3114
  /**
3115
  * Filter for result of pmpro_round_price.
3116
  */
3117
  $rounded = apply_filters( 'pmpro_round_price', $rounded );
3118
+
3119
  return $rounded;
3120
  }
3121
 
3238
  return false;
3239
  }
3240
  }
3241
+
3242
  /**
3243
  * Does the dave provided fall within the current year?
3244
  * Merged in from the Better Logins Report Add On.
3306
  if ( ! in_array( $name, $top_level_pages ) ) {
3307
  $insert['post_parent'] = $pmpro_pages['account'];
3308
  }
3309
+
3310
  // tweak the login slug
3311
  if ( $name == 'login' ) {
3312
  $insert['post_name'] = 'login';
3480
  function pmpro_show_discount_code() {
3481
  global $wpdb;
3482
  static $show;
3483
+
3484
  // check DB if we haven't yet
3485
  if ( !isset( $show ) ) {
3486
  if ( $wpdb->get_var( "SELECT id FROM $wpdb->pmpro_discount_codes LIMIT 1" ) ) {
3489
  $show = false;
3490
  }
3491
  }
3492
+
3493
  $show = apply_filters( "pmpro_show_discount_code", $show );
3494
+
3495
  return $show;
3496
  }
3497
 
3504
  function pmpro_was_checkout_form_submitted() {
3505
  // Default to false.
3506
  $submit = false;
3507
+
3508
  // Basic check for a field called submit-checkout.
3509
  if ( isset( $_REQUEST['submit-checkout'] ) ) {
3510
  $submit = true;
3511
  }
3512
+
3513
  // _x stuff in case they clicked on the image button with their mouse
3514
  if ( empty( $submit ) && isset( $_REQUEST['submit-checkout_x'] ) ) {
3515
  $submit = true;
3516
  }
3517
+
3518
  return $submit;
3519
  }
3520
+
3521
  /**
3522
  * Build the order object used at checkout.
3523
  * @since 2.1
3524
  * @return mixed $order Order object.
3525
  */
3526
+ function pmpro_build_order_for_checkout() {
3527
  global $post, $gateway, $wpdb, $besecure, $discount_code, $discount_code_id, $pmpro_level, $pmpro_levels, $pmpro_msg, $pmpro_msgt, $pmpro_review, $skip_account_fields, $pmpro_paypal_token, $pmpro_show_discount_code, $pmpro_error_fields, $pmpro_required_billing_fields, $pmpro_required_user_fields, $wp_version, $current_user, $pmpro_requirebilling, $tospage, $username, $password, $password2, $bfirstname, $blastname, $baddress1, $baddress2, $bcity, $bstate, $bzipcode, $bcountry, $bphone, $bemail, $bconfirmemail, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear, $pmpro_states, $recaptcha, $recaptcha_privatekey, $CVV;
3528
+
3529
  $morder = new MemberOrder();
3530
  $morder->membership_id = $pmpro_level->id;
3531
  $morder->membership_name = $pmpro_level->name;
3532
  $morder->discount_code = $discount_code;
3533
  $morder->InitialPayment = pmpro_round_price( $pmpro_level->initial_payment );
3534
  $morder->PaymentAmount = pmpro_round_price( $pmpro_level->billing_amount );
3535
+ $morder->ProfileStartDate = date_i18n( "Y-m-d\TH:i:s", current_time( "timestamp" ) );
3536
  $morder->BillingPeriod = $pmpro_level->cycle_period;
3537
  $morder->BillingFrequency = $pmpro_level->cycle_number;
3538
  if ( $pmpro_level->billing_limit ) {
3544
  $morder->TrialBillingCycles = $pmpro_level->trial_limit;
3545
  $morder->TrialAmount = pmpro_round_price( $pmpro_level->trial_amount );
3546
  }
3547
+
3548
  // Credit card values.
3549
  $morder->cardtype = $CardType;
3550
  $morder->accountnumber = $AccountNumber;
3553
  $morder->ExpirationDate = $ExpirationMonth . $ExpirationYear;
3554
  $morder->ExpirationDate_YdashM = $ExpirationYear . "-" . $ExpirationMonth;
3555
  $morder->CVV2 = $CVV;
3556
+
3557
  // Not saving email in order table, but the sites need it.
3558
  $morder->Email = $bemail;
3559
+
3560
  // Save the user ID if logged in.
3561
  if ( $current_user->ID ) {
3562
  $morder->user_id = $current_user->ID;
3563
  }
3564
+
3565
  // Sometimes we need these split up.
3566
  $morder->FirstName = $bfirstname;
3567
  $morder->LastName = $blastname;
3568
  $morder->Address1 = $baddress1;
3569
  $morder->Address2 = $baddress2;
3570
+
3571
  // Set other values.
3572
  $morder->billing = new stdClass();
3573
  $morder->billing->name = $bfirstname . " " . $blastname;
3576
  $morder->billing->state = $bstate;
3577
  $morder->billing->country = $bcountry;
3578
  $morder->billing->zip = $bzipcode;
3579
+ $morder->billing->phone = $bphone;
3580
  $morder->gateway = $gateway;
3581
  $morder->setGateway();
3582
+
3583
  // Set up level var.
3584
  $morder->getMembershipLevelAtCheckout();
3585
+
3586
  // Set tax.
3587
  $initial_tax = $morder->getTaxForPrice( $morder->InitialPayment );
3588
  $recurring_tax = $morder->getTaxForPrice( $morder->PaymentAmount );
3589
+
3590
  // Set amounts.
3591
  $morder->initial_amount = pmpro_round_price((float)$morder->InitialPayment + (float)$initial_tax);
3592
  $morder->subscription_amount = pmpro_round_price((float)$morder->PaymentAmount + (float)$recurring_tax);
3593
+
3594
  // Filter for order, since v1.8
3595
  $morder = apply_filters( 'pmpro_checkout_order', $morder );
3596
+
3597
  return $morder;
3598
  }
3599
 
3615
  $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file, false, true );
3616
 
3617
  // Return false if there is no plugin data.
3618
+ if ( empty( $plugin_data ) || empty( $plugin_data['Version'] ) ) {
3619
  return false;
3620
  }
3621
 
3636
  * @param string $operator Operator to use, e.g. >, <, >=, <=, =.
3637
  * @return bool true or false based on the operator passed in. Returns null for invalid operators.
3638
  */
3639
+ function pmpro_int_compare( $a, $b, $operator ) {
3640
  switch ( $operator ) {
3641
  case '>':
3642
  $r = (int)$a > (int)$b;
3651
  $r = (int)$a <= (int)$b;
3652
  break;
3653
  case '=':
3654
+ case '==':
3655
  $r = (int)$a == (int)$b;
3656
  break;
3657
  default:
3658
  $r = null;
3659
  }
3660
+
3661
  return $r;
3662
  }
3663
 
3671
  */
3672
  function pmpro_insert_or_replace( $table, $data, $format, $primary_key = 'id' ) {
3673
  global $wpdb;
3674
+
3675
  if ( empty( $data[$primary_key] ) ) {
3676
  // Insert. Remove keys first.
3677
  $index = array_search( $primary_key, array_keys( $data ) );
3682
  return $wpdb->insert( $table, $data, $format );
3683
  } else {
3684
  // Replace.
3685
+ $replaced = $wpdb->replace( $table, $data, $format );
3686
  }
3687
+ }
3688
+
3689
+ /**
3690
+ * Checks if a webhook is running
3691
+ * @since 2.5
3692
+ * @param string $gateway If passed in, requires that specific gateway.
3693
+ * @param bool $set Set to true to set the constant and fire the action hook.
3694
+ * @return bool True or false if a PMPro webhook set the constant or not.
3695
+ */
3696
+ function pmpro_doing_webhook( $gateway = null, $set = false ){
3697
+ // If second param is set, set things up.
3698
+ if ( ! empty( $set ) ) {
3699
+ define( 'PMPRO_DOING_WEBHOOK', $gateway );
3700
+ do_action( 'pmpro_doing_webhook', $gateway );
3701
+ return true;
3702
+ }
3703
+
3704
+ // Otherwise, check if we were already set up.
3705
+ if( defined( 'PMPRO_DOING_WEBHOOK' ) && !empty ( PMPRO_DOING_WEBHOOK ) ){
3706
+ if( $gateway !== null ){
3707
+ if( PMPRO_DOING_WEBHOOK == $gateway ){
3708
+ return true;
3709
+ } else {
3710
+ return false;
3711
+ }
3712
+ } else {
3713
+ return true;
3714
+ }
3715
+ } else {
3716
+ return false;
3717
+ }
3718
+
3719
+ }
3720
+
3721
+ /**
3722
+ * Sanitizing strings using wp_kses and allowing style tags.
3723
+ *
3724
+ * @param string $original_string The string to sanitize.
3725
+ * @param string $context The sanitization context.
3726
+ *
3727
+ * @return string The sanitized string.
3728
+ *
3729
+ * @since 2.6.1
3730
+ */
3731
+ function pmpro_kses( $original_string, $context = 'email' ) {
3732
+ $context = 'pmpro_' . $context;
3733
+
3734
+ $sanitized_string = $original_string;
3735
+
3736
+ if ( 'pmpro_email' === $context ) {
3737
+ // Always remove script tags and their contents.
3738
+ $sanitized_string = preg_replace( '@<script[^>]*?>.*?</script>@si', '', $sanitized_string );
3739
+ }
3740
+
3741
+ $sanitized_string = wp_kses( $sanitized_string, $context );
3742
+
3743
+ /**
3744
+ * Allow overriding the normal pmpro_kses functionality for a context.
3745
+ *
3746
+ * @param string $sanitized_string The sanitized string.
3747
+ * @param string $original_string The original string.
3748
+ * @param string $context The sanitization context.
3749
+ *
3750
+ * @since 2.6.2
3751
+ */
3752
+ return apply_filters( 'pmpro_kses', $sanitized_string, $original_string, $context );
3753
+ }
3754
+
3755
+ /**
3756
+ * Filter the allowed HTML tags for PMPro contexts.
3757
+ *
3758
+ * @param array[]|string $allowed_html The allowed HTML tags.
3759
+ * @param string $context The context name.
3760
+ *
3761
+ * @since 2.6.2
3762
+ */
3763
+ function pmpro_kses_allowed_html( $allowed_html, $context ) {
3764
+ // Only override for our pmpro_* contexts.
3765
+ if ( 0 !== strpos( $context, 'pmpro_' ) ) {
3766
+ return $allowed_html;
3767
+ }
3768
+
3769
+ $custom_tags = [];
3770
+
3771
+ if ( 'pmpro_email' === $context ) {
3772
+ $custom_tags['html'] = [
3773
+ 'xmlns' => true,
3774
+ 'xmlns:v' => true,
3775
+ 'xmlns:o' => true,
3776
+ ];
3777
+ $custom_tags['head'] = [];
3778
+ $custom_tags['xml'] = [];
3779
+ $custom_tags['meta'] = [
3780
+ 'name' => true,
3781
+ 'content' => true,
3782
+ 'charset' => true,
3783
+ 'http-equiv' => true,
3784
+ ];
3785
+ $custom_tags['title'] = [];
3786
+ $custom_tags['body'] = [];
3787
+
3788
+ $custom_tags['table'] = [
3789
+ 'height' => true,
3790
+ 'style' => true,
3791
+ ];
3792
+ $custom_tags['a'] = [
3793
+ 'style' => true,
3794
+ ];
3795
+ $custom_tags['style'] = [
3796
+ 'type' => true,
3797
+ ];
3798
+ }
3799
+
3800
+ // Our default context starts with what is available for posts.
3801
+ $allowed_html = wp_kses_allowed_html( 'post' );
3802
+
3803
+ // Merge the allowed HTML tags into our custom tags in case post already has support for it + more.
3804
+ foreach ( $custom_tags as $tag => $attributes ) {
3805
+ // Maybe merge our attributes into an already defined tag's attributes.
3806
+ if ( isset( $allowed_html[ $tag ] ) && true !== $attributes ) {
3807
+ $attributes = array_merge( $allowed_html[ $tag ], $attributes );
3808
+ }
3809
+
3810
+ $allowed_html[ $tag ] = $attributes;
3811
+ }
3812
+
3813
+ return $allowed_html;
3814
+ }
3815
+ add_filter( 'wp_kses_allowed_html', 'pmpro_kses_allowed_html', 10, 2 );
3816
+
3817
+ /**
3818
+ * Show deprecation warning if calling function was called publically.
3819
+ *
3820
+ * Useful for preparing to change method visibility from public to private.
3821
+ *
3822
+ * @param string $deprecation_notice_version to show.
3823
+ * @return bool
3824
+ */
3825
+ function pmpro_method_should_be_private( $deprecated_notice_version ) {
3826
+ $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS );
3827
+
3828
+ // Check whether the caller of this function is in the same file (class)
3829
+ // as the caller of the previous function.
3830
+ if ( $backtrace[0]['file'] !== $backtrace[1]['file'] ) {
3831
+ _deprecated_function( $backtrace[1]['function'], $deprecated_notice_version );
3832
+ return true;
3833
+ }
3834
+ return false;
3835
+ }
3836
+
3837
+ /**
3838
+ * Send a 200 HTTP reponse without ending PHP execution.
3839
+ *
3840
+ * Useful to avoid issues like timeouts from gateways during
3841
+ * webhook/IPN handlers.
3842
+ *
3843
+ * Works with Apache and Nginx.
3844
+ *
3845
+ * Based on code from https://stackoverflow.com/a/42245266
3846
+ *
3847
+ * @since 2.6.4
3848
+ */
3849
+ function pmpro_send_200_http_response() {
3850
+ /**
3851
+ * Allow filtering whether to send an early 200 HTTP response.
3852
+ *
3853
+ * @since 2.6.4
3854
+ *
3855
+ * @param bool $send_early_response Whether to send an early 200 HTTP response.
3856
+ */
3857
+ if ( ! apply_filters( 'pmpro_send_200_http_response', false ) ) {
3858
+ return;
3859
+ }
3860
+
3861
+ // Ngnix compatibility: Check if fastcgi_finish_request is callable.
3862
+ if ( is_callable( 'fastcgi_finish_request' ) ) {
3863
+ session_write_close();
3864
+ fastcgi_finish_request();
3865
+
3866
+ return;
3867
+ }
3868
+
3869
+ ignore_user_abort(true);
3870
+
3871
+ ob_start();
3872
+ $server_protocol = filter_input( INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING );
3873
+ if ( ! in_array( $server_protocol, array( 'HTTP/1.1', 'HTTP/2', 'HTTP/2.0' ), true ) ) {
3874
+ $server_protocol = 'HTTP/1.0';
3875
+ }
3876
+
3877
+ header( $server_protocol . ' 200 OK' );
3878
+ header( 'Content-Encoding: none' );
3879
+ header( 'Content-Length: ' . ob_get_length() );
3880
+ header( 'Connection: close' );
3881
+
3882
+ ob_end_flush();
3883
+ ob_flush();
3884
+ flush();
3885
+ }
3886
+
3887
+ /**
3888
+ * Returns formatted ISO-8601 date (Used for Zapier Native app.)
3889
+ * @since 2.6.6
3890
+ * @param $date date A valid date value.
3891
+ * @return string The date in ISO-8601 format.
3892
+ */
3893
+ function pmpro_format_date_iso8601( $date ) {
3894
+ $datetime = new DateTime( $date );
3895
+ return $datetime->format( DateTime::ATOM );
3896
+ }
3897
+
3898
+ /**
3899
+ * Determines the user's actual IP address
3900
+ *
3901
+ * $_SERVER['REMOTE_ADDR'] cannot be used in all cases, such as when the user
3902
+ * is making their request through a proxy, or when the web server is behind
3903
+ * a proxy. In those cases, $_SERVER['REMOTE_ADDR'] is set to the proxy address rather
3904
+ * than the user's actual address.
3905
+ *
3906
+ * Modified from WP_Community_Events::get_unsafe_client_ip() in core WP.
3907
+ * Modified from https://stackoverflow.com/a/2031935/450127, MIT license.
3908
+ * Modified from https://github.com/geertw/php-ip-anonymizer, MIT license.
3909
+ *
3910
+ * SECURITY WARNING: This function is _NOT_ intended to be used in
3911
+ * circumstances where the authenticity of the IP address matters. This does
3912
+ * _NOT_ guarantee that the returned address is valid or accurate, and it can
3913
+ * be easily spoofed.
3914
+ *
3915
+ * @since 2.7
3916
+ *
3917
+ * @return string|false The ip address on success or false on failure.
3918
+ */
3919
+ function pmpro_get_ip() {
3920
+ $client_ip = false;
3921
+
3922
+ // In order of preference, with the best ones for this purpose first.
3923
+ // Added some from JetPack's Jetpack_Protect_Module::get_headers()
3924
+ $address_headers = array(
3925
+ 'GD_PHP_HANDLER',
3926
+ 'HTTP_AKAMAI_ORIGIN_HOP',
3927
+ 'HTTP_CF_CONNECTING_IP',
3928
+ 'HTTP_CLIENT_IP',
3929
+ 'HTTP_FASTLY_CLIENT_IP',
3930
+ 'HTTP_FORWARDED',
3931
+ 'HTTP_FORWARDED_FOR',
3932
+ 'HTTP_INCAP_CLIENT_IP',
3933
+ 'HTTP_TRUE_CLIENT_IP',
3934
+ 'HTTP_X_CLIENTIP',
3935
+ 'HTTP_X_CLUSTER_CLIENT_IP',
3936
+ 'HTTP_X_FORWARDED',
3937
+ 'HTTP_X_FORWARDED_FOR',
3938
+ 'HTTP_X_IP_TRAIL',
3939
+ 'HTTP_X_REAL_IP',
3940
+ 'HTTP_X_VARNISH',
3941
+ 'REMOTE_ADDR',
3942
+ );
3943
+
3944
+ foreach ( $address_headers as $header ) {
3945
+ if ( array_key_exists( $header, $_SERVER ) ) {
3946
+ /*
3947
+ * HTTP_X_FORWARDED_FOR can contain a chain of comma-separated
3948
+ * addresses. The first one is the original client. It can't be
3949
+ * trusted for authenticity, but we don't need to for this purpose.
3950
+ */
3951
+ $address_chain = explode( ',', $_SERVER[ $header ] );
3952
+ $client_ip = trim( $address_chain[0] );
3953
+
3954
+ break;
3955
+ }
3956
+ }
3957
+
3958
+ if ( ! $client_ip ) {
3959
+ return false;
3960
+ }
3961
+
3962
+ // Sanitize the IP
3963
+ $client_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $client_ip );
3964
+
3965
+ return $client_ip;
3966
  }
includes/init.php CHANGED
@@ -7,6 +7,7 @@ function pmpro_init() {
7
  require_once(PMPRO_DIR . '/includes/countries.php');
8
  require_once(PMPRO_DIR . '/includes/states.php');
9
  require_once(PMPRO_DIR . '/includes/currencies.php');
 
10
 
11
  global $pmpro_pages, $pmpro_core_pages, $pmpro_ready, $pmpro_currencies, $pmpro_currency, $pmpro_currency_symbol;
12
  $pmpro_pages = array();
@@ -69,7 +70,7 @@ function pmpro_wp()
69
  //run the appropriate preheader function
70
  foreach($pmpro_core_pages as $pmpro_page_name => $pmpro_page_id)
71
  {
72
- if(!empty($post->post_content) && strpos($post->post_content, "[pmpro_" . $pmpro_page_name . "]") !== false)
73
  {
74
  //preheader
75
  require_once(PMPRO_DIR . "/preheaders/" . $pmpro_page_name . ".php");
@@ -98,7 +99,7 @@ function pmpro_wp()
98
  }
99
  }
100
  }
101
- add_action("wp", "pmpro_wp", 1);
102
 
103
  /*
104
  Add PMPro page names to the BODY class.
@@ -179,7 +180,6 @@ function pmpro_manage_users_columns($columns) {
179
 
180
  function pmpro_sortable_column($columns)
181
  {
182
- // $columns['pmpro_membership_level'] = ['level', 'desc'];
183
  $columns['pmpro_membership_level'] = array( 'level', 'desc' );
184
  return $columns;
185
  }
@@ -200,16 +200,19 @@ function pmpro_manage_users_custom_column($column_data, $column_name, $user_id)
200
  return $column_data;
201
  }
202
 
203
- function pmpro_sortable_column_query($query) {
204
  global $wpdb;
205
 
206
  $vars = $query->query_vars;
207
 
208
- if($vars['orderby'] == 'level'){
209
- $query->query_from .= " LEFT JOIN {$wpdb->prefix}pmpro_memberships_users AS pmpro_mu ON {$wpdb->prefix}users.ID = pmpro_mu.user_id AND pmpro_mu.status = 'active'";
210
- $query->query_orderby = "ORDER BY pmpro_mu.membership_id " . $vars['order'] . ", {$wpdb->prefix}users.user_registered";
211
- }
212
 
 
 
 
 
 
213
  }
214
 
215
  add_filter('manage_users_columns', 'pmpro_manage_users_columns');
7
  require_once(PMPRO_DIR . '/includes/countries.php');
8
  require_once(PMPRO_DIR . '/includes/states.php');
9
  require_once(PMPRO_DIR . '/includes/currencies.php');
10
+ require_once(PMPRO_DIR . '/includes/email-templates.php');
11
 
12
  global $pmpro_pages, $pmpro_core_pages, $pmpro_ready, $pmpro_currencies, $pmpro_currency, $pmpro_currency_symbol;
13
  $pmpro_pages = array();
70
  //run the appropriate preheader function
71
  foreach($pmpro_core_pages as $pmpro_page_name => $pmpro_page_id)
72
  {
73
+ if(!empty($post->post_content) && ( strpos($post->post_content, "[pmpro_" . $pmpro_page_name . "]") !== false || has_block( 'pmpro/' . $pmpro_page_name . '-page', $post ) ) )
74
  {
75
  //preheader
76
  require_once(PMPRO_DIR . "/preheaders/" . $pmpro_page_name . ".php");
99
  }
100
  }
101
  }
102
+ add_action("wp", "pmpro_wp", 2);
103
 
104
  /*
105
  Add PMPro page names to the BODY class.
180
 
181
  function pmpro_sortable_column($columns)
182
  {
 
183
  $columns['pmpro_membership_level'] = array( 'level', 'desc' );
184
  return $columns;
185
  }
200
  return $column_data;
201
  }
202
 
203
+ function pmpro_sortable_column_query( $query ) {
204
  global $wpdb;
205
 
206
  $vars = $query->query_vars;
207
 
208
+ if ( $vars['orderby'] == 'level' ){
209
+ $order = pmpro_sanitize_with_safelist( $vars['order'], array( 'asc', 'desc', 'ASC', 'DESC' ) );
 
 
210
 
211
+ if ( ! empty( $order ) ) {
212
+ $query->query_from .= " LEFT JOIN $wpdb->pmpro_memberships_users AS pmpro_mu ON $wpdb->users.ID = pmpro_mu.user_id AND pmpro_mu.status = 'active' LEFT JOIN $wpdb->pmpro_membership_levels AS pmpro_ml ON pmpro_mu.membership_id = pmpro_ml.id";
213
+ $query->query_orderby = "ORDER BY pmpro_ml.name " . esc_sql( $order ) . ", $wpdb->users.user_registered";
214
+ }
215
+ }
216
  }
217
 
218
  add_filter('manage_users_columns', 'pmpro_manage_users_columns');
includes/lib/Braintree/README.md DELETED
@@ -1,134 +0,0 @@
1
- # Braintree PHP library
2
-
3
- The Braintree PHP library provides integration access to the Braintree Gateway.
4
-
5
- ## Please Note
6
- > **The Payment Card Industry (PCI) Council has [mandated](https://blog.pcisecuritystandards.org/migrating-from-ssl-and-early-tls) that early versions of TLS be retired from service. All organizations that handle credit card information are required to comply with this standard. As part of this obligation, Braintree is updating its services to require TLS 1.2 for all HTTPS connections. Braintree will also require HTTP/1.1 for all connections. Please see our [technical documentation](https://github.com/paypal/tls-update) for more information.**
7
-
8
- ## Dependencies
9
-
10
- PHP version >= 5.4.0 is required.
11
-
12
- The following PHP extensions are required:
13
-
14
- * curl
15
- * dom
16
- * hash
17
- * openssl
18
- * xmlwriter
19
-
20
- ## Quick Start Example
21
-
22
- ```php
23
- <?php
24
-
25
- require_once 'PATH_TO_BRAINTREE/lib/Braintree.php';
26
-
27
- // Instantiate a Braintree Gateway either like this:
28
- $gateway = new Braintree_Gateway([
29
- 'environment' => 'sandbox',
30
- 'merchantId' => 'your_merchant_id',
31
- 'publicKey' => 'your_public_key',
32
- 'privateKey' => 'your_private_key'
33
- ]);
34
-
35
- // or like this:
36
- $config = new Braintree_Configuration([
37
- 'environment' => 'sandbox',
38
- 'merchantId' => 'your_merchant_id',
39
- 'publicKey' => 'your_public_key',
40
- 'privateKey' => 'your_private_key'
41
- ]);
42
- $gateway = new Braintree\Gateway($config)
43
-
44
- // Then, create a transaction:
45
- $result = $gateway->transaction()->sale([
46
- 'amount' => '1000.00',
47
- 'paymentMethodNonce' => 'nonceFromTheClient',
48
- 'options' => [ 'submitForSettlement' => true ]
49
- ]);
50
-
51
- if ($result->success) {
52
- print_r("success!: " . $result->transaction->id);
53
- } else if ($result->transaction) {
54
- print_r("Error processing transaction:");
55
- print_r("\n code: " . $result->transaction->processorResponseCode);
56
- print_r("\n text: " . $result->transaction->processorResponseText);
57
- } else {
58
- print_r("Validation errors: \n");
59
- print_r($result->errors->deepAll());
60
- }
61
- ```
62
-
63
- Both PSR-0 and PSR-4 namespacing are supported. If you are using composer with `--classmap-authoritative` or
64
- `--optimize-autoloader` enabled, you'll have to reference classes using PSR-4 namespacing:
65
-
66
- ```php
67
- $gateway = new Braintree\Gateway([
68
- 'environment' => 'sandbox',
69
- 'merchantId' => 'your_merchant_id',
70
- 'publicKey' => 'your_public_key',
71
- 'privateKey' => 'your_private_key'
72
- ]);
73
-
74
- // or
75
-
76
- $config = new Braintree\Configuration([
77
- 'environment' => 'sandbox',
78
- 'merchantId' => 'your_merchant_id',
79
- 'publicKey' => 'your_public_key',
80
- 'privateKey' => 'your_private_key'
81
- ]);
82
- $gateway = new Braintree\Gateway($config)
83
- ```
84
-
85
- ## HHVM Support
86
-
87
- The Braintree PHP library will run on HHVM >= 3.4.2.
88
-
89
- ## Google App Engine Support
90
-
91
- When using Google App Engine include the curl extention in your `php.ini` file (see [#190](https://github.com/braintree/braintree_php/issues/190) for more information):
92
-
93
- ```ini
94
- extension = "curl.so"
95
- ```
96
-
97
- and turn off accepting gzip responses:
98
-
99
- ```php
100
- $gateway = new Braintree\Gateway([
101
- 'environment' => 'sandbox',
102
- // ...
103
- 'acceptGzipEncoding' => false,
104
- ]);
105
- ```
106
-
107
- ## Legacy PHP Support
108
-
109
- Version [2.40.0](https://github.com/braintree/braintree_php/releases/tag/2.40.0) is compatible with PHP 5.2 and 5.3. You can find it on our releases page.
110
-
111
- ## Documentation
112
-
113
- * [Official documentation](https://developers.braintreepayments.com/php/sdk/server/overview)
114
-
115
- ## Developing (Docker)
116
-
117
- The `Makefile` and `Dockerfile` will build an image containing the dependencies and drop you to a terminal where you can run tests.
118
-
119
- ```
120
- make
121
- ```
122
-
123
- ## Testing
124
-
125
- The unit specs can be run by anyone on any system, but the integration specs are meant to be run against a local development server of our gateway code. These integration specs are not meant for public consumption and will likely fail if run on your system. To run unit tests use rake: `rake test:unit`.
126
-
127
- The benefit of the `rake` tasks is that testing covers default `hhvm` and `php` interpreters. However, if you want to run tests manually simply use the following command:
128
- ```
129
- phpunit tests/unit/
130
- ```
131
-
132
- ## License
133
-
134
- See the LICENSE file.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/lib/Braintree/composer.json DELETED
@@ -1,36 +0,0 @@
1
- {
2
- "name": "braintree/braintree_php",
3
- "type": "library",
4
- "description": "Braintree PHP Client Library",
5
- "license": "MIT",
6
- "authors": [
7
- {
8
- "name": "Braintree",
9
- "homepage": "https://www.braintreepayments.com"
10
- }
11
- ],
12
- "require": {
13
- "php": ">=5.4.0",
14
- "ext-curl": "*",
15
- "ext-dom": "*",
16
- "ext-hash": "*",
17
- "ext-openssl": "*",
18
- "ext-xmlwriter": "*"
19
- },
20
- "require-dev": {
21
- "phpunit/phpunit": "3.7.*"
22
- },
23
- "autoload": {
24
- "psr-0": {
25
- "Braintree": "lib/"
26
- },
27
- "psr-4": {
28
- "Braintree\\": "lib/Braintree"
29
- }
30
- },
31
- "autoload-dev": {
32
- "psr-4": {
33
- "Test\\": "tests"
34
- }
35
- }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/lib/Braintree/lib/Braintree/Util.php CHANGED
@@ -99,7 +99,7 @@ class Util
99
 
100
  switch($error["extensions"]["errorClass"]) {
101
  case "VALIDATION":
102
- continue;
103
  case "AUTHENTICATION":
104
  throw new Exception\Authentication();
105
  break;
99
 
100
  switch($error["extensions"]["errorClass"]) {
101
  case "VALIDATION":
102
+ continue 2;
103
  case "AUTHENTICATION":
104
  throw new Exception\Authentication();
105
  break;
includes/lib/Stripe/README.md DELETED
@@ -1,266 +0,0 @@
1
- # Stripe PHP bindings
2
-
3
- [![Build Status](https://travis-ci.org/stripe/stripe-php.svg?branch=master)](https://travis-ci.org/stripe/stripe-php)
4
- [![Latest Stable Version](https://poser.pugx.org/stripe/stripe-php/v/stable.svg)](https://packagist.org/packages/stripe/stripe-php)
5
- [![Total Downloads](https://poser.pugx.org/stripe/stripe-php/downloads.svg)](https://packagist.org/packages/stripe/stripe-php)
6
- [![License](https://poser.pugx.org/stripe/stripe-php/license.svg)](https://packagist.org/packages/stripe/stripe-php)
7
- [![Code Coverage](https://coveralls.io/repos/stripe/stripe-php/badge.svg?branch=master)](https://coveralls.io/r/stripe/stripe-php?branch=master)
8
-
9
- The Stripe PHP library provides convenient access to the Stripe API from
10
- applications written in the PHP language. It includes a pre-defined set of
11
- classes for API resources that initialize themselves dynamically from API
12
- responses which makes it compatible with a wide range of versions of the Stripe
13
- API.
14
-
15
- ## Requirements
16
-
17
- PHP 5.6.0 and later.
18
-
19
- ## Composer
20
-
21
- You can install the bindings via [Composer](http://getcomposer.org/). Run the following command:
22
-
23
- ```bash
24
- composer require stripe/stripe-php
25
- ```
26
-
27
- To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/01-basic-usage.md#autoloading):
28
-
29
- ```php
30
- require_once('vendor/autoload.php');
31
- ```
32
-
33
- ## Manual Installation
34
-
35
- If you do not wish to use Composer, you can download the [latest release](https://github.com/stripe/stripe-php/releases). Then, to use the bindings, include the `init.php` file.
36
-
37
- ```php
38
- require_once('/path/to/stripe-php/init.php');
39
- ```
40
-
41
- ## Dependencies
42
-
43
- The bindings require the following extensions in order to work properly:
44
-
45
- - [`curl`](https://secure.php.net/manual/en/book.curl.php), although you can use your own non-cURL client if you prefer
46
- - [`json`](https://secure.php.net/manual/en/book.json.php)
47
- - [`mbstring`](https://secure.php.net/manual/en/book.mbstring.php) (Multibyte String)
48
-
49
- If you use Composer, these dependencies should be handled automatically. If you install manually, you'll want to make sure that these extensions are available.
50
-
51
- ## Getting Started
52
-
53
- Simple usage looks like:
54
-
55
- ```php
56
- $stripe = new \Stripe\StripeClient('sk_test_BQokikJOvBiI2HlWgH4olfQ2');
57
- $customer = $stripe->customers->create([
58
- 'description' => 'example customer',
59
- 'email' => 'email@example.com',
60
- 'payment_method' => 'pm_card_visa',
61
- ]);
62
- echo $customer;
63
- ```
64
-
65
- ### Client/service patterns vs legacy patterns
66
-
67
- You can continue to use the legacy integration patterns used prior to version [7.33.0](https://github.com/stripe/stripe-php/blob/master/CHANGELOG.md#7330---2020-05-14). Review the [migration guide](https://github.com/stripe/stripe-php/wiki/Migration-to-StripeClient-and-services-in-7.33.0) for the backwards-compatible client/services pattern changes.
68
-
69
- ## Documentation
70
-
71
- See the [PHP API docs](https://stripe.com/docs/api/php#intro).
72
-
73
- ## Legacy Version Support
74
-
75
- ### PHP 5.4 & 5.5
76
-
77
- If you are using PHP 5.4 or 5.5, you can download v6.21.1 ([zip](https://github.com/stripe/stripe-php/archive/v6.21.1.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.9.2.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
78
-
79
- ### PHP 5.3
80
-
81
- If you are using PHP 5.3, you can download v5.9.2 ([zip](https://github.com/stripe/stripe-php/archive/v5.9.2.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.9.2.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
82
-
83
- ## Custom Request Timeouts
84
-
85
- _NOTE:_ We do not recommend decreasing the timeout for non-read-only calls (e.g. charge creation), since even if you locally timeout, the request on Stripe's side can still complete. If you are decreasing timeouts on these calls, make sure to use [idempotency tokens](https://stripe.com/docs/api/php#idempotent_requests) to avoid executing the same transaction twice as a result of timeout retry logic.
86
-
87
- To modify request timeouts (connect or total, in seconds) you'll need to tell the API client to use a CurlClient other than its default. You'll set the timeouts in that CurlClient.
88
-
89
- ```php
90
- // set up your tweaked Curl client
91
- $curl = new \Stripe\HttpClient\CurlClient();
92
- $curl->setTimeout(10); // default is \Stripe\HttpClient\CurlClient::DEFAULT_TIMEOUT
93
- $curl->setConnectTimeout(5); // default is \Stripe\HttpClient\CurlClient::DEFAULT_CONNECT_TIMEOUT
94
-
95
- echo $curl->getTimeout(); // 10
96
- echo $curl->getConnectTimeout(); // 5
97
-
98
- // tell Stripe to use the tweaked client
99
- \Stripe\ApiRequestor::setHttpClient($curl);
100
-
101
- // use the Stripe API client as you normally would
102
- ```
103
-
104
- ## Custom cURL Options (e.g. proxies)
105
-
106
- Need to set a proxy for your requests? Pass in the requisite `CURLOPT_*` array to the CurlClient constructor, using the same syntax as `curl_stopt_array()`. This will set the default cURL options for each HTTP request made by the SDK, though many more common options (e.g. timeouts; see above on how to set those) will be overridden by the client even if set here.
107
-
108
- ```php
109
- // set up your tweaked Curl client
110
- $curl = new \Stripe\HttpClient\CurlClient([CURLOPT_PROXY => 'proxy.local:80']);
111
- // tell Stripe to use the tweaked client
112
- \Stripe\ApiRequestor::setHttpClient($curl);
113
- ```
114
-
115
- Alternately, a callable can be passed to the CurlClient constructor that returns the above array based on request inputs. See `testDefaultOptions()` in `tests/CurlClientTest.php` for an example of this behavior. Note that the callable is called at the beginning of every API request, before the request is sent.
116
-
117
- ### Configuring a Logger
118
-
119
- The library does minimal logging, but it can be configured
120
- with a [`PSR-3` compatible logger][psr3] so that messages
121
- end up there instead of `error_log`:
122
-
123
- ```php
124
- \Stripe\Stripe::setLogger($logger);
125
- ```
126
-
127
- ### Accessing response data
128
-
129
- You can access the data from the last API response on any object via `getLastResponse()`.
130
-
131
- ```php
132
- $customer = $stripe->customers->create([
133
- 'description' => 'example customer',
134
- ]);
135
- echo $customer->getLastResponse()->headers['Request-Id'];
136
- ```
137
-
138
- ### SSL / TLS compatibility issues
139
-
140
- Stripe's API now requires that [all connections use TLS 1.2](https://stripe.com/blog/upgrading-tls). Some systems (most notably some older CentOS and RHEL versions) are capable of using TLS 1.2 but will use TLS 1.0 or 1.1 by default. In this case, you'd get an `invalid_request_error` with the following error message: "Stripe no longer supports API requests made with TLS 1.0. Please initiate HTTPS connections with TLS 1.2 or later. You can learn more about this at [https://stripe.com/blog/upgrading-tls](https://stripe.com/blog/upgrading-tls).".
141
-
142
- The recommended course of action is to [upgrade your cURL and OpenSSL packages](https://support.stripe.com/questions/how-do-i-upgrade-my-stripe-integration-from-tls-1-0-to-tls-1-2#php) so that TLS 1.2 is used by default, but if that is not possible, you might be able to solve the issue by setting the `CURLOPT_SSLVERSION` option to either `CURL_SSLVERSION_TLSv1` or `CURL_SSLVERSION_TLSv1_2`:
143
-
144
- ```php
145
- $curl = new \Stripe\HttpClient\CurlClient([CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1]);
146
- \Stripe\ApiRequestor::setHttpClient($curl);
147
- ```
148
-
149
- ### Per-request Configuration
150
-
151
- For apps that need to use multiple keys during the lifetime of a process, like
152
- one that uses [Stripe Connect][connect], it's also possible to set a
153
- per-request key and/or account:
154
-
155
- ```php
156
- $customers = $stripe->customers->all([],[
157
- 'api_key' => 'sk_test_...',
158
- 'stripe_account' => 'acct_...'
159
- ]);
160
-
161
- $stripe->customers->retrieve('cus_123456789', [], [
162
- 'api_key' => 'sk_test_...',
163
- 'stripe_account' => 'acct_...'
164
- ]);
165
- ```
166
-
167
- ### Configuring CA Bundles
168
-
169
- By default, the library will use its own internal bundle of known CA
170
- certificates, but it's possible to configure your own:
171
-
172
- ```php
173
- \Stripe\Stripe::setCABundlePath("path/to/ca/bundle");
174
- ```
175
-
176
- ### Configuring Automatic Retries
177
-
178
- The library can be configured to automatically retry requests that fail due to
179
- an intermittent network problem:
180
-
181
- ```php
182
- \Stripe\Stripe::setMaxNetworkRetries(2);
183
- ```
184
-
185
- [Idempotency keys][idempotency-keys] are added to requests to guarantee that
186
- retries are safe.
187
-
188
- ### Request latency telemetry
189
-
190
- By default, the library sends request latency telemetry to Stripe. These
191
- numbers help Stripe improve the overall latency of its API for all users.
192
-
193
- You can disable this behavior if you prefer:
194
-
195
- ```php
196
- \Stripe\Stripe::setEnableTelemetry(false);
197
- ```
198
-
199
- ## Development
200
-
201
- Get [Composer][composer]. For example, on Mac OS:
202
-
203
- ```bash
204
- brew install composer
205
- ```
206
-
207
- Install dependencies:
208
-
209
- ```bash
210
- composer install
211
- ```
212
-
213
- The test suite depends on [stripe-mock], so make sure to fetch and run it from a
214
- background terminal ([stripe-mock's README][stripe-mock] also contains
215
- instructions for installing via Homebrew and other methods):
216
-
217
- ```bash
218
- go get -u github.com/stripe/stripe-mock
219
- stripe-mock
220
- ```
221
-
222
- Install dependencies as mentioned above (which will resolve [PHPUnit](http://packagist.org/packages/phpunit/phpunit)), then you can run the test suite:
223
-
224
- ```bash
225
- ./vendor/bin/phpunit
226
- ```
227
-
228
- Or to run an individual test file:
229
-
230
- ```bash
231
- ./vendor/bin/phpunit tests/UtilTest.php
232
- ```
233
-
234
- Update bundled CA certificates from the [Mozilla cURL release][curl]:
235
-
236
- ```bash
237
- ./update_certs.php
238
- ```
239
-
240
- The library uses [PHP CS Fixer][php-cs-fixer] for code formatting. Code must be formatted before PRs are submitted, otherwise CI will fail. Run the formatter with:
241
-
242
- ```bash
243
- ./vendor/bin/php-cs-fixer fix -v .
244
- ```
245
-
246
- ## Attention plugin developers
247
-
248
- Are you writing a plugin that integrates Stripe and embeds our library? Then please use the `setAppInfo` function to identify your plugin. For example:
249
-
250
- ```php
251
- \Stripe\Stripe::setAppInfo("MyAwesomePlugin", "1.2.34", "https://myawesomeplugin.info");
252
- ```
253
-
254
- The method should be called once, before any request is sent to the API. The second and third parameters are optional.
255
-
256
- ### SSL / TLS configuration option
257
-
258
- See the "SSL / TLS compatibility issues" paragraph above for full context. If you want to ensure that your plugin can be used on all systems, you should add a configuration option to let your users choose between different values for `CURLOPT_SSLVERSION`: none (default), `CURL_SSLVERSION_TLSv1` and `CURL_SSLVERSION_TLSv1_2`.
259
-
260
- [composer]: https://getcomposer.org/
261
- [connect]: https://stripe.com/connect
262
- [curl]: http://curl.haxx.se/docs/caextract.html
263
- [idempotency-keys]: https://stripe.com/docs/api/php#idempotent_requests
264
- [php-cs-fixer]: https://github.com/FriendsOfPHP/PHP-CS-Fixer
265
- [psr3]: http://www.php-fig.org/psr/psr-3/
266
- [stripe-mock]: https://github.com/stripe/stripe-mock
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/lib/name-parser.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /*
3
  Taken from: http://code.google.com/p/php-name-parser/
4
-
5
  Changed function names to avoid conflicts.
6
  */
7
 
@@ -14,11 +14,20 @@
14
  if(!function_exists("pnp_split_full_name"))
15
  {
16
  function pnp_split_full_name($full_name) {
17
- if(empty($full_name))
18
- return "";
19
-
 
 
 
 
 
 
 
 
 
20
  $fname = $lname = $initials = NULL;
21
-
22
  $full_name = trim($full_name);
23
  // split into words
24
  $unfiltered_name_parts = explode(" ",$full_name);
@@ -28,11 +37,21 @@ if(!function_exists("pnp_split_full_name"))
28
  if (!empty($word) && $word[0] != "(")
29
  $name_parts[] = $word;
30
  }
 
 
 
 
 
31
  $num_words = sizeof($name_parts);
32
 
33
  // is the first word a title? (Mr. Mrs, etc)
34
  $salutation = pnp_is_salutation($name_parts[0]);
35
- $suffix = pnp_is_suffix($name_parts[sizeof($name_parts)-1]);
 
 
 
 
 
36
 
37
  // set the range for the middle part of the name (trim prefixes & suffixes)
38
  $start = ($salutation) ? 1 : 0;
@@ -48,12 +67,12 @@ if(!function_exists("pnp_split_full_name"))
48
  // is it a middle initial or part of their first name?
49
  // if we start off with an initial, we'll call it the first name
50
  if (pnp_is_initial($word)) {
51
- // is the initial the first word?
52
  if ($i == $start) {
53
  // if so, do a look-ahead to see if they go by their middle name
54
  // for ex: "R. Jason Smith" => "Jason Smith" & "R." is stored as an initial
55
  // but "R. J. Smith" => "R. Smith" and "J." is stored as an initial
56
- if (pnp_is_initial($name_parts[$i+1]))
57
  $fname .= " ".strtoupper($word);
58
  else
59
  $initials .= " ".strtoupper($word);
@@ -63,7 +82,7 @@ if(!function_exists("pnp_split_full_name"))
63
  }
64
  } else {
65
  $fname .= " ".pnp_fix_case($word);
66
- }
67
  }
68
 
69
  // check that we have more than 1 word in our string
1
  <?php
2
  /*
3
  Taken from: http://code.google.com/p/php-name-parser/
4
+
5
  Changed function names to avoid conflicts.
6
  */
7
 
14
  if(!function_exists("pnp_split_full_name"))
15
  {
16
  function pnp_split_full_name($full_name) {
17
+ $name = [
18
+ 'salutation' => '',
19
+ 'fname' => '',
20
+ 'initials' => '',
21
+ 'lname' => '',
22
+ 'suffix' => '',
23
+ ];
24
+
25
+ if ( empty( $full_name ) ) {
26
+ return $name;
27
+ }
28
+
29
  $fname = $lname = $initials = NULL;
30
+
31
  $full_name = trim($full_name);
32
  // split into words
33
  $unfiltered_name_parts = explode(" ",$full_name);
37
  if (!empty($word) && $word[0] != "(")
38
  $name_parts[] = $word;
39
  }
40
+
41
+ if ( empty( $name_parts ) ) {
42
+ return $name;
43
+ }
44
+
45
  $num_words = sizeof($name_parts);
46
 
47
  // is the first word a title? (Mr. Mrs, etc)
48
  $salutation = pnp_is_salutation($name_parts[0]);
49
+
50
+ $suffix = false;
51
+
52
+ if ( isset( $name_parts[sizeof($name_parts)-1] ) ) {
53
+ $suffix = pnp_is_suffix( $name_parts[ sizeof( $name_parts ) - 1 ] );
54
+ }
55
 
56
  // set the range for the middle part of the name (trim prefixes & suffixes)
57
  $start = ($salutation) ? 1 : 0;
67
  // is it a middle initial or part of their first name?
68
  // if we start off with an initial, we'll call it the first name
69
  if (pnp_is_initial($word)) {
70
+ // is the initial the first word?
71
  if ($i == $start) {
72
  // if so, do a look-ahead to see if they go by their middle name
73
  // for ex: "R. Jason Smith" => "Jason Smith" & "R." is stored as an initial
74
  // but "R. J. Smith" => "R. Smith" and "J." is stored as an initial
75
+ if ( isset( $name_parts[ $i + 1 ] ) && pnp_is_initial( $name_parts[ $i + 1 ] ) )
76
  $fname .= " ".strtoupper($word);
77
  else
78
  $initials .= " ".strtoupper($word);
82
  }
83
  } else {
84
  $fname .= " ".pnp_fix_case($word);
85
+ }
86
  }
87
 
88
  // check that we have more than 1 word in our string
includes/lib/stripe-apple-pay/apple-developer-merchantid-domain-association ADDED
@@ -0,0 +1 @@
 
1
+ 7B227073704964223A2239373943394538343346343131343044463144313834343232393232313734313034353044314339464446394437384337313531303944334643463542433731222C2276657273696F6E223A312C22637265617465644F6E223A313536363233343735303036312C227369676E6174757265223A22333038303036303932613836343838366637306430313037303261303830333038303032303130313331306633303064303630393630383634383031363530333034303230313035303033303830303630393261383634383836663730643031303730313030303061303830333038323033653333303832303338386130303330323031303230323038346333303431343935313964353433363330306130363038326138363438636533643034303330323330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313339333033353331333833303331333333323335333735613137306433323334333033353331333633303331333333323335333735613330356633313235333032333036303335353034303330633163363536333633326437333664373032643632373236663662363537323264373336393637366535663535343333343264353035323466343433313134333031323036303335353034306230633062363934663533323035333739373337343635366437333331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034633231353737656465626436633762323231386636386464373039306131323138646337623062643666326332383364383436303935643934616634613534313162383334323065643831316633343037653833333331663163353463336637656233323230643662616435643465666634393238393839336537633066313361333832303231313330383230323064333030633036303335353164313330313031666630343032333030303330316630363033353531643233303431383330313638303134323366323439633434663933653465663237653663346636323836633366613262626664326534623330343530363038326230363031303530353037303130313034333933303337333033353036303832623036303130353035303733303031383632393638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635363136393633363133333330333233303832303131643036303335353164323030343832303131343330383230313130333038323031306330363039326138363438383666373633363430353031333038316665333038316333303630383262303630313035303530373032303233303831623630633831623335323635366336393631366536333635323036663665323037343638363937333230363336353732373436393636363936333631373436353230363237393230363136653739323037303631373237343739323036313733373337353664363537333230363136333633363537303734363136653633363532303666363632303734363836353230373436383635366532303631373037303663363936333631363236633635323037333734363136653634363137323634323037343635373236643733323036313665363432303633366636653634363937343639366636653733323036663636323037353733363532633230363336353732373436393636363936333631373436353230373036663663363936333739323036313665363432303633363537323734363936363639363336313734363936663665323037303732363136333734363936333635323037333734363137343635366436353665373437333265333033363036303832623036303130353035303730323031313632613638373437343730336132663266373737373737326536313730373036633635326536333666366432663633363537323734363936363639363336313734363536313735373436383666373236393734373932663330333430363033353531643166303432643330326233303239613032376130323538363233363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353631363936333631333332653633373236633330316430363033353531643065303431363034313439343537646236666435373438313836383938393736326637653537383530376537396235383234333030653036303335353164306630313031666630343034303330323037383033303066303630393261383634383836663736333634303631643034303230353030333030613036303832613836343863653364303430333032303334393030333034363032323130306265303935373166653731653165373335623535653561666163623463373266656234343566333031383532323263373235313030326236316562643666353530323231303064313862333530613564643664643665623137343630333562313165623263653837636661336536616636636264383338303839306463383263646461613633333038323032656533303832303237356130303330323031303230323038343936643266626633613938646139373330306130363038326138363438636533643034303330323330363733313162333031393036303335353034303330633132343137303730366336353230353236663666373432303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133343330333533303336333233333334333633333330356131373064333233393330333533303336333233333334333633333330356133303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346630313731313834313964373634383564353161356532353831303737366538383061326566646537626165346465303864666334623933653133333536643536363562333561653232643039373736306432323465376262613038666437363137636538386362373662623636373062656338653832393834666635343435613338316637333038316634333034363036303832623036303130353035303730313031303433613330333833303336303630383262303630313035303530373330303138363261363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363537323666366637343633363136373333333031643036303335353164306530343136303431343233663234396334346639336534656632376536633466363238366333666132626266643265346233303066303630333535316431333031303166663034303533303033303130316666333031663036303335353164323330343138333031363830313462626230646561313538333338383961613438613939646562656264656261666461636232346162333033373036303335353164316630343330333032653330326361303261613032383836323636383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635373236663666373436333631363733333265363337323663333030653036303335353164306630313031666630343034303330323031303633303130303630613261383634383836663736333634303630323065303430323035303033303061303630383261383634386365336430343033303230333637303033303634303233303361636637323833353131363939623138366662333563333536636136326266663431376564643930663735346461323865626566313963383135653432623738396638393866373962353939663938643534313064386639646539633266653032333033323264643534343231623061333035373736633564663333383362393036376664313737633263323136643936346663363732363938323132366635346638376137643162393963623962303938393231363130363939306630393932316430303030333138323031386233303832303138373032303130313330383138363330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533303230383463333034313439353139643534333633303064303630393630383634383031363530333034303230313035303061303831393533303138303630393261383634383836663730643031303930333331306230363039326138363438383666373064303130373031333031633036303932613836343838366637306430313039303533313066313730643331333933303338333133393331333733313332333333303561333032613036303932613836343838366637306430313039333433313164333031623330306430363039363038363438303136353033303430323031303530306131306130363038326138363438636533643034303330323330326630363039326138363438383666373064303130393034333132323034323062303731303365313430613462386231376262613230316130336163643036396234653431366232613263383066383661383338313435633239373566633131333030613036303832613836343863653364303430333032303434363330343430323230343639306264636637626461663833636466343934396534633035313039656463663334373665303564373261313264376335666538633033303033343464663032323032363764353863393365626233353031333836363062353730373938613064643731313734316262353864626436613138363633353038353431656565393035303030303030303030303030227D
includes/lib/stripe-apple-pay/stripe-apple-pay.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Add query var if Stripe is looking for domain association file.
5
+ */
6
+ function pmpro_stripe_apple_pay_rewrite_rule() {
7
+ add_rewrite_rule( '^\.well-known\/apple-developer-merchantid-domain-association$', 'index.php?pmpro_stripe_apple_pay=true', 'top' );
8
+ }
9
+ add_action( 'init', 'pmpro_stripe_apple_pay_rewrite_rule' );
10
+
11
+ /*
12
+ * Create query var to detect if Stripe is looking for domain association file.
13
+ */
14
+ function pmpro_stripe_apple_pay_add_query_vars( $qvars ) {
15
+ $qvars[] = 'pmpro_stripe_apple_pay';
16
+ return $qvars;
17
+ }
18
+ add_filter( 'query_vars', 'pmpro_stripe_apple_pay_add_query_vars' );
19
+
20
+ /**
21
+ * If query var is present, serve the domain association file.
22
+ */
23
+ function pmpro_stripe_apple_pay_controller() {
24
+ global $wp_filesystem;
25
+
26
+ if ( empty( get_query_var( 'pmpro_stripe_apple_pay' ) ) ) {
27
+ return;
28
+ }
29
+
30
+ require_once ( ABSPATH . '/wp-admin/includes/file.php' );
31
+ WP_Filesystem();
32
+ echo $wp_filesystem->get_contents( PMPRO_DIR . '/includes/lib/stripe-apple-pay/apple-developer-merchantid-domain-association' );
33
+ exit;
34
+ }
35
+ add_action( 'template_redirect', 'pmpro_stripe_apple_pay_controller' );
36
+
37
+ /**
38
+ * Remove trailing slash from WP redirect if serving domain association file.
39
+ */
40
+ function pmpro_stripe_apple_pay_redirect_canonical_filter( $redirect, $request ) {
41
+ if ( ! empty( get_query_var( 'pmpro_stripe_apple_pay' ) ) ) {
42
+ return false;
43
+ }
44
+ return $redirect;
45
+ }
46
+ add_filter( 'redirect_canonical', 'pmpro_stripe_apple_pay_redirect_canonical_filter', 10, 2 );
includes/license.php CHANGED
@@ -25,39 +25,56 @@
25
  */
26
  define('PMPRO_LICENSE_SERVER', 'https://license.paidmembershipspro.com/');
27
 
28
- /*
29
- Check license.
30
- */
 
 
 
 
31
  function pmpro_license_isValid($key = NULL, $type = NULL, $force = false) {
32
- //check cache first
33
- $pmpro_license_check = get_option('pmpro_license_check', false);
34
- if(empty($force) && $pmpro_license_check !== false && $pmpro_license_check['enddate'] > current_time('timestamp'))
35
- {
36
- if(empty($type))
37
- return true;
38
- elseif($type == $pmpro_license_check['license'])
39
- return true;
40
- else
41
- return false;
42
- }
43
-
44
- //get key and site url
45
- if(empty($key))
46
  $key = get_option("pmpro_license_key", "");
47
-
48
- //no key
49
- if(!empty($key))
50
- {
51
- return pmpro_license_check_key($key);
52
  }
53
- else
54
- {
55
- //no key
56
  delete_option('pmpro_license_check');
57
  add_option('pmpro_license_check', array('license'=>false, 'enddate'=>0), NULL, 'no');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
 
 
59
  return false;
60
  }
 
 
 
61
  }
62
 
63
  /*
@@ -75,134 +92,71 @@ function pmpro_license_deactivation() {
75
  }
76
  register_deactivation_hook(__FILE__, 'pmpro_deactivation');
77
 
78
- //check keys with PMPro once a month
 
 
 
 
 
 
79
  function pmpro_license_check_key($key = NULL) {
80
- //get key
81
- if(empty($key))
82
- $key = get_option('pmpro_license_key');
83
 
84
- //key? check with server
85
- if(!empty($key))
86
- {
87
- //check license server
88
- $url = add_query_arg(array('license'=>$key, 'domain'=>site_url()), PMPRO_LICENSE_SERVER);
89
-
90
- /**
91
- * Filter to change the timeout for this wp_remote_get() request.
92
- *
93
- * @since 1.8.5.1
94
- *
95
- * @param int $timeout The number of seconds before the request times out
96
- */
97
- $timeout = apply_filters("pmpro_license_check_key_timeout", 5);
98
-
99
- $r = wp_remote_get($url, array("timeout" => $timeout));
100
-
101
- //test response
102
- if(is_wp_error($r)) {
103
- //error
104
- pmpro_setMessage("Could not connect to the PMPro License Server to check key Try again later.", "error");
105
- }
106
- elseif(!empty($r) && $r['response']['code'] == 200)
107
- {
108
- $r = json_decode($r['body']);
109
-
110
- if($r->active == 1)
111
- {
112
- //valid key save enddate
113
- if(!empty($r->enddate))
114
- $enddate = strtotime($r->enddate, current_time('timestamp'));
115
- else
116
- $enddate = strtotime("+1 Year", current_time("timestamp"));
117
-
118
- delete_option('pmpro_license_check');
119
- add_option('pmpro_license_check', array('license'=>$r->license, 'enddate'=>$enddate), NULL, 'no');
120
- return true;
121
- }
122
- elseif(!empty($r->error))
123
- {
124
- //invalid key
125
- global $pmpro_license_error;
126
- $pmpro_license_error = $r->error;
127
-
128
- delete_option('pmpro_license_check');
129
- add_option('pmpro_license_check', array('license'=>false, 'enddate'=>0), NULL, 'no');
130
-
131
- }
132
- }
133
- }
134
-
135
- //no key or there was an error
136
- return false;
137
- }
138
- add_action('pmpro_license_check_key', 'pmpro_license_check_key');
139
-
140
- /*
141
- Check for pause
142
- */
143
- function pmpro_license_pause() {
144
- if(!empty($_REQUEST['pmpro_nag_paused']) && current_user_can('manage_options')) {
145
- $pmpro_nag_paused = current_time('timestamp')+(3600*24*7);
146
- update_option('pmpro_nag_paused', $pmpro_nag_paused, 'no');
147
-
148
- return;
149
- }
150
- }
151
- add_action('admin_init', 'pmpro_license_pause');
152
-
153
- /*
154
- Add nags.
155
- */
156
- //nag function embedded into headers of plugins
157
- function pmpro_license_nag() {
158
- static $pmpro_nagged;
159
-
160
- //nagged already?
161
- if(!empty($pmpro_nagged)) {
162
- return;
163
- }
164
-
165
- //remember that we've nagged already
166
- $pmpro_nagged = true;
167
-
168
- //blocked by constant?
169
- if(defined('PMPRO_LICENSE_NAG') && !PMPRO_LICENSE_NAG) {
170
- return;
171
  }
172
 
173
- //don't load on the license page
174
- if(!empty($_REQUEST['page']) && $_REQUEST['page'] == 'pmpro-license') {
175
- return;
176
  }
177
 
178
- //valid license?
179
- if(pmpro_license_isValid()) {
180
- return;
181
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
- $pmpro_nag_paused = get_option('pmpro_nag_paused', 0);
184
- if(current_time('timestamp') < $pmpro_nag_paused && $pmpro_nag_paused < current_time('timestamp')*3600*24*35) {
185
- return;
186
  }
 
 
 
 
 
 
 
 
 
 
187
 
188
- //get key for later
189
- $key = get_option('pmpro_license_key');
190
-
191
- //okay, show nag
192
- ?>
193
- <div class="<?php if(!empty($key)) { ?>error<?php } else { ?>notice notice-warning<?php } ?> fade">
194
- <p>
195
- <?php
196
- //only show the invalid part if they've entered a key
197
-
198
- if(!empty($key)) {
199
- ?><strong><?php _e('Invalid PMPro License Key.', 'paid-memberships-pro' );?></strong><?php
200
- }
201
- ?>
202
- <?php _e("If you're running Paid Memberships Pro on a production website, we recommend an annual support license.", 'paid-memberships-pro' );?>
203
- <a href="<?php echo admin_url('admin.php?page=pmpro-license');?>"><?php _e('More Info', 'paid-memberships-pro' );?></a>&nbsp;|&nbsp;<a href="<?php echo add_query_arg('pmpro_nag_paused', '1', $_SERVER['REQUEST_URI']);?>"><?php _e('Dismiss', 'paid-memberships-pro' );?></a>
204
- </p>
205
- </div>
206
- <?php
207
  }
208
- add_action('admin_notices', 'pmpro_license_nag');
25
  */
26
  define('PMPRO_LICENSE_SERVER', 'https://license.paidmembershipspro.com/');
27
 
28
+ /**
29
+ * Check if a license key is valid.
30
+ * @param string $key The key to check.
31
+ * @param string $type If passed will also check that the key is this type.
32
+ * @param bool $force If true, will check key against the PMPro server.
33
+ * @return bool True if valid, false if not.
34
+ */
35
  function pmpro_license_isValid($key = NULL, $type = NULL, $force = false) {
36
+ // Get key from options if non passed in.
37
+ if( empty( $key ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
38
  $key = get_option("pmpro_license_key", "");
 
 
 
 
 
39
  }
40
+
41
+ // No key? Clean up options and return false.
42
+ if ( empty( $key ) ) {
43
  delete_option('pmpro_license_check');
44
  add_option('pmpro_license_check', array('license'=>false, 'enddate'=>0), NULL, 'no');
45
+ return false;
46
+ }
47
+
48
+ // If force was passed in, let's check with the server.
49
+ if ( $force ) {
50
+ $pmpro_license_check = pmpro_license_check_key( $key );
51
+ }
52
+
53
+ // Get license check value from options.
54
+ $pmpro_license_check = get_option( 'pmpro_license_check', false );
55
+
56
+ // No license info from server?
57
+ if ( empty( $pmpro_license_check ) ) {
58
+ return false;
59
+ }
60
+
61
+ // Server check errored out?
62
+ if ( is_wp_error( $pmpro_license_check ) ) {
63
+ return false;
64
+ }
65
+
66
+ // Check if 30 days past the end date. (We only run the cron every 30 days.)
67
+ if ( $pmpro_license_check['enddate'] < ( current_time( 'timestamp' ) + 86400*31 ) ) {
68
+ return false;
69
+ }
70
 
71
+ // Check if a specific type.
72
+ if ( ! empty( $type ) && $type != $pmpro_license_check['license'] ) {
73
  return false;
74
  }
75
+
76
+ // If we got here, we should be good.
77
+ return true;
78
  }
79
 
80
  /*
92
  }
93
  register_deactivation_hook(__FILE__, 'pmpro_deactivation');
94
 
95
+ /**
96
+ * Check a key against the PMPro license server.
97
+ * Runs via cron every month.
98
+ * @param string The key to check.
99
+ * @return array|WP_Error Returns an array with the key and enddate
100
+ * or WP_Error if invalid or there was an error.
101
+ */
102
  function pmpro_license_check_key($key = NULL) {
103
+ global $pmpro_license_error;
 
 
104
 
105
+ // Get key from options if non passed in.
106
+ if( empty( $key ) ) {
107
+ $key = get_option("pmpro_license_key", "");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  }
109
 
110
+ // No key? Return error.
111
+ if ( empty ( $key ) ) {
112
+ return new WP_Error ( 'no_key', __( 'Missing key.', 'paid-memberships-pro' ) );
113
  }
114
 
115
+ /**
116
+ * Filter to change the timeout for this wp_remote_get() request.
117
+ *
118
+ * @since 1.8.5.1
119
+ *
120
+ * @param int $timeout The number of seconds before the request times out
121
+ */
122
+ $timeout = apply_filters( 'pmpro_license_check_key_timeout', 5 );
123
+
124
+ $url = add_query_arg(array('license'=>$key, 'domain'=>site_url()), PMPRO_LICENSE_SERVER);
125
+ $r = wp_remote_get( $url, array( "timeout" => $timeout ) );
126
+
127
+ // Trouble connecting?
128
+ if( is_wp_error( $r ) ) {
129
+ // Connection error.
130
+ return new WP_Error( 'connection_error', $r->get_error_message() );
131
+ }
132
 
133
+ // Bad response code?
134
+ if ( $r['response']['code'] !== 200 ) {
135
+ return new WP_Error( 'bad_response_code', __( sprintf( 'Bad response code %s.', 'paid-memberships-pro' ), $r['response']['code'] ) );
136
  }
137
+
138
+ // Process the response.
139
+ $r = json_decode($r['body']);
140
+ if( $r->active == 1 ) {
141
+ // Get end date. If none, let's set it 1 year out.
142
+ if( ! empty( $r->enddate ) ) {
143
+ $enddate = strtotime( $r->enddate, current_time( 'timestamp' ) );
144
+ } else {
145
+ $enddate = strtotime( '+1 Year', current_time( 'timestamp' ) );
146
+ }
147
 
148
+ $license_check = array( 'license' => $r->license, 'enddate' => $enddate );
149
+ update_option( 'pmpro_license_check', $license_check, 'no' );
150
+
151
+ return $license_check;
152
+ } elseif ( ! empty( $r->error ) ) {
153
+ // Invalid key. Let's clear out the option.
154
+ update_option( 'pmpro_license_check', array('license'=>false, 'enddate'=>0), 'no' );
155
+
156
+ return new WP_Error( 'invalid_key', $r->error );
157
+ } else {
158
+ // Unknown error. We should maybe clear out the option, but we're not.
159
+ return new WP_Error( 'unknown_error', __( 'Unknown error.', 'paid-memberships-pro' ) );
160
+ }
 
 
 
 
 
 
161
  }
162
+ add_action('pmpro_license_check_key', 'pmpro_license_check_key');
includes/localization.php CHANGED
@@ -2,7 +2,7 @@
2
  function pmpro_load_textdomain()
3
  {
4
  //get the locale
5
- $locale = apply_filters("plugin_locale", get_locale(), "paid-memberships-pro");
6
  $mofile = "paid-memberships-pro-" . $locale . ".mo";
7
 
8
  //paths to local (plugin) and global (WP) language files
@@ -25,11 +25,14 @@ function pmpro_load_textdomain()
25
  add_action("init", "pmpro_load_textdomain", 1);
26
 
27
  function pmpro_translate_billing_period($period, $number = 1)
28
- {
29
  //note as of v1.8, we stopped using _n and split things up to aid in localization
30
  if($number == 1)
31
  {
32
- if($period == "Day")
 
 
 
33
  return __("Day", 'paid-memberships-pro' );
34
  elseif($period == "Week")
35
  return __("Week", 'paid-memberships-pro' );
@@ -39,8 +42,10 @@ function pmpro_translate_billing_period($period, $number = 1)
39
  return __("Year", 'paid-memberships-pro' );
40
  }
41
  else
42
- {
43
- if($period == "Day")
 
 
44
  return __("Days", 'paid-memberships-pro' );
45
  elseif($period == "Week")
46
  return __("Weeks", 'paid-memberships-pro' );
2
  function pmpro_load_textdomain()
3
  {
4
  //get the locale
5
+ $locale = apply_filters("plugin_locale", get_user_locale(), "paid-memberships-pro");
6
  $mofile = "paid-memberships-pro-" . $locale . ".mo";
7
 
8
  //paths to local (plugin) and global (WP) language files
25
  add_action("init", "pmpro_load_textdomain", 1);
26
 
27
  function pmpro_translate_billing_period($period, $number = 1)
28
+ {
29
  //note as of v1.8, we stopped using _n and split things up to aid in localization
30
  if($number == 1)
31
  {
32
+
33
+ if( $period == "Hour" ){
34
+ return __("Hour", "paid-memberships-pro" );
35
+ } else if($period == "Day")
36
  return __("Day", 'paid-memberships-pro' );
37
  elseif($period == "Week")
38
  return __("Week", 'paid-memberships-pro' );
42
  return __("Year", 'paid-memberships-pro' );
43
  }
44
  else
45
+ {
46
+ if( $period == "Hour" ){
47
+ return __("Hours", "paid-memberships-pro" );
48
+ } else if($period == "Day")
49
  return __("Days", 'paid-memberships-pro' );
50
  elseif($period == "Week")
51
  return __("Weeks", 'paid-memberships-pro' );
includes/login.php CHANGED
@@ -31,7 +31,7 @@ function pmpro_login_redirect( $redirect_to, $request = NULL, $user = NULL ) {
31
  // This filter is left in place for PMPro versions dating back to 2014.
32
  return apply_filters( 'pmpro_login_redirect_url', $redirect_to, $request, $user );
33
  }
34
- add_filter( 'login_redirect','pmpro_login_redirect', 10, 3 );
35
 
36
  /**
37
  * Where is the sign up page? Levels page or default multisite page.
@@ -84,12 +84,19 @@ add_action('login_init', 'pmpro_login_head');
84
  * @since 1.7.14
85
  */
86
  function pmpro_redirect_to_logged_in() {
87
- if((pmpro_is_login_page() || is_page("login")) && !empty($_REQUEST['redirect_to']) && is_user_logged_in() && (empty($_REQUEST['action']) || $_REQUEST['action'] == 'login') && empty($_REQUEST['reauth'])) {
88
- wp_safe_redirect($_REQUEST['redirect_to']);
 
 
 
 
 
 
 
89
  exit;
90
  }
91
  }
92
- add_action("template_redirect", "pmpro_redirect_to_logged_in", 5);
93
  add_action("login_init", "pmpro_redirect_to_logged_in", 5);
94
 
95
  /**
@@ -99,9 +106,19 @@ add_action("login_init", "pmpro_redirect_to_logged_in", 5);
99
  * @since 2.3
100
  */
101
  function pmpro_login_url_filter( $login_url='', $redirect='' ) {
 
 
 
 
 
 
102
  $login_page_id = pmpro_getOption( 'login_page_id' );
103
  if ( ! empty ( $login_page_id ) ) {
104
- $login_url = get_permalink( $login_page_id );
 
 
 
 
105
 
106
  if ( ! empty( $redirect ) ) {
107
  $login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url ) ;
@@ -119,7 +136,7 @@ function pmpro_login_url_filter( $login_url='', $redirect='' ) {
119
  *
120
  */
121
  function pmpro_wp_loaded_login_setup() {
122
- add_filter( 'login_url', 'pmpro_login_url_filter', 50, 2 );
123
  }
124
  add_action( 'wp_loaded', 'pmpro_wp_loaded_login_setup' );
125
 
@@ -130,16 +147,16 @@ add_action( 'wp_loaded', 'pmpro_wp_loaded_login_setup' );
130
  function pmpro_use_default_login_for_confirm_admin_email( $location ) {
131
  if ( strpos( $location, 'action=confirm_admin_email' ) !== false ) {
132
  $login_url = wp_login_url();
133
-
134
  remove_filter( 'login_url', 'pmpro_login_url_filter', 50, 2 );
135
  $default_login_url = wp_login_url();
136
  add_filter( 'login_url', 'pmpro_login_url_filter', 50, 2 );
137
-
138
  if ( $login_url != $default_login_url ) {
139
  $location = str_replace( $login_url, $default_login_url, $location );
140
  }
141
  }
142
-
143
  return $location;
144
  }
145
  add_filter( 'wp_redirect', 'pmpro_use_default_login_for_confirm_admin_email' );
@@ -155,22 +172,22 @@ add_filter( 'wp_redirect', 'pmpro_use_default_login_for_confirm_admin_email' );
155
  */
156
  function pmpro_login_url( $redirect = '', $force_reauth = false ) {
157
  global $pmpro_pages;
158
-
159
  if ( empty( $pmpro_pages['login'] ) ) {
160
  // skip everything, including filter below
161
  return wp_login_url( $redirect, $force_reauth );
162
  }
163
-
164
  $login_url = get_permalink( $pmpro_pages['login'] );
165
-
166
  if ( ! empty( $redirect ) ) {
167
  $login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url );
168
  }
169
-
170
  if ( $force_reauth ) {
171
  $login_url = add_query_arg( 'reauth', '1', $login_url );
172
  }
173
-
174
  /**
175
  * Filters the login URL.
176
  *
@@ -192,19 +209,19 @@ function pmpro_login_url( $redirect = '', $force_reauth = false ) {
192
  */
193
  function pmpro_lostpassword_url( $redirect = '' ) {
194
  global $pmpro_pages;
195
-
196
  if ( empty( $pmpro_pages['login'] ) ) {
197
  // skip everything, including filter below
198
  return wp_lostpassword_url( $redirect );
199
  }
200
-
201
  $args = array( 'action' => 'lostpassword' );
202
  if ( ! empty( $redirect ) ) {
203
- $args['redirect_to'] = urlencode( $redirect );
204
  }
205
-
206
  $lostpassword_url = add_query_arg( $args, get_permalink( $pmpro_pages['login'] ) );
207
-
208
  /**
209
  * Filters the Lost Password URL.
210
  *
@@ -270,7 +287,7 @@ function pmpro_login_document_title_parts( $titleparts ) {
270
  if ( empty( $pmpro_pages ) || empty ( $pmpro_pages['login'] ) || ! is_page( $pmpro_pages['login'] ) ) {
271
  return $titleparts;
272
  }
273
-
274
  if ( is_user_logged_in() ) {
275
  $titleparts['title'] = __( 'Welcome', 'paid-memberships-pro' );
276
  } elseif ( ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] === 'reset_pass' ) {
@@ -293,7 +310,7 @@ function pmpro_login_forms_handler( $show_menu = true, $show_logout_link = true,
293
  if ( $location === 'widget' && pmpro_is_login_page() ) {
294
  return '';
295
  }
296
-
297
  // Set the message return string.
298
  $message = '';
299
  $msgt = 'pmpro_alert';
@@ -322,6 +339,12 @@ function pmpro_login_forms_handler( $show_menu = true, $show_logout_link = true,
322
  case 'recovered':
323
  $message = __( 'Check your email for the confirmation link.', 'paid-memberships-pro' );
324
  break;
 
 
 
 
 
 
325
  }
326
  }
327
 
@@ -371,7 +394,7 @@ function pmpro_login_forms_handler( $show_menu = true, $show_logout_link = true,
371
  }
372
 
373
  if ( isset( $_GET['password'] ) ) {
374
- switch( sanitize_text_field( $_GET['password'] ) ) {
375
  case 'changed':
376
  $message = __( 'Your password has successfully been updated.', 'paid-memberships-pro' );
377
  $msgt = 'pmpro_success';
@@ -421,7 +444,7 @@ function pmpro_login_forms_handler( $show_menu = true, $show_logout_link = true,
421
 
422
  // Note we don't show messages on the widget form.
423
  if ( $message && $location !== 'widget' ) {
424
- echo '<div class="' . pmpro_get_element_class( 'pmpro_message ' . $msgt, esc_attr( $msgt ) ) . '">'. esc_html( $message ) .'</div>';
425
  }
426
 
427
  // Get the form title HTML tag.
@@ -440,44 +463,44 @@ function pmpro_login_forms_handler( $show_menu = true, $show_logout_link = true,
440
  }
441
 
442
  // Figure out which login view to show.
443
- if ( ! is_user_logged_in() ) {
444
  if ( ! in_array( $action, array( 'reset_pass', 'rp' ) ) ) {
445
  // Login form.
446
  if ( empty( $_GET['login'] ) || empty( $_GET['key'] ) ) {
447
  $username = isset( $_REQUEST['username'] ) ? sanitize_text_field( $_REQUEST['username'] ) : NULL;
448
  $redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url( $_REQUEST['redirect_to'] ) : NULL;
449
-
450
- // Redirect users back to their page that they logged-in from via the widget.
451
- if( empty( $redirect_to ) && $location === 'widget' && apply_filters( 'pmpro_login_widget_redirect_back', true ) ) {
452
  $redirect_to = esc_url( site_url( $_SERVER['REQUEST_URI'] ) );
453
  }
454
  ?>
455
  <div class="<?php echo pmpro_get_element_class( 'pmpro_login_wrap' ); ?>">
456
- <?php
457
  if ( ! pmpro_is_login_page() ) {
458
- echo $before_title . esc_html( 'Log In', 'paid-memberships-pro' ) . $after_title;
459
  }
460
  ?>
461
  <?php
462
  pmpro_login_form( array( 'value_username' => esc_html( $username ), 'redirect' => esc_url( $redirect_to ) ) );
463
  pmpro_login_forms_handler_nav( 'login' );
464
  ?>
465
- </div> <!-- end pmpro_login_wrap -->
466
  <?php if ( pmpro_is_login_page() ) { ?>
467
  <script>
468
  document.getElementById('user_login').focus();
469
  </script>
470
  <?php } ?>
471
-
472
  <?php
473
  }
474
  } elseif ( $location !== 'widget' && ( $action === 'reset_pass' || ( $action === 'rp' && in_array( $_REQUEST['login'], array( 'invalidkey', 'expiredkey' ) ) ) ) ) {
475
- // Reset password form.
476
  ?>
477
  <div class="<?php echo pmpro_get_element_class( 'pmpro_lost_password_wrap' ); ?>">
478
- <?php
479
  if ( ! pmpro_is_login_page() ) {
480
- echo $before_title . esc_html( 'Password Reset', 'paid-memberships-pro' ) . $after_title;
481
  }
482
  ?>
483
  <p class="<?php echo pmpro_get_element_class( 'pmpro_lost_password-instructions' ); ?>">
@@ -492,12 +515,12 @@ function pmpro_login_forms_handler( $show_menu = true, $show_logout_link = true,
492
  </div> <!-- end pmpro_lost_password_wrap -->
493
  <?php
494
  } elseif ( $location !== 'widget' && $action === 'rp' ) {
495
- // Password reset processing key.
496
  ?>
497
  <div class="<?php echo pmpro_get_element_class( 'pmpro_reset_password_wrap' ); ?>">
498
- <?php
499
  if ( ! pmpro_is_login_page() ) {
500
- echo $before_title . esc_html( 'Reset Password', 'paid-memberships-pro' ) . $after_title;
501
  }
502
  ?>
503
  <?php pmpro_reset_password_form(); ?>
@@ -515,12 +538,12 @@ function pmpro_login_forms_handler( $show_menu = true, $show_logout_link = true,
515
  <?php
516
  }
517
  }
518
-
519
  $content = ob_get_clean();
520
  if ( $echo ) {
521
  echo $content;
522
  }
523
-
524
  return $content;
525
  }
526
 
@@ -560,11 +583,11 @@ function pmpro_lost_password_form() { ?>
560
  function pmpro_lost_password_redirect() {
561
  if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) {
562
  $login_page = pmpro_getOption( 'login_page_id' );
563
-
564
  if ( empty( $login_page ) ) {
565
  return;
566
  }
567
-
568
  $redirect_url = $login_page ? get_permalink( $login_page ): '';
569
 
570
  $errors = retrieve_password();
@@ -584,17 +607,17 @@ add_action( 'login_form_lostpassword', 'pmpro_lost_password_redirect' );
584
  * Redirect Password reset to our own page.
585
  * @since 2.3
586
  */
587
- function pmpro_reset_password_redirect() {
588
  if ( 'GET' == $_SERVER['REQUEST_METHOD'] ) {
589
  $login_page = pmpro_getOption( 'login_page_id' );
590
-
591
  if ( empty( $login_page ) ) {
592
  return;
593
  }
594
-
595
  $redirect_url = $login_page ? get_permalink( $login_page ): '';
596
  $user = check_password_reset_key( $_REQUEST['rp_key'], $_REQUEST['rp_login'] );
597
-
598
  if ( ! $user || is_wp_error( $user ) ) {
599
  if ( $user && $user->get_error_code() === 'expired_key' ) {
600
  wp_redirect( add_query_arg( 'login', urlencode( 'expiredkey' ), $redirect_url ) );
@@ -621,11 +644,42 @@ add_action( 'login_form_resetpass', 'pmpro_reset_password_redirect' );
621
  function pmpro_reset_password_form() {
622
  if ( isset( $_REQUEST['login'] ) && isset( $_REQUEST['key'] ) ) {
623
 
624
- // Error messages
625
- $errors = array();
626
- if ( isset( $_REQUEST['error'] ) ) {
627
- $error_codes = explode( ',', sanitize_text_field( $_REQUEST['error'] ) );
628
- } ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  <form name="resetpassform" id="resetpassform" class="<?php echo pmpro_get_element_class( 'pmpro_form', 'resetpassform' ); ?>" action="<?php echo esc_url( site_url( 'wp-login.php?action=resetpass' ) ); ?>" method="post" autocomplete="off">
630
  <input type="hidden" id="user_login" name="rp_login" value="<?php echo esc_attr( sanitize_text_field( $_REQUEST['login'] ) ); ?>" autocomplete="off" />
631
  <input type="hidden" name="rp_key" value="<?php echo esc_attr( sanitize_text_field( $_REQUEST['key'] ) ); ?>" />
@@ -701,14 +755,14 @@ function pmpro_login_forms_handler_nav( $pmpro_form ) { ?>
701
  function pmpro_do_password_reset() {
702
  if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) {
703
  $login_page = pmpro_getOption( 'login_page_id' );
704
-
705
  if ( empty( $login_page ) ) {
706
  return;
707
  }
708
-
709
  $rp_key = sanitize_text_field( $_REQUEST['rp_key'] );
710
- $rp_login = sanitize_text_field( $_REQUEST['rp_login'] );
711
-
712
  $redirect_url = $login_page ? get_permalink( $login_page ): '';
713
  $user = check_password_reset_key( $rp_key, $rp_login );
714
 
@@ -767,35 +821,22 @@ add_action( 'login_form_resetpass', 'pmpro_do_password_reset' );
767
  *
768
  * @since 2.3
769
  */
770
- function pmpro_password_reset_email_filter( $message, $key, $user_login, $user_data ) {
771
-
772
- $login_page_id = pmpro_getOption( 'login_page_id' );
773
- if ( ! empty ( $login_page_id ) ) {
774
- $login_url = get_permalink( $login_page_id );
775
- $message = str_replace( site_url( 'wp-login.php' ), $login_url, $message );
776
- }
777
-
778
- return $message;
779
- }
780
- add_filter( 'retrieve_password_message', 'pmpro_password_reset_email_filter', 10, 4 );
781
-
782
- /**
783
- * Replace the default login URL in the new user notification email
784
- * with the membership account page login URL instead.
785
- *
786
- * @since 2.3.4
787
- */
788
- function pmpro_new_user_notification_email_filter( $message, $user, $blogname ) {
789
 
790
  $login_page_id = pmpro_getOption( 'login_page_id' );
791
  if ( ! empty ( $login_page_id ) ) {
792
- $login_url = get_permalink( $login_page_id );
 
 
 
 
793
  $message = str_replace( network_site_url( 'wp-login.php' ), $login_url, $message );
794
  }
795
 
796
  return $message;
797
  }
798
- add_filter( 'wp_new_user_notification_email', 'pmpro_new_user_notification_email_filter', 10, 3 );
 
799
 
800
  /**
801
  * Authenticate the frontend user login.
@@ -846,7 +887,7 @@ add_filter( 'authenticate', 'pmpro_authenticate_username_password', 30, 3);
846
  */
847
  function pmpro_login_failed( $username ) {
848
 
849
- $login_page = pmpro_getOption( 'login_page_id' );
850
  if ( empty( $login_page ) ) {
851
  return;
852
  }
@@ -954,7 +995,7 @@ function pmpro_confirmaction_handler() {
954
  if ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] !== 'confirmaction' ) {
955
  return false;
956
  }
957
-
958
  if ( ! isset( $_GET['request_id'] ) ) {
959
  wp_die( __( 'Missing request ID.' ) );
960
  }
@@ -973,4 +1014,6 @@ function pmpro_confirmaction_handler() {
973
 
974
  /** This action is documented in wp-login.php */
975
  do_action( 'user_request_action_confirmed', $request_id );
976
- }
 
 
31
  // This filter is left in place for PMPro versions dating back to 2014.
32
  return apply_filters( 'pmpro_login_redirect_url', $redirect_to, $request, $user );
33
  }
34
+ add_filter( 'login_redirect','pmpro_login_redirect', 10, 3 );
35
 
36
  /**
37
  * Where is the sign up page? Levels page or default multisite page.
84
  * @since 1.7.14
85
  */
86
  function pmpro_redirect_to_logged_in() {
87
+ // Fixes Site Health loopback test.
88
+
89
+ if( ( pmpro_is_login_page() || is_page("login") )
90
+ && ! empty( $_REQUEST['redirect_to'] )
91
+ && is_user_logged_in()
92
+ && ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] == 'login' )
93
+ && empty( $_REQUEST['reauth']) ) {
94
+
95
+ wp_safe_redirect( esc_url_raw( $_REQUEST['redirect_to'] ) );
96
  exit;
97
  }
98
  }
99
+ add_action("template_redirect", "pmpro_redirect_to_logged_in", 15);
100
  add_action("login_init", "pmpro_redirect_to_logged_in", 5);
101
 
102
  /**
106
  * @since 2.3
107
  */
108
  function pmpro_login_url_filter( $login_url='', $redirect='' ) {
109
+ // Don't filter when specifically on wp-login.php.
110
+ if ( $_SERVER['SCRIPT_NAME'] === '/wp-login.php' ) {
111
+ return $login_url;
112
+ }
113
+
114
+ // Check for a PMPro Login page.
115
  $login_page_id = pmpro_getOption( 'login_page_id' );
116
  if ( ! empty ( $login_page_id ) ) {
117
+ $login_page_permalink = get_permalink( $login_page_id );
118
+ // If the page or permalink is unavailable, don't override the url here.
119
+ if ( $login_page_permalink ) {
120
+ $login_url = $login_page_permalink;
121
+ }
122
 
123
  if ( ! empty( $redirect ) ) {
124
  $login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url ) ;
136
  *
137
  */
138
  function pmpro_wp_loaded_login_setup() {
139
+ add_filter( 'login_url', 'pmpro_login_url_filter', 50, 2 );
140
  }
141
  add_action( 'wp_loaded', 'pmpro_wp_loaded_login_setup' );
142
 
147
  function pmpro_use_default_login_for_confirm_admin_email( $location ) {
148
  if ( strpos( $location, 'action=confirm_admin_email' ) !== false ) {
149
  $login_url = wp_login_url();
150
+
151
  remove_filter( 'login_url', 'pmpro_login_url_filter', 50, 2 );
152
  $default_login_url = wp_login_url();
153
  add_filter( 'login_url', 'pmpro_login_url_filter', 50, 2 );
154
+
155
  if ( $login_url != $default_login_url ) {
156
  $location = str_replace( $login_url, $default_login_url, $location );
157
  }
158
  }
159
+
160
  return $location;
161
  }
162
  add_filter( 'wp_redirect', 'pmpro_use_default_login_for_confirm_admin_email' );
172
  */
173
  function pmpro_login_url( $redirect = '', $force_reauth = false ) {
174
  global $pmpro_pages;
175
+
176
  if ( empty( $pmpro_pages['login'] ) ) {
177
  // skip everything, including filter below
178
  return wp_login_url( $redirect, $force_reauth );
179
  }
180
+
181
  $login_url = get_permalink( $pmpro_pages['login'] );
182
+
183
  if ( ! empty( $redirect ) ) {
184
  $login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url );
185
  }
186
+
187
  if ( $force_reauth ) {
188
  $login_url = add_query_arg( 'reauth', '1', $login_url );
189
  }
190
+
191
  /**
192
  * Filters the login URL.
193
  *
209
  */
210
  function pmpro_lostpassword_url( $redirect = '' ) {
211
  global $pmpro_pages;
212
+
213
  if ( empty( $pmpro_pages['login'] ) ) {
214
  // skip everything, including filter below
215
  return wp_lostpassword_url( $redirect );
216
  }
217
+
218
  $args = array( 'action' => 'lostpassword' );
219
  if ( ! empty( $redirect ) ) {
220
+ $args['redirect_to'] = urlencode( $redirect );
221
  }
222
+
223
  $lostpassword_url = add_query_arg( $args, get_permalink( $pmpro_pages['login'] ) );
224
+
225
  /**
226
  * Filters the Lost Password URL.
227
  *
287
  if ( empty( $pmpro_pages ) || empty ( $pmpro_pages['login'] ) || ! is_page( $pmpro_pages['login'] ) ) {
288
  return $titleparts;
289
  }
290
+
291
  if ( is_user_logged_in() ) {
292
  $titleparts['title'] = __( 'Welcome', 'paid-memberships-pro' );
293
  } elseif ( ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] === 'reset_pass' ) {
310
  if ( $location === 'widget' && pmpro_is_login_page() ) {
311
  return '';
312
  }
313
+
314
  // Set the message return string.
315
  $message = '';
316
  $msgt = 'pmpro_alert';
339
  case 'recovered':
340
  $message = __( 'Check your email for the confirmation link.', 'paid-memberships-pro' );
341
  break;
342
+ case 'confirmaction':
343
+ // Check if we are processing a confirmaction for a Data Request.
344
+ $request_id = pmpro_confirmaction_handler();
345
+ $message = _wp_privacy_account_request_confirmed_message( $request_id );
346
+ $msgt = 'pmpro_success';
347
+ break;
348
  }
349
  }
350
 
394
  }
395
 
396
  if ( isset( $_GET['password'] ) ) {
397
+ switch( $_GET['password'] ) {
398
  case 'changed':
399
  $message = __( 'Your password has successfully been updated.', 'paid-memberships-pro' );
400
  $msgt = 'pmpro_success';
444
 
445
  // Note we don't show messages on the widget form.
446
  if ( $message && $location !== 'widget' ) {
447
+ echo '<div class="' . pmpro_get_element_class( 'pmpro_message ' . $msgt, esc_attr( $msgt ) ) . '">'. wp_kses_post( $message ) .'</div>';
448
  }
449
 
450
  // Get the form title HTML tag.
463
  }
464
 
465
  // Figure out which login view to show.
466
+ if ( ! is_user_logged_in() ) {
467
  if ( ! in_array( $action, array( 'reset_pass', 'rp' ) ) ) {
468
  // Login form.
469
  if ( empty( $_GET['login'] ) || empty( $_GET['key'] ) ) {
470
  $username = isset( $_REQUEST['username'] ) ? sanitize_text_field( $_REQUEST['username'] ) : NULL;
471
  $redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url( $_REQUEST['redirect_to'] ) : NULL;
472
+
473
+ // Redirect users back to their page that they logged-in from via the widget.
474
+ if( empty( $redirect_to ) && $location === 'widget' && apply_filters( 'pmpro_login_widget_redirect_back', true ) ) {
475
  $redirect_to = esc_url( site_url( $_SERVER['REQUEST_URI'] ) );
476
  }
477
  ?>
478
  <div class="<?php echo pmpro_get_element_class( 'pmpro_login_wrap' ); ?>">
479
+ <?php
480
  if ( ! pmpro_is_login_page() ) {
481
+ echo $before_title . esc_html__( 'Log In', 'paid-memberships-pro' ) . $after_title;
482
  }
483
  ?>
484
  <?php
485
  pmpro_login_form( array( 'value_username' => esc_html( $username ), 'redirect' => esc_url( $redirect_to ) ) );
486
  pmpro_login_forms_handler_nav( 'login' );
487
  ?>
488
+ </div> <!-- end pmpro_login_wrap -->
489
  <?php if ( pmpro_is_login_page() ) { ?>
490
  <script>
491
  document.getElementById('user_login').focus();
492
  </script>
493
  <?php } ?>
494
+
495
  <?php
496
  }
497
  } elseif ( $location !== 'widget' && ( $action === 'reset_pass' || ( $action === 'rp' && in_array( $_REQUEST['login'], array( 'invalidkey', 'expiredkey' ) ) ) ) ) {
498
+ // Reset password form.
499
  ?>
500
  <div class="<?php echo pmpro_get_element_class( 'pmpro_lost_password_wrap' ); ?>">
501
+ <?php
502
  if ( ! pmpro_is_login_page() ) {
503
+ echo $before_title . esc_html__( 'Password Reset', 'paid-memberships-pro' ) . $after_title;
504
  }
505
  ?>
506
  <p class="<?php echo pmpro_get_element_class( 'pmpro_lost_password-instructions' ); ?>">
515
  </div> <!-- end pmpro_lost_password_wrap -->
516
  <?php
517
  } elseif ( $location !== 'widget' && $action === 'rp' ) {
518
+ // Password reset processing key.
519
  ?>
520
  <div class="<?php echo pmpro_get_element_class( 'pmpro_reset_password_wrap' ); ?>">
521
+ <?php
522
  if ( ! pmpro_is_login_page() ) {
523
+ echo $before_title . esc_html__( 'Reset Password', 'paid-memberships-pro' ) . $after_title;
524
  }
525
  ?>
526
  <?php pmpro_reset_password_form(); ?>
538
  <?php
539
  }
540
  }
541
+
542
  $content = ob_get_clean();
543
  if ( $echo ) {
544
  echo $content;
545
  }
546
+
547
  return $content;
548
  }
549
 
583
  function pmpro_lost_password_redirect() {
584
  if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) {
585
  $login_page = pmpro_getOption( 'login_page_id' );
586
+
587
  if ( empty( $login_page ) ) {
588
  return;
589
  }
590
+
591
  $redirect_url = $login_page ? get_permalink( $login_page ): '';
592
 
593
  $errors = retrieve_password();
607
  * Redirect Password reset to our own page.
608
  * @since 2.3
609
  */
610
+ function pmpro_reset_password_redirect() {
611
  if ( 'GET' == $_SERVER['REQUEST_METHOD'] ) {
612
  $login_page = pmpro_getOption( 'login_page_id' );
613
+
614
  if ( empty( $login_page ) ) {
615
  return;
616
  }
617
+
618
  $redirect_url = $login_page ? get_permalink( $login_page ): '';
619
  $user = check_password_reset_key( $_REQUEST['rp_key'], $_REQUEST['rp_login'] );
620
+
621
  if ( ! $user || is_wp_error( $user ) ) {
622
  if ( $user && $user->get_error_code() === 'expired_key' ) {
623
  wp_redirect( add_query_arg( 'login', urlencode( 'expiredkey' ), $redirect_url ) );
644
  function pmpro_reset_password_form() {
645
  if ( isset( $_REQUEST['login'] ) && isset( $_REQUEST['key'] ) ) {
646
 
647
+ // Check if reset key is valid.
648
+ $user = check_password_reset_key( $_REQUEST['key'], $_REQUEST['login'] );
649
+ $errors = new WP_Error();
650
+ if ( ! $user || is_wp_error( $user ) ) {
651
+ if ( $user && $user->get_error_code() === 'invalid_key' ) {
652
+ $errors->add( 'invalidkey', __( 'Your password reset link appears to be invalid. Please request a new link below.', 'paid-membeships-pro' ) );
653
+ } elseif ( $user && $user->get_error_code() === 'expired_key' ) {
654
+ $errors->add( 'expiredkey', __( 'Your password reset link has expired. Please request a new link below.', 'paid-membeships-pro' ) );
655
+ }
656
+ }
657
+
658
+ // Grabbing errors from $_GET like wp-login.php does.
659
+ if ( isset( $_GET['error'] ) ) {
660
+ if ( 'invalidkey' === $_GET['error'] ) {
661
+ $errors->add( 'invalidkey', __( 'Your password reset link appears to be invalid. Please request a new link below.', 'paid-membeships-pro' ) );
662
+ } elseif ( 'expiredkey' === $_GET['error'] ) {
663
+ $errors->add( 'expiredkey', __( 'Your password reset link has expired. Please request a new link below.', 'paid-membeships-pro' ) );
664
+ }
665
+ }
666
+
667
+ if ( ! empty( $errors ) && $errors->has_errors() ) {
668
+ // Combine errors into one message.
669
+ $message = '';
670
+ foreach ( $errors->get_error_codes() as $code ) {
671
+ foreach ( $errors->get_error_messages( $code ) as $error_message ) {
672
+ $message .= ' ' . $error_message . ' ';
673
+ }
674
+ }
675
+
676
+ $msgt = 'pmpro_error';
677
+ echo '<div class="' . pmpro_get_element_class( 'pmpro_message ' . $msgt, esc_attr( $msgt ) ) . '">'. esc_html( $message ) .'</div>';
678
+ echo pmpro_lost_password_form();
679
+ return;
680
+ }
681
+
682
+ ?>
683
  <form name="resetpassform" id="resetpassform" class="<?php echo pmpro_get_element_class( 'pmpro_form', 'resetpassform' ); ?>" action="<?php echo esc_url( site_url( 'wp-login.php?action=resetpass' ) ); ?>" method="post" autocomplete="off">
684
  <input type="hidden" id="user_login" name="rp_login" value="<?php echo esc_attr( sanitize_text_field( $_REQUEST['login'] ) ); ?>" autocomplete="off" />
685
  <input type="hidden" name="rp_key" value="<?php echo esc_attr( sanitize_text_field( $_REQUEST['key'] ) ); ?>" />
755
  function pmpro_do_password_reset() {
756
  if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) {
757
  $login_page = pmpro_getOption( 'login_page_id' );
758
+
759
  if ( empty( $login_page ) ) {
760
  return;
761
  }
762
+
763
  $rp_key = sanitize_text_field( $_REQUEST['rp_key'] );
764
+ $rp_login = sanitize_text_field( $_REQUEST['rp_login'] );
765
+
766
  $redirect_url = $login_page ? get_permalink( $login_page ): '';
767
  $user = check_password_reset_key( $rp_key, $rp_login );
768
 
821
  *
822
  * @since 2.3
823
  */
824
+ function pmpro_password_reset_email_filter( $message, $key, $user_login ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
825
 
826
  $login_page_id = pmpro_getOption( 'login_page_id' );
827
  if ( ! empty ( $login_page_id ) ) {
828
+ $login_url = get_permalink( $login_page_id );
829
+ if ( strpos( $login_url, '?' ) ) {
830
+ // Login page permalink contains a '?', so we need to replace the '?' already in the login URL with '&'.
831
+ $message = str_replace( network_site_url( 'wp-login.php' ) . '?', $login_url . '&', $message );
832
+ }
833
  $message = str_replace( network_site_url( 'wp-login.php' ), $login_url, $message );
834
  }
835
 
836
  return $message;
837
  }
838
+ add_filter( 'retrieve_password_message', 'pmpro_password_reset_email_filter', 20, 3 );
839
+ add_filter( 'wp_new_user_notification_email', 'pmpro_password_reset_email_filter', 10, 3 );
840
 
841
  /**
842
  * Authenticate the frontend user login.
887
  */
888
  function pmpro_login_failed( $username ) {
889
 
890
+ $login_page = pmpro_getOption( 'login_page_id' );
891
  if ( empty( $login_page ) ) {
892
  return;
893
  }
995
  if ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] !== 'confirmaction' ) {
996
  return false;
997
  }
998
+
999
  if ( ! isset( $_GET['request_id'] ) ) {
1000
  wp_die( __( 'Missing request ID.' ) );
1001
  }
1014
 
1015
  /** This action is documented in wp-login.php */
1016
  do_action( 'user_request_action_confirmed', $request_id );
1017
+
1018
+ return $request_id;
1019
+ }
includes/metaboxes.php CHANGED
@@ -5,9 +5,28 @@
5
  function pmpro_page_meta() {
6
  global $post, $wpdb;
7
  $membership_levels = pmpro_getAllLevels( true, true );
 
8
  $page_levels = $wpdb->get_col( "SELECT membership_id FROM {$wpdb->pmpro_memberships_pages} WHERE page_id = '" . intval( $post->ID ) . "'" );
9
- ?>
10
- <ul id="membershipschecklist" class="list:category categorychecklist form-no-clear">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  <input type="hidden" name="pmpro_noncename" id="pmpro_noncename" value="<?php echo esc_attr( wp_create_nonce( plugin_basename(__FILE__) ) )?>" />
12
  <?php
13
  $in_member_cat = false;
@@ -37,7 +56,8 @@ function pmpro_page_meta() {
37
  <p class="pmpro_meta_notice">* <?php _e("This post is already protected for this level because it is within a category that requires membership.", 'paid-memberships-pro' );?></p>
38
  <?php
39
  }
40
-
 
41
  do_action( 'pmpro_after_require_membership_metabox', $post );
42
  ?>
43
  <?php
5
  function pmpro_page_meta() {
6
  global $post, $wpdb;
7
  $membership_levels = pmpro_getAllLevels( true, true );
8
+ $membership_levels = pmpro_sort_levels_by_order( $membership_levels );
9
  $page_levels = $wpdb->get_col( "SELECT membership_id FROM {$wpdb->pmpro_memberships_pages} WHERE page_id = '" . intval( $post->ID ) . "'" );
10
+
11
+ // Build the selectors for the #memberships list based on level count.
12
+ $pmpro_memberships_checklist_classes = array( 'list:category', 'categorychecklist', 'form-no-clear');
13
+ if ( count( $membership_levels ) > 9 ) {
14
+ $pmpro_memberships_checklist_classes[] = "pmpro_scrollable";
15
+ }
16
+ $pmpro_memberships_checklist_classes = implode( ' ', array_unique( $pmpro_memberships_checklist_classes ) );
17
+
18
+ if ( count( $membership_levels ) > 1 ) { ?>
19
+ <p><?php esc_html_e( 'Select:', 'paid-memberships-pro' ); ?> <a id="pmpro-memberships-checklist-select-all" href="javascript:void(0);"><?php esc_html_e( 'All', 'paid-memberships-pro' ); ?></a> | <a id="pmpro-memberships-checklist-select-none" href="javascript:void(0);"><?php esc_html_e( 'None', 'paid-memberships-pro' ); ?></a></p>
20
+ <script type="text/javascript">
21
+ jQuery('#pmpro-memberships-checklist-select-all').click(function(){
22
+ jQuery('#pmpro-memberships-checklist input').prop('checked', true);
23
+ });
24
+ jQuery('#pmpro-memberships-checklist-select-none').click(function(){
25
+ jQuery('#pmpro-memberships-checklist input').prop('checked', false);
26
+ });
27
+ </script>
28
+ <?php } ?>
29
+ <ul id="pmpro-memberships-checklist" class="<?php echo esc_attr( $pmpro_memberships_checklist_classes ); ?>">
30
  <input type="hidden" name="pmpro_noncename" id="pmpro_noncename" value="<?php echo esc_attr( wp_create_nonce( plugin_basename(__FILE__) ) )?>" />
31
  <?php
32
  $in_member_cat = false;
56
  <p class="pmpro_meta_notice">* <?php _e("This post is already protected for this level because it is within a category that requires membership.", 'paid-memberships-pro' );?></p>
57
  <?php
58
  }
59
+ ?>
60
+ <?php
61
  do_action( 'pmpro_after_require_membership_metabox', $post );
62
  ?>
63
  <?php
includes/notifications.php CHANGED
@@ -44,6 +44,7 @@ function pmpro_notifications() {
44
  'class' => array(),
45
  ),
46
  'br' => array(),
 
47
  );
48
  echo wp_kses( $notification->content, $allowed_html );
49
  ?>
@@ -434,14 +435,17 @@ function pmpro_notification_test_pmpro_setting( $data ) {
434
 
435
  /**
436
  * PMPro site URL test.
437
- * @param string $string String to look for in the site URL
438
  * @returns bool true if the string shows up in the site URL
439
  */
440
- function pmpro_notification_test_site_url_match( $string ) {
441
  if ( ! empty( $string ) ) {
442
- if ( strpos( get_bloginfo( 'url' ), $string ) !== false ) {
443
- return true;
444
- }
 
 
 
445
  }
446
  return false;
447
  }
44
  'class' => array(),
45
  ),
46
  'br' => array(),
47
+ 'strike' => array(),
48
  );
49
  echo wp_kses( $notification->content, $allowed_html );
50
  ?>
435
 
436
  /**
437
  * PMPro site URL test.
438
+ * @param string $string String or array of strings to look for in the site URL
439
  * @returns bool true if the string shows up in the site URL
440
  */
441
+ function pmpro_notification_test_site_url_match( $string ) {
442
  if ( ! empty( $string ) ) {
443
+ $strings_to_check = (array) $string;
444
+ foreach( $strings_to_check as $check ) {
445
+ if ( strpos( get_bloginfo( 'url' ), $check ) !== false ) {
446
+ return true;
447
+ }
448
+ }
449
  }
450
  return false;
451
  }
includes/profile.php CHANGED
@@ -40,31 +40,7 @@ function pmpro_membership_level_profile_fields($user)
40
  <?php
41
  }
42
  ?>
43
- </select>
44
- <span id="current_level_cost">
45
- <?php
46
- $membership_values = pmpro_getMembershipLevelForUser($user->ID);
47
-
48
- //we tweak the initial payment here so the text here effectively shows the recurring amount
49
- if(!empty($membership_values))
50
- {
51
- $membership_values->original_initial_payment = $membership_values->initial_payment;
52
- $membership_values->initial_payment = $membership_values->billing_amount;
53
- }
54
-
55
- if(empty($membership_values) || pmpro_isLevelFree($membership_values))
56
- {
57
- if(!empty($membership_values->original_initial_payment) && $membership_values->original_initial_payment > 0)
58
- echo __('Paid', 'paid-memberships-pro' ) . pmpro_formatPrice($membership_values->original_initial_payment) . ".";
59
- else
60
- _e('Not paying.', 'paid-memberships-pro' );
61
- }
62
- else
63
- {
64
- echo pmpro_getLevelCost($membership_values, true, true);
65
- }
66
- ?>
67
- </span>
68
  <p id="cancel_description" class="description hidden"><?php _e("This will not change the subscription at the gateway unless the 'Cancel' checkbox is selected below.", 'paid-memberships-pro' ); ?></p>
69
  </td>
70
  </tr>
@@ -81,6 +57,9 @@ function pmpro_membership_level_profile_fields($user)
81
  $selected_expires_day = date( 'j', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
82
  $selected_expires_month = date( 'm', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
83
  $selected_expires_year = date( 'Y', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
 
 
 
84
  ?>
85
  <tr>
86
  <th><label for="expiration"><?php _e("Expires", 'paid-memberships-pro' ); ?></label></th>
@@ -103,6 +82,21 @@ function pmpro_membership_level_profile_fields($user)
103
  </select>
104
  <input name="expires_day" type="text" size="2" value="<?php echo $selected_expires_day?>" />
105
  <input name="expires_year" type="text" size="4" value="<?php echo $selected_expires_year?>" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  </span>
107
  <script>
108
  jQuery('#expires').change(function() {
@@ -140,17 +134,19 @@ function pmpro_membership_level_profile_fields($user)
140
  <th><label for="tos_consent_history"><?php _e("TOS Consent History", 'paid-memberships-pro' ); ?></label></th>
141
  <td id="tos_consent_history">
142
  <?php
143
- if( !empty( $consent_log ) ) {
144
- if( count( $consent_log ) > 10 ) {
145
- $scrollable = 'pmpro_scrollable';
146
- } else {
147
- $scrollable = '';
 
148
  }
149
- echo '<ul class="pmpro_consent_log ' . $scrollable . '">';
 
150
  foreach( $consent_log as $entry ) {
151
  echo '<li>' . pmpro_consent_to_text( $entry ) . '</li>';
152
  }
153
- echo '</ul>';
154
  } else {
155
  echo __( 'N/A', 'paid-memberships-pro' );
156
  }
@@ -313,6 +309,14 @@ function pmpro_membership_level_profile_fields_update()
313
  {
314
  //update the expiration date
315
  $expiration_date = intval($_REQUEST['expires_year']) . "-" . str_pad(intval($_REQUEST['expires_month']), 2, "0", STR_PAD_LEFT) . "-" . str_pad(intval($_REQUEST['expires_day']), 2, "0", STR_PAD_LEFT);
 
 
 
 
 
 
 
 
316
  $sqlQuery = "UPDATE $wpdb->pmpro_memberships_users SET enddate = '" . $expiration_date . "' WHERE status = 'active' AND membership_id = '" . intval($_REQUEST['membership_level']) . "' AND user_id = '" . $user_ID . "' LIMIT 1";
317
  if($wpdb->query($sqlQuery))
318
  $expiration_changed = true;
@@ -357,6 +361,250 @@ add_action( 'edit_user_profile', 'pmpro_membership_level_profile_fields' );
357
  add_action( 'personal_options_update', 'pmpro_membership_level_profile_fields_update' );
358
  add_action( 'edit_user_profile_update', 'pmpro_membership_level_profile_fields_update' );
359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  /**
361
  * Sanitizes the passed value.
362
  *
@@ -405,13 +653,12 @@ function pmpro_member_profile_edit_form() {
405
  return;
406
  }
407
 
408
- do_action( 'pmpro_personal_options_update', $current_user->ID );
409
-
410
  // Saving profile updates.
411
  if ( isset( $_POST['action'] ) && $_POST['action'] == 'update-profile' && $current_user->ID == $_POST['user_id'] && wp_verify_nonce( $_POST['update_user_nonce'], 'update-user_' . $current_user->ID ) ) {
412
  $update = true;
413
  $user = new stdClass;
414
  $user->ID = $_POST[ 'user_id' ];
 
415
  } else {
416
  $update = false;
417
  }
40
  <?php
41
  }
42
  ?>
43
+ </select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  <p id="cancel_description" class="description hidden"><?php _e("This will not change the subscription at the gateway unless the 'Cancel' checkbox is selected below.", 'paid-memberships-pro' ); ?></p>
45
  </td>
46
  </tr>
57
  $selected_expires_day = date( 'j', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
58
  $selected_expires_month = date( 'm', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
59
  $selected_expires_year = date( 'Y', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
60
+ $selected_expires_hour = date( 'H', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
61
+
62
+ $selected_expires_minute = date( 'i', $end_date ? $user->membership_level->enddate : current_time('timestamp') );
63
  ?>
64
  <tr>
65
  <th><label for="expiration"><?php _e("Expires", 'paid-memberships-pro' ); ?></label></th>
82
  </select>
83
  <input name="expires_day" type="text" size="2" value="<?php echo $selected_expires_day?>" />
84
  <input name="expires_year" type="text" size="4" value="<?php echo $selected_expires_year?>" />
85
+ <?php _e('at', 'paid-memberships-pro'); ?>
86
+ <select name='expires_hour'>
87
+ <?php
88
+ for( $i = 0; $i <= 24; $i++ ){
89
+ echo "<option value='".$i."' ".selected( $selected_expires_hour, sprintf("%02d", $i ), true ).">".sprintf("%02d", $i )."</option>";
90
+ }
91
+ ?>
92
+ </select>
93
+ <select name='expires_minute'>
94
+ <?php
95
+ for( $i = 0; $i <= 59; $i++ ){
96
+ echo "<option value='".$i."' ".selected( $selected_expires_minute, sprintf("%02d", $i ), true ).">".sprintf("%02d", $i )."</option>";
97
+ }
98
+ ?>
99
+ </select>
100
  </span>
101
  <script>
102
  jQuery('#expires').change(function() {
134
  <th><label for="tos_consent_history"><?php _e("TOS Consent History", 'paid-memberships-pro' ); ?></label></th>
135
  <td id="tos_consent_history">
136
  <?php
137
+ if ( ! empty( $consent_log ) ) {
138
+ // Build the selectors for the invoices history list based on history count.
139
+ $consent_log_classes = array();
140
+ $consent_log_classes[] = "pmpro_consent_log";
141
+ if ( count( $consent_log ) > 5 ) {
142
+ $consent_log_classes[] = "pmpro_scrollable";
143
  }
144
+ $consent_log_class = implode( ' ', array_unique( $consent_log_classes ) );
145
+ echo '<ul class="' . esc_attr( $consent_log_class ) . '">';
146
  foreach( $consent_log as $entry ) {
147
  echo '<li>' . pmpro_consent_to_text( $entry ) . '</li>';
148
  }
149
+ echo '</ul> <!-- end pmpro_consent_log -->';
150
  } else {
151
  echo __( 'N/A', 'paid-memberships-pro' );
152
  }
309
  {
310
  //update the expiration date
311
  $expiration_date = intval($_REQUEST['expires_year']) . "-" . str_pad(intval($_REQUEST['expires_month']), 2, "0", STR_PAD_LEFT) . "-" . str_pad(intval($_REQUEST['expires_day']), 2, "0", STR_PAD_LEFT);
312
+ if( !empty( $_REQUEST['expires_hour'] ) ){
313
+ if( !empty( $_REQUEST['expires_minute'] ) ){
314
+ $expiration_date = $expiration_date ." ".$_REQUEST['expires_hour'].":".$_REQUEST['expires_minute'].":00";
315
+ } else{
316
+ $expiration_date = $expiration_date ." ".$_REQUEST['expires_hour'].":00:00";
317
+ }
318
+ }
319
+
320
  $sqlQuery = "UPDATE $wpdb->pmpro_memberships_users SET enddate = '" . $expiration_date . "' WHERE status = 'active' AND membership_id = '" . intval($_REQUEST['membership_level']) . "' AND user_id = '" . $user_ID . "' LIMIT 1";
321
  if($wpdb->query($sqlQuery))
322
  $expiration_changed = true;
361
  add_action( 'personal_options_update', 'pmpro_membership_level_profile_fields_update' );
362
  add_action( 'edit_user_profile_update', 'pmpro_membership_level_profile_fields_update' );
363
 
364
+
365
+ /**
366
+ * Add the history view to the user profile
367
+ *
368
+ */
369
+ function pmpro_membership_history_profile_fields( $user ) {
370
+ global $current_user;
371
+ $membership_level_capability = apply_filters( 'pmpro_edit_member_capability', 'manage_options' );
372
+
373
+ if ( ! current_user_can( $membership_level_capability ) ) {
374
+ return false;
375
+ }
376
+
377
+ global $wpdb;
378
+
379
+ //Show all invoices for user
380
+ $invoices = $wpdb->get_results("SELECT mo.*, UNIX_TIMESTAMP(mo.timestamp) as timestamp, du.code_id as code_id FROM $wpdb->pmpro_membership_orders mo LEFT JOIN $wpdb->pmpro_discount_codes_uses du ON mo.id = du.order_id WHERE mo.user_id = '$user->ID' ORDER BY mo.timestamp DESC");
381
+
382
+ $levelshistory = $wpdb->get_results("SELECT * FROM $wpdb->pmpro_memberships_users WHERE user_id = '$user->ID' ORDER BY id DESC");
383
+
384
+ $totalvalue = $wpdb->get_var("SELECT SUM(total) FROM $wpdb->pmpro_membership_orders WHERE user_id = '$user->ID' AND status NOT IN('token','review','pending','error','refunded')");
385
+
386
+ if ( $invoices || $levelshistory ) { ?>
387
+ <hr />
388
+ <h3><?php esc_html_e( 'Member History', 'paid-memberships-pro' ); ?></h3>
389
+ <p><strong><?php esc_html_e( 'Total Paid', 'paid-memberships-pro' ); ?></strong> <?php echo pmpro_formatPrice( $totalvalue ); ?></p>
390
+ <ul id="member-history-filters" class="subsubsub">
391
+ <li id="member-history-filters-orders"><a href="javascript:void(0);" class="current orders tab"><?php esc_html_e( 'Order History', 'paid-memberships-pro' ); ?></a> <span>(<?php echo count( $invoices ); ?>)</span></li>
392
+ <li id="member-history-filters-memberships">| <a href="javascript:void(0);" class="tab"><?php esc_html_e( 'Membership Levels History', 'paid-memberships-pro' ); ?></a> <span>(<?php echo count( $levelshistory ); ?>)</span></li>
393
+ </ul>
394
+ <br class="clear" />
395
+ <?php
396
+ // Build the selectors for the invoices history list based on history count.
397
+ $invoices_classes = array();
398
+ $invoices_classes[] = "widgets-holder-wrap";
399
+ if ( ! empty( $invoices ) && count( $invoices ) > 2 ) {
400
+ $invoices_classes[] = "pmpro_scrollable";
401
+ }
402
+ $invoice_class = implode( ' ', array_unique( $invoices_classes ) );
403
+ ?>
404
+ <div id="member-history-orders" class="<?php echo esc_attr( $invoice_class ); ?>">
405
+ <?php if ( $invoices ) { ?>
406
+ <table class="wp-list-table widefat striped fixed" width="100%" cellpadding="0" cellspacing="0" border="0">
407
+ <thead>
408
+ <tr>
409
+ <th><?php esc_html_e( 'Date', 'paid-memberships-pro' ); ?></th>
410
+ <th><?php esc_html_e( 'Invoice ID', 'paid-memberships-pro' ); ?></th>
411
+ <th><?php esc_html_e( 'Level', 'paid-memberships-pro' ); ?></th>
412
+ <th><?php esc_html_e( 'Level ID', 'paid-memberships-pro' ); ?>
413
+ <th><?php esc_html_e( 'Total Billed', 'paid-memberships-pro' ); ?></th>
414
+ <th><?php esc_html_e( 'Discount Code', 'paid-memberships-pro' ); ?></th>
415
+ <th><?php esc_html_e( 'Status', 'paid-memberships-pro' ); ?></th>
416
+ <?php do_action('pmpromh_orders_extra_cols_header');?>
417
+ </tr>
418
+ </thead>
419
+ <tbody>
420
+ <?php
421
+ foreach ( $invoices as $invoice ) {
422
+ $level = pmpro_getLevel( $invoice->membership_id );
423
+ ?>
424
+ <tr>
425
+ <td><?php echo date_i18n( get_option( 'date_format'), $invoice->timestamp ); ?></td>
426
+ <td class="order_code column-order_code has-row-actions">
427
+ <a href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'order' => $invoice->id ), admin_url('admin.php' ) ); ?>"><?php echo $invoice->code; ?></a><br />
428
+ <div class="row-actions">
429
+ <span class="edit">
430
+ <a title="<?php esc_html_e( 'Edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'order' => $invoice->id ), admin_url('admin.php' ) ); ?>"><?php esc_html_e( 'Edit', 'paid-memberships-pro' ); ?></a>
431
+ </span> |
432
+ <span class="print">
433
+ <a target="_blank" title="<?php _e( 'Print', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'action' => 'pmpro_orders_print_view', 'order' => $invoice->id ), admin_url('admin-ajax.php' ) ); ?>"><?php esc_html_e( 'Print', 'paid-memberships-pro' ); ?></a>
434
+ </span>
435
+ <?php if ( function_exists( 'pmpro_add_email_order_modal' ) ) { ?>
436
+ |
437
+ <span class="email">
438
+ <a title="<?php esc_html_e( 'Email', 'paid-memberships-pro' ); ?>" href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link" data-order="<?php echo esc_attr( $invoice->id ); ?>"><?php _e( 'Email', 'paid-memberships-pro' ); ?></a>
439
+ </span>
440
+ <?php } ?>
441
+ </div> <!-- end .row-actions -->
442
+ </td>
443
+ <td><?php if ( ! empty( $level ) ) { echo $level->name; } else { _e( 'N/A', 'paid-memberships-pro'); } ?></td>
444
+ <td><?php if ( ! empty( $level ) ) { echo $level->id; } else { _e( 'N/A', 'paid-memberships-pro'); } ?></td>
445
+ <td><?php echo pmpro_formatPrice( $invoice->total ); ?></td>
446
+ <td><?php
447
+ if ( empty( $invoice->code_id ) ) {
448
+ echo '-';
449
+ } else {
450
+ $discountQuery = "SELECT c.code FROM $wpdb->pmpro_discount_codes c WHERE c.id = ".$invoice->code_id." LIMIT 1";
451
+ $discount_code = $wpdb->get_row( $discountQuery );
452
+ echo '<a href="admin.php?page=pmpro-discountcodes&edit='.$invoice->code_id.'">'. esc_attr( $discount_code->code ) . '</a>';
453
+ }
454
+ ?></td>
455
+ <td>
456
+ <?php
457
+ if ( empty( $invoice->status ) ) {
458
+ echo '-';
459
+ } else {
460
+ echo esc_html( $invoice->status );
461
+ }
462
+ ?>
463
+ </td>
464
+ <?php do_action( 'pmpromh_orders_extra_cols_body', $invoice ); ?>
465
+ </tr>
466
+ <?php
467
+ }
468
+ ?>
469
+ </tbody>
470
+ </table>
471
+ <?php } else { ?>
472
+ <table class="wp-list-table widefat striped fixed" width="100%" cellpadding="0" cellspacing="0" border="0">
473
+ <tbody>
474
+ <tr>
475
+ <td><?php esc_html_e( 'No membership orders found.', 'paid-memberships-pro' ); ?></td>
476
+ </tr>
477
+ </tbody>
478
+ </table>
479
+ <?php } ?>
480
+ </div> <!-- end #member-history-invoices -->
481
+ <?php
482
+ // Build the selectors for the membership levels history list based on history count.
483
+ $levelshistory_classes = array();
484
+ $levelshistory_classes[] = "widgets-holder-wrap";
485
+ if ( ! empty( $levelshistory ) && count( $levelshistory ) > 4 ) {
486
+ $levelshistory_classes[] = "pmpro_scrollable";
487
+ }
488
+ $levelshistory_class = implode( ' ', array_unique( $levelshistory_classes ) );
489
+ ?>
490
+ <div id="member-history-memberships" class="<?php echo esc_attr( $levelshistory_class ); ?>" style="display: none;">
491
+ <?php if ( $levelshistory ) { ?>
492
+ <table class="wp-list-table widefat striped fixed" width="100%" cellpadding="0" cellspacing="0" border="0">
493
+ <thead>
494
+ <tr>
495
+ <th><?php esc_html_e( 'Level ID', 'paid-memberships-pro' ); ?>
496
+ <th><?php esc_html_e( 'Level', 'paid-memberships-pro' ); ?></th>
497
+ <th><?php esc_html_e( 'Start Date', 'paid-memberships-pro' ); ?></th>
498
+ <th><?php esc_html_e( 'Date Modified', 'paid-memberships-pro' ); ?></th>
499
+ <th><?php esc_html_e( 'End Date', 'paid-memberships-pro' ); ?></th>
500
+ <th><?php esc_html_e( 'Level Cost', 'paid-memberships-pro' ); ?></th>
501
+ <th><?php esc_html_e( 'Status', 'paid-memberships-pro' ); ?></th>
502
+ <?php do_action( 'pmpromh_member_history_extra_cols_header' ); ?>
503
+ </tr>
504
+ </thead>
505
+ <tbody>
506
+ <?php
507
+ foreach ( $levelshistory as $levelhistory ) {
508
+ $level = pmpro_getLevel( $levelhistory->membership_id );
509
+
510
+ if ( $levelhistory->enddate === null || $levelhistory->enddate == '0000-00-00 00:00:00' ) {
511
+ $levelhistory->enddate = __( 'Never', 'paid-memberships-pro' );
512
+ } else {
513
+ $levelhistory->enddate = date_i18n( get_option( 'date_format'), strtotime( $levelhistory->enddate ) );
514
+ } ?>
515
+ <tr>
516
+ <td><?php if ( ! empty( $level ) ) { echo $level->id; } else { esc_html_e( 'N/A', 'paid-memberships-pro' ); } ?></td>
517
+ <td><?php if ( ! empty( $level ) ) { echo $level->name; } else { esc_html_e( 'N/A', 'paid-memberships-pro' ); } ?></td>
518
+ <td><?php echo ( $levelhistory->startdate === '0000-00-00 00:00:00' ? __('N/A', 'paid-memberships-pro') : date_i18n( get_option( 'date_format' ), strtotime( $levelhistory->startdate ) ) ); ?></td>
519
+ <td><?php echo date_i18n( get_option( 'date_format'), strtotime( $levelhistory->modified ) ); ?></td>
520
+ <td><?php echo esc_html( $levelhistory->enddate ); ?></td>
521
+ <td><?php echo pmpro_getLevelCost( $levelhistory, true, true ); ?></td>
522
+ <td>
523
+ <?php
524
+ if ( empty( $levelhistory->status ) ) {
525
+ echo '-';
526
+ } else {
527
+ echo esc_html( $levelhistory->status );
528
+ }
529
+ ?>
530
+ </td>
531
+ <?php do_action( 'pmpromh_member_history_extra_cols_body', $user, $level ); ?>
532
+ </tr>
533
+ <?php
534
+ }
535
+ ?>
536
+ </tbody>
537
+ </table>
538
+ <?php } else { ?>
539
+ <table class="wp-list-table widefat striped fixed" width="100%" cellpadding="0" cellspacing="0" border="0">
540
+ <tbody>
541
+ <tr>
542
+ <td><?php esc_html_e( 'No membership history found.', 'paid-memberships-pro'); ?></td>
543
+ </tr>
544
+ </tbody>
545
+ </table>
546
+ <?php } ?>
547
+ </div> <!-- end #member-history-memberships -->
548
+ <script>
549
+ //tabs
550
+ jQuery(document).ready(function() {
551
+ jQuery('#member-history-filters a.tab').click(function() {
552
+ //which tab?
553
+ var tab = jQuery(this).parent().attr('id').replace('member-history-filters-', '');
554
+
555
+ //un select tabs
556
+ jQuery('#member-history-filters a.tab').removeClass('current');
557
+
558
+ //select this tab
559
+ jQuery('#member-history-filters-'+tab+' a').addClass('current');
560
+
561
+ //show orders?
562
+ if(tab == 'orders')
563
+ {
564
+ jQuery('#member-history-memberships').hide();
565
+ jQuery('#member-history-orders').show();
566
+ }
567
+ else
568
+ {
569
+ jQuery('div#member-history-orders').hide();
570
+ jQuery('#member-history-memberships').show();
571
+
572
+ <?php if ( count( $levelshistory ) > 5 ) { ?>
573
+ jQuery('#member-history-memberships').css({'height': '150px', 'overflow': 'auto' });
574
+ <?php } ?>
575
+ }
576
+ });
577
+ });
578
+ </script>
579
+ <?php
580
+ }
581
+ }
582
+ add_action('edit_user_profile', 'pmpro_membership_history_profile_fields');
583
+ add_action('show_user_profile', 'pmpro_membership_history_profile_fields');
584
+
585
+
586
+ /**
587
+ * Allow orders to be emailed from the member history section on user profile.
588
+ *
589
+ */
590
+ function pmpro_membership_history_email_modal() {
591
+ $screen = get_current_screen();
592
+ if ( $screen->base == 'user-edit' || $screen->base == 'profile' ) {
593
+ // Require the core Paid Memberships Pro Admin Functions.
594
+ if ( defined( 'PMPRO_DIR' ) ) {
595
+ require_once( PMPRO_DIR . '/adminpages/functions.php' );
596
+ }
597
+
598
+ // Load the email order modal.
599
+ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
600
+ pmpro_add_email_order_modal();
601
+ }
602
+ }
603
+ }
604
+ add_action( 'in_admin_header', 'pmpro_membership_history_email_modal' );
605
+
606
+
607
+
608
  /**
609
  * Sanitizes the passed value.
610
  *
653
  return;
654
  }
655
 
 
 
656
  // Saving profile updates.
657
  if ( isset( $_POST['action'] ) && $_POST['action'] == 'update-profile' && $current_user->ID == $_POST['user_id'] && wp_verify_nonce( $_POST['update_user_nonce'], 'update-user_' . $current_user->ID ) ) {
658
  $update = true;
659
  $user = new stdClass;
660
  $user->ID = $_POST[ 'user_id' ];
661
+ do_action( 'pmpro_personal_options_update', $user->ID );
662
  } else {
663
  $update = false;
664
  }
includes/recaptcha.php CHANGED
@@ -5,14 +5,15 @@
5
  */
6
  function pmpro_init_recaptcha() {
7
  //don't load if setting is off
8
- global $recaptcha, $recaptcha_validated;
9
  $recaptcha = pmpro_getOption( 'recaptcha' );
10
  if ( empty( $recaptcha ) ) {
11
  return;
12
  }
13
 
14
- //don't load unless we're on the checkout page
15
- if ( ! pmpro_is_checkout() ) {
 
16
  return;
17
  }
18
 
@@ -28,91 +29,143 @@ function pmpro_init_recaptcha() {
28
 
29
  require_once(PMPRO_DIR . '/includes/lib/recaptchalib.php' );
30
 
31
- function pmpro_recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) {
 
32
 
33
- // Figure out language.
34
- $locale = get_locale();
35
- if(!empty($locale)) {
36
- $parts = explode("_", $locale);
37
- $lang = $parts[0];
38
- } else {
39
- $lang = "en";
40
- }
41
- $lang = apply_filters( 'pmpro_recaptcha_lang', $lang );
42
-
43
- // Check which version of ReCAPTCHA we are using.
44
- $recaptcha_version = pmpro_getOption( 'recaptcha_version' );
45
-
46
- if( $recaptcha_version == '3_invisible' ) { ?>
47
- <div class="g-recaptcha" data-sitekey="<?php echo $pubkey;?>" data-size="invisible" data-callback="onSubmit"></div>
48
- <script type="text/javascript">
49
- var pmpro_recaptcha_validated = false;
50
- var pmpro_recaptcha_onSubmit = function(token) {
51
- if ( pmpro_recaptcha_validated ) {
52
- jQuery('#pmpro_form').submit();
53
- return;
54
- } else {
55
- jQuery.ajax({
56
- url: '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>',
57
- type: 'GET',
58
- timeout: 30000,
59
- dataType: 'html',
60
- data: {
61
- 'action': 'pmpro_validate_recaptcha',
62
- 'g-recaptcha-response': token,
63
- },
64
- error: function(xml){
65
- alert('Error validating ReCAPTCHA.');
66
- },
67
- success: function(response){
68
- if ( response == '1' ) {
69
- pmpro_recaptcha_validated = true;
70
-
71
- //get a new token to be submitted with the form
72
- grecaptcha.execute();
73
- } else {
74
- pmpro_recaptcha_validated = false;
75
-
76
- //warn user validation failed
77
- alert( 'ReCAPTCHA validation failed. Try again.' );
78
-
79
- //get a new token to be submitted with the form
80
- grecaptcha.execute();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
- }
83
  });
84
- }
85
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- var pmpro_recaptcha_onloadCallback = function() {
88
- // Render on main submit button.
89
- grecaptcha.render('pmpro_btn-submit', {
90
- 'sitekey' : '<?php echo $pubkey;?>',
91
- 'callback' : pmpro_recaptcha_onSubmit
92
- });
 
 
 
 
 
 
 
 
93
 
94
- // Update other submit buttons.
95
- var submit_buttons = jQuery('.pmpro_btn-submit-checkout');
96
- submit_buttons.each(function() {
97
- if(jQuery(this).attr('id') != 'pmpro_btn-submit') {
98
- jQuery(this).click(function(event) {
99
  event.preventDefault();
100
- grecaptcha.execute();
101
- });
102
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  });
104
- };
105
- </script>
106
- <script type="text/javascript"
107
- src="https://www.google.com/recaptcha/api.js?onload=pmpro_recaptcha_onloadCallback&hl=<?php echo $lang;?>&render=explicit" async defer>
108
- </script>
109
- <?php } else { ?>
110
- <div class="g-recaptcha" data-sitekey="<?php echo $pubkey;?>"></div>
111
- <script type="text/javascript"
112
- src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang;?>">
113
- </script>
114
- <?php }
115
- }
116
 
117
  //for templates using the old recaptcha_get_html
118
  if( ! function_exists( 'recaptcha_get_html' ) ) {
@@ -125,7 +178,7 @@ function pmpro_init_recaptcha() {
125
  $recaptcha_privatekey = pmpro_getOption( 'recaptcha_privatekey' );
126
  }
127
  }
128
- add_action( 'wp', 'pmpro_init_recaptcha', 5 );
129
 
130
  /**
131
  * AJAX Method to Validate a ReCAPTCHA Response Token
@@ -137,7 +190,6 @@ function pmpro_wp_ajax_validate_recaptcha() {
137
 
138
  $reCaptcha = new pmpro_ReCaptcha( $recaptcha_privatekey );
139
  $resp = $reCaptcha->verifyResponse( $_SERVER['REMOTE_ADDR'], $_REQUEST['g-recaptcha-response'] );
140
-
141
  if ( $resp->success ) {
142
  pmpro_set_session_var( 'pmpro_recaptcha_validated', true );
143
  echo "1";
@@ -153,4 +205,5 @@ add_action( 'wp_ajax_pmpro_validate_recaptcha', 'pmpro_wp_ajax_validate_recaptch
153
  function pmpro_after_checkout_reset_recaptcha() {
154
  pmpro_unset_session_var( 'pmpro_recaptcha_validated' );
155
  }
156
- add_action( 'pmpro_after_checkout', 'pmpro_after_checkout_reset_recaptcha' );
 
5
  */
6
  function pmpro_init_recaptcha() {
7
  //don't load if setting is off
8
+ global $recaptcha, $recaptcha_validated, $pmpro_pages;
9
  $recaptcha = pmpro_getOption( 'recaptcha' );
10
  if ( empty( $recaptcha ) ) {
11
  return;
12
  }
13
 
14
+ //don't load unless we're on the checkout or billing page
15
+ $is_billing = ! empty( $pmpro_pages['billing'] ) && is_page( $pmpro_pages['billing'] );
16
+ if ( ! pmpro_is_checkout() && ! $is_billing ) {
17
  return;
18
  }
19
 
29
 
30
  require_once(PMPRO_DIR . '/includes/lib/recaptchalib.php' );
31
 
32
+ if ( ! function_exists( 'pmpro_recaptcha_get_html' ) ) {
33
+ function pmpro_recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) {
34
 
35
+ // Figure out language.
36
+ $locale = get_locale();
37
+ if(!empty($locale)) {
38
+ $parts = explode("_", $locale);
39
+ $lang = $parts[0];
40
+ } else {
41
+ $lang = "en";
42
+ }
43
+ $lang = apply_filters( 'pmpro_recaptcha_lang', $lang );
44
+
45
+ // Check which version of ReCAPTCHA we are using.
46
+ $recaptcha_version = pmpro_getOption( 'recaptcha_version' );
47
+
48
+ if( $recaptcha_version == '3_invisible' ) { ?>
49
+ <div class="g-recaptcha" data-sitekey="<?php echo $pubkey;?>" data-size="invisible" data-callback="onSubmit"></div>
50
+ <script type="text/javascript">
51
+ var pmpro_recaptcha_validated = false;
52
+ var pmpro_recaptcha_onSubmit = function(token) {
53
+ if ( pmpro_recaptcha_validated ) {
54
+ jQuery('#pmpro_form').submit();
55
+ return;
56
+ } else {
57
+ jQuery.ajax({
58
+ url: '<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>',
59
+ type: 'GET',
60
+ timeout: 30000,
61
+ dataType: 'html',
62
+ data: {
63
+ 'action': 'pmpro_validate_recaptcha',
64
+ 'g-recaptcha-response': token,
65
+ },
66
+ error: function(xml){
67
+ alert('Error validating ReCAPTCHA.');
68
+ },
69
+ success: function(response){
70
+ if ( response == '1' ) {
71
+ pmpro_recaptcha_validated = true;
72
+
73
+ //get a new token to be submitted with the form
74
+ grecaptcha.execute();
75
+ } else {
76
+ pmpro_recaptcha_validated = false;
77
+
78
+ //warn user validation failed
79
+ alert( 'ReCAPTCHA validation failed. Try again.' );
80
+
81
+ //get a new token to be submitted with the form
82
+ grecaptcha.execute();
83
+ }
84
+ }
85
+ });
86
+ }
87
+ };
88
+
89
+ var pmpro_recaptcha_onloadCallback = function() {
90
+ // Render on main submit button.
91
+ grecaptcha.render('pmpro_btn-submit', {
92
+ 'sitekey' : '<?php echo $pubkey;?>',
93
+ 'callback' : pmpro_recaptcha_onSubmit
94
+ });
95
+
96
+ // Update other submit buttons.
97
+ var submit_buttons = jQuery('.pmpro_btn-submit-checkout');
98
+ submit_buttons.each(function() {
99
+ if(jQuery(this).attr('id') != 'pmpro_btn-submit') {
100
+ jQuery(this).click(function(event) {
101
+ event.preventDefault();
102
+ grecaptcha.execute();
103
+ });
104
  }
 
105
  });
106
+ };
107
+ </script>
108
+ <script type="text/javascript"
109
+ src="https://www.google.com/recaptcha/api.js?onload=pmpro_recaptcha_onloadCallback&hl=<?php echo $lang;?>&render=explicit" async defer>
110
+ </script>
111
+ <?php } else { ?>
112
+ <div class="g-recaptcha" data-callback="pmpro_recaptcha_validatedCallback" data-expired-callback="pmpro_recaptcha_expiredCallback" data-sitekey="<?php echo $pubkey;?>"></div>
113
+ <script type="text/javascript">
114
+ var pmpro_recaptcha_validated = false;
115
+ var pmpro_recaptcha_error_msg = "<?php esc_attr_e( 'Please check the ReCAPTCHA box to confirm you are not a bot.', 'paid-memberships-pro' ); ?>";
116
+
117
+ // Validation callback.
118
+ function pmpro_recaptcha_validatedCallback() {
119
+ // ReCAPTCHA worked.
120
+ pmpro_recaptcha_validated = true;
121
+
122
+ // Re-enable the submit button.
123
+ jQuery('.pmpro_btn-submit-checkout,.pmpro_btn-submit').removeAttr('disabled');
124
 
125
+ // Hide processing message.
126
+ jQuery('#pmpro_processing_message').css('visibility', 'hidden');
127
+
128
+ // Hide error message.
129
+ if ( jQuery('#pmpro_message').text() == pmpro_recaptcha_error_msg ) {
130
+ jQuery( '#pmpro_message' ).hide();
131
+ jQuery( '#pmpro_message_bottom' ).hide();
132
+ }
133
+ };
134
+
135
+ // Expiration callback.
136
+ function pmpro_recaptcha_expiredCallback() {
137
+ pmpro_recaptcha_validated = false;
138
+ }
139
 
140
+ // Check validation on submit.
141
+ jQuery(document).ready(function(){
142
+ jQuery('#pmpro_form').submit(function(event){
143
+ if( pmpro_recaptcha_validated == false ) {
 
144
  event.preventDefault();
145
+
146
+ // Re-enable the submit button.
147
+ jQuery('.pmpro_btn-submit-checkout,.pmpro_btn-submit').removeAttr('disabled');
148
+
149
+ // Hide processing message.
150
+ jQuery('#pmpro_processing_message').css('visibility', 'hidden');
151
+
152
+ // error message
153
+ jQuery( '#pmpro_message' ).text( pmpro_recaptcha_error_msg ).addClass( 'pmpro_error' ).removeClass( 'pmpro_alert' ).removeClass( 'pmpro_success' ).hide().fadeIn();
154
+ jQuery( '#pmpro_message_bottom' ).hide().fadeIn();
155
+
156
+ return false;
157
+ } else {
158
+ return true;
159
+ }
160
+ });
161
  });
162
+ </script>
163
+ <script type="text/javascript"
164
+ src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang;?>">
165
+ </script>
166
+ <?php }
167
+ }
168
+ }
 
 
 
 
 
169
 
170
  //for templates using the old recaptcha_get_html
171
  if( ! function_exists( 'recaptcha_get_html' ) ) {
178
  $recaptcha_privatekey = pmpro_getOption( 'recaptcha_privatekey' );
179
  }
180
  }
181
+ add_action( 'wp', 'pmpro_init_recaptcha', 1 );
182
 
183
  /**
184
  * AJAX Method to Validate a ReCAPTCHA Response Token
190
 
191
  $reCaptcha = new pmpro_ReCaptcha( $recaptcha_privatekey );
192
  $resp = $reCaptcha->verifyResponse( $_SERVER['REMOTE_ADDR'], $_REQUEST['g-recaptcha-response'] );
 
193
  if ( $resp->success ) {
194
  pmpro_set_session_var( 'pmpro_recaptcha_validated', true );
195
  echo "1";
205
  function pmpro_after_checkout_reset_recaptcha() {
206
  pmpro_unset_session_var( 'pmpro_recaptcha_validated' );
207
  }
208
+ add_action( 'pmpro_after_checkout', 'pmpro_after_checkout_reset_recaptcha' );
209
+ add_action( 'pmpro_after_update_billing', 'pmpro_after_checkout_reset_recaptcha' );
includes/rest-api.php CHANGED
@@ -173,6 +173,50 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
173
  'permission_callback' => array( $this, 'pmpro_rest_api_get_permissions_check' )
174
  ),
175
  ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
177
 
178
  /**
@@ -183,16 +227,16 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
183
  function pmpro_rest_api_get_membership_level_for_user($request) {
184
  $params = $request->get_params();
185
 
186
- $user_id = isset( $params['user_id'] ) ? $params['user_id'] : null;
187
 
188
  // Param id was used instead (old style endpoint).
189
  if ( empty( $user_id ) && !empty( $params['id'] ) ) {
190
- $user_id = $params['id'];
191
  }
192
 
193
  // Query by email.
194
  if ( empty( $user_id ) && !empty( $params['email'] ) ) {
195
- $user = get_user_by_email( $params['email'] );
196
  $user_id = $user->ID;
197
  }
198
 
@@ -213,16 +257,16 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
213
  function pmpro_rest_api_get_membership_levels_for_user($request) {
214
  $params = $request->get_params();
215
 
216
- $user_id = isset( $params['user_id'] ) ? $params['user_id'] : null;
217
 
218
  // Param id was used instead.
219
  if ( empty( $user_id ) && !empty( $params['id'] ) ) {
220
- $user_id = $params['id'];
221
  }
222
 
223
  // Param email was used instead.
224
  if ( empty( $user_id ) && !empty( $params['email'] ) ) {
225
- $user = get_user_by_email( $params['email'] );
226
  $user_id = $user->ID;
227
  }
228
 
@@ -243,13 +287,13 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
243
  */
244
  function pmpro_rest_api_get_has_membership_access($request) {
245
  $params = $request->get_params();
246
- $post_id = isset( $params['post_id'] ) ? $params['post_id'] : null;
247
- $user_id = isset( $params['user_id'] ) ? $params['user_id'] : null;
248
 
249
  if ( empty( $user_id ) ) {
250
  // see if they sent an email
251
  if ( ! empty( $params['email'] ) ) {
252
- $user = get_user_by_email( $params['email'] );
253
  $user_id = $user->ID;
254
  } else {
255
  return new WP_REST_Response( 'No user information passed through.', 404 );
@@ -274,29 +318,43 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
274
  */
275
  function pmpro_rest_api_change_membership_level( $request ) {
276
  $params = $request->get_params();
277
- $user_id = isset( $params['user_id'] ) ? $params['user_id'] : null;
278
- $level_id = isset( $params['level_id'] ) ? $params['level_id'] : null;
 
 
279
 
280
  if ( empty( $user_id ) ) {
281
  // see if they sent an email
282
- if ( ! empty( $params['email'] ) ) {
283
- $user = get_user_by_email( $params['email'] );
284
  $user_id = $user->ID;
285
  } else {
 
 
 
 
286
  return new WP_REST_Response( 'No user information passed through.', 404 );
287
  }
288
  }
289
 
290
  if ( ! function_exists( 'pmpro_changeMembershipLevel' ) ) {
 
 
 
 
291
  return new WP_REST_Response( 'Paid Memberships Pro function not found.', 404 );
292
  }
293
-
294
  if ( ! empty( $user_id ) ) {
295
  $response = pmpro_changeMembershipLevel( $level_id, $user_id );
296
  } else {
297
  $response = false;
298
  }
299
 
 
 
 
 
300
  return new WP_REST_Response( $response, 200 );
301
  }
302
 
@@ -307,24 +365,38 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
307
  */
308
  function pmpro_rest_api_cancel_membership_level( $request ) {
309
  $params = $request->get_params();
310
- $user_id = isset( $params['user_id'] ) ? $params['user_id'] : null;
311
- $level_id = isset( $params['level_id'] ) ? $params['level_id'] : null;
 
 
312
 
313
  if ( empty( $user_id ) ) {
314
  // see if they sent an email
315
- if ( ! empty( $params['email'] ) ) {
316
- $user = get_user_by_email( $params['email'] );
317
  $user_id = $user->ID;
318
  } else {
 
 
 
 
319
  return new WP_REST_Response( 'No user information passed through.', 404 );
320
  }
321
  }
322
 
323
  if ( empty( $level_id ) ) {
 
 
 
 
324
  return new WP_REST_Response( 'No membership level ID data.', 400 );
325
  }
326
 
327
  if ( ! function_exists( 'pmpro_cancelMembershipLevel' ) ) {
 
 
 
 
328
  return new WP_REST_Response( 'Paid Memberships Pro function not found.', 404 );
329
  }
330
 
@@ -333,7 +405,11 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
333
  } else {
334
  $response = false;
335
  }
336
-
 
 
 
 
337
  return new WP_REST_Response( $response, 200 );
338
  }
339
 
@@ -350,6 +426,7 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
350
 
351
  $params = $request->get_params();
352
  $id = isset( $params['id'] ) ? intval( $params['id'] ) : null;
 
353
 
354
  if ( empty( $id ) ) {
355
  return new WP_REST_Response( 'ID not passed through', 400 );
@@ -364,6 +441,10 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
364
  $level->confirmation = '';
365
  }
366
 
 
 
 
 
367
  return new WP_REST_Response( $level, 200 );
368
  }
369
 
@@ -382,6 +463,7 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
382
  $method = $request->get_method();
383
 
384
  $id = isset( $params['id'] ) ? intval( $params['id'] ) : '';
 
385
 
386
  // Pass through an ID only for PUT/PATCH methods. POST treats it as a brand new level.
387
  if ( ! empty( $id ) && ( $method === 'PUT' || $method === 'PATCH' ) ) {
@@ -422,6 +504,10 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
422
  $level->categories = $categories;
423
  $level->save();
424
 
 
 
 
 
425
  return new WP_REST_Response( $level, 200 );
426
 
427
  }
@@ -460,7 +546,7 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
460
  }
461
 
462
  $params = $request->get_params();
463
- $code = isset( $params['code'] ) ? $params['code'] : null;
464
 
465
  if ( empty( $code ) ) {
466
  return new WP_REST_Response( 'No discount code sent.', 400 );
@@ -487,7 +573,7 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
487
  $uses = isset( $params['uses'] ) ? intval( $params['uses'] ) : '';
488
  $starts = isset( $params['starts'] ) ? sanitize_text_field( $params['starts'] ) : '';
489
  $expires = isset( $params['expires'] ) ? sanitize_text_field( $params['expires'] ) : '';
490
- $levels = isset( $params['levels'] ) ? $params['levels'] : null;
491
 
492
  if ( ! empty( $levels ) ) {
493
  $levels = json_decode( $levels, true );
@@ -529,7 +615,7 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
529
 
530
  $discount_code->code = $code;
531
  $discount_code->starts = $starts;
532
- $discount_code->ends = $expires;
533
  $discount_code->uses = $uses;
534
  $discount_code->levels = !empty( $levels_array ) ? $levels_array : $levels;
535
  $discount_code->save();
@@ -546,12 +632,17 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
546
  function pmpro_rest_api_get_checkout_level( $request ) {
547
  $params = $request->get_params();
548
 
549
- $level_id = isset( $params['level_id'] ) ? $params['level_id'] : null;
 
 
 
 
 
550
  if ( empty( $level_id ) ) {
551
  return new WP_REST_Response( 'No level found.', 400 );
552
  }
553
 
554
- $discount_code = isset( $params['discount_code'] ) ? $params['discount_code'] : null;
555
  $checkout_level = pmpro_getLevelAtCheckout( $level_id, $discount_code );
556
 
557
  // Hide confirmation message if not an admin or member.
@@ -575,27 +666,183 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
575
  if ( ! empty( $pmpro_checkout_level_ids ) ) {
576
  // MMPU Compatibility...
577
  $level_ids = $pmpro_checkout_level_ids;
578
- } elseif ( isset( $_REQUEST['level_id'] ) ) {
579
- $level_ids = explode( '+', $_REQUEST['level_id'] );
 
 
580
  }
581
 
582
  if ( empty( $level_ids ) ) {
583
  return new WP_REST_Response( 'No levels found.', 400 );
584
  }
585
- $discount_code = isset( $params['discount_code'] ) ? $params['discount_code'] : null;
586
 
587
  $r = array();
588
  $r['initial_payment'] = 0.00;
589
  foreach ( $level_ids as $level_id ) {
590
  $r[ $level_id ] = pmpro_getLevelAtCheckout( $level_id, $discount_code );
591
  if ( ! empty( $r[ $level_id ]->initial_payment ) ) {
592
- $r['initial_payment'] += intval( $r[ $level_id ]->initial_payment );
593
  }
594
  }
595
  $r['initial_payment_formatted'] = pmpro_formatPrice( $r['initial_payment'] );
596
  return new WP_REST_Response( $r );
597
  }
598
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  /**
600
  * Default permissions check for endpoints/routes.
601
  * Defaults to 'subscriber' for all GET requests and
@@ -621,18 +868,21 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
621
  '/pmpro/v1/membership_level' => true,
622
  '/pmpro/v1/discount_code' => 'pmpro_discountcodes',
623
  '/pmpro/v1/checkout_level' => true,
624
- '/pmpro/v1/checkout_levels' => true,
 
 
 
625
  );
626
  $route_caps = apply_filters( 'pmpro_rest_api_route_capabilities', $route_caps, $request );
627
-
628
  if ( isset( $route_caps[$route] ) ) {
629
  if ( $route_caps[$route] === true ) {
630
  // public
631
  $permission = true;
632
- } else {
633
- $permission = current_user_can( $route_caps[$route] );
634
  }
635
- }
636
 
637
  // Is the request method allowed? We disable DELETE by default.
638
  if ( ! in_array( $method, pmpro_get_rest_api_methods( $route ) ) ) {
@@ -640,7 +890,6 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
640
  }
641
 
642
  $permission = apply_filters( 'pmpro_rest_api_permissions', $permission, $request );
643
-
644
  return $permission;
645
  }
646
 
@@ -651,8 +900,6 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
651
  function pmpro_rest_api_convert_to_array( $string ) {
652
  return explode( ',', $string );
653
  }
654
-
655
-
656
  } // End of class
657
 
658
  /**
@@ -667,6 +914,7 @@ if ( class_exists( 'WP_REST_Controller' ) ) {
667
  add_action( 'rest_api_init', 'pmpro_rest_api_register_custom_routes', 5 );
668
  }
669
 
 
670
  /**
671
  * Get the allowed methods for PMPro REST API endpoints.
672
  * To enable DELETE, hook into this filter.
@@ -676,4 +924,4 @@ function pmpro_get_rest_api_methods( $route = NULL ) {
676
  $methods = array( 'GET', 'POST', 'PUT', 'PATCH' );
677
  $methods = apply_filters( 'pmpro_rest_api_methods', $methods, $route );
678
  return $methods;
679
- }
173
  'permission_callback' => array( $this, 'pmpro_rest_api_get_permissions_check' )
174
  ),
175
  ));
176
+
177
+ /**
178
+ * Authentication route for Zapier integration.
179
+ *
180
+ * Used to do authentication when connecting Zapier to PMPro.
181
+ *
182
+ * @since 2.6.0
183
+ */
184
+ register_rest_route( $pmpro_namespace, '/me',
185
+ array(
186
+ array(
187
+ 'methods' => WP_REST_Server::READABLE,
188
+ 'callback' => array( $this, 'validate_me' ),
189
+ 'permission_callback' => array( $this, 'pmpro_rest_api_get_permissions_check' )
190
+ ),
191
+ )
192
+ );
193
+
194
+ /**
195
+ * Get the last couple of membership levels/members.
196
+ *
197
+ * @since 2.6.0
198
+ */
199
+ register_rest_route( $pmpro_namespace, '/recent_memberships',
200
+ array(
201
+ array(
202
+ 'methods' => WP_REST_Server::READABLE,
203
+ 'callback' => array( $this, 'pmpro_rest_api_recent_memberships' ),
204
+ 'args' => array(
205
+ 'status' => array(),
206
+ ),
207
+ 'permission_callback' => array( $this, 'pmpro_rest_api_get_permissions_check' ),
208
+ )
209
+ ));
210
+
211
+ register_rest_route( $pmpro_namespace, '/recent_orders',
212
+ array(
213
+ array(
214
+ 'methods' => WP_REST_Server::READABLE,
215
+ 'callback' => array( $this, 'pmpro_rest_api_recent_orders' ),
216
+ 'permission_callback' => array( $this, 'pmpro_rest_api_get_permissions_check' ),
217
+ )
218
+ )
219
+ );
220
  }
221
 
222
  /**
227
  function pmpro_rest_api_get_membership_level_for_user($request) {
228
  $params = $request->get_params();
229
 
230
+ $user_id = isset( $params['user_id'] ) ? intval( $params['user_id'] ) : null;
231
 
232
  // Param id was used instead (old style endpoint).
233
  if ( empty( $user_id ) && !empty( $params['id'] ) ) {
234
+ $user_id = intval( $params['id'] );
235
  }
236
 
237
  // Query by email.
238
  if ( empty( $user_id ) && !empty( $params['email'] ) ) {
239
+ $user = get_user_by_email( sanitize_email( $params['email'] ) );
240
  $user_id = $user->ID;
241
  }
242
 
257
  function pmpro_rest_api_get_membership_levels_for_user($request) {
258
  $params = $request->get_params();
259
 
260
+ $user_id = isset( $params['user_id'] ) ? intval( $params['user_id'] ) : null;
261
 
262
  // Param id was used instead.
263
  if ( empty( $user_id ) && !empty( $params['id'] ) ) {
264
+ $user_id = intval( $params['id'] );
265
  }
266
 
267
  // Param email was used instead.
268
  if ( empty( $user_id ) && !empty( $params['email'] ) ) {
269
+ $user = get_user_by_email( sanitize_email( $params['email'] ) );
270
  $user_id = $user->ID;
271
  }
272
 
287
  */
288
  function pmpro_rest_api_get_has_membership_access($request) {
289
  $params = $request->get_params();
290
+ $post_id = isset( $params['post_id'] ) ? intval( $params['post_id'] ) : null;
291
+ $user_id = isset( $params['user_id'] ) ? intval( $params['user_id'] ) : null;
292
 
293
  if ( empty( $user_id ) ) {
294
  // see if they sent an email
295
  if ( ! empty( $params['email'] ) ) {
296
+ $user = get_user_by_email( sanitize_email( $params['email'] ) );
297
  $user_id = $user->ID;
298
  } else {
299
  return new WP_REST_Response( 'No user information passed through.', 404 );
318
  */
319
  function pmpro_rest_api_change_membership_level( $request ) {
320
  $params = $request->get_params();
321
+ $user_id = isset( $params['user_id'] ) ? intval( $params['user_id'] ) : null;
322
+ $level_id = isset( $params['level_id'] ) ? intval( $params['level_id'] ) : null;
323
+ $email = isset( $params['email'] ) ? sanitize_email( $params['email'] ) : null;
324
+ $response_type = isset( $params['response_type'] ) ? sanitize_text_field( $params['response_type'] ) : null;
325
 
326
  if ( empty( $user_id ) ) {
327
  // see if they sent an email
328
+ if ( ! empty( $email ) ) {
329
+ $user = get_user_by_email( $email );
330
  $user_id = $user->ID;
331
  } else {
332
+ if ( 'json' === $response_type ) {
333
+ wp_send_json_error( array( 'email' => $email, 'error' => 'No user information passed through.' ) );
334
+ }
335
+
336
  return new WP_REST_Response( 'No user information passed through.', 404 );
337
  }
338
  }
339
 
340
  if ( ! function_exists( 'pmpro_changeMembershipLevel' ) ) {
341
+ if ( 'json' === $response_type ) {
342
+ wp_send_json_error( array( 'email' => $email, 'error' => 'Paid Memberships Pro function not found.' ) );
343
+ }
344
+
345
  return new WP_REST_Response( 'Paid Memberships Pro function not found.', 404 );
346
  }
347
+
348
  if ( ! empty( $user_id ) ) {
349
  $response = pmpro_changeMembershipLevel( $level_id, $user_id );
350
  } else {
351
  $response = false;
352
  }
353
 
354
+ if ( 'json' === $response_type ) {
355
+ wp_send_json_success( array( 'user_id' => $user_id, 'level_changed' => $level_id, 'response' => $response, 'status' => 200 ) );
356
+ }
357
+
358
  return new WP_REST_Response( $response, 200 );
359
  }
360
 
365
  */
366
  function pmpro_rest_api_cancel_membership_level( $request ) {
367
  $params = $request->get_params();
368
+ $user_id = isset( $params['user_id'] ) ? intval( $params['user_id'] ) : null;
369
+ $level_id = isset( $params['level_id'] ) ? intval( $params['level_id'] ) : null;
370
+ $email = isset( $params['email'] ) ? sanitize_email( $params['email'] ) : null;
371
+ $response_type = isset( $params['response_type'] ) ? sanitize_text_field( $params['response_type'] ) : null;
372
 
373
  if ( empty( $user_id ) ) {
374
  // see if they sent an email
375
+ if ( ! empty( $email ) ) {
376
+ $user = get_user_by_email( $email );
377
  $user_id = $user->ID;
378
  } else {
379
+ if ( 'json' === $response_type ) {
380
+ wp_send_json_error( array( 'email' => $email ) );
381
+ }
382
+
383
  return new WP_REST_Response( 'No user information passed through.', 404 );
384
  }
385
  }
386
 
387
  if ( empty( $level_id ) ) {
388
+ if ( 'json' === $response_type ) {
389
+ wp_send_json_error( array( 'email' => $email ) );
390
+ }
391
+
392
  return new WP_REST_Response( 'No membership level ID data.', 400 );
393
  }
394
 
395
  if ( ! function_exists( 'pmpro_cancelMembershipLevel' ) ) {
396
+ if ( 'json' === $response_type ) {
397
+ wp_send_json_error( array( 'email' => $email ) );
398
+ }
399
+
400
  return new WP_REST_Response( 'Paid Memberships Pro function not found.', 404 );
401
  }
402
 
405
  } else {
406
  $response = false;
407
  }
408
+
409
+ if ( 'json' === $response_type ) {
410
+ wp_send_json_success( array( 'email' => $email ) );
411
+ }
412
+
413
  return new WP_REST_Response( $response, 200 );
414
  }
415
 
426
 
427
  $params = $request->get_params();
428
  $id = isset( $params['id'] ) ? intval( $params['id'] ) : null;
429
+ $response_type = isset( $params['response_type'] ) ? sanitize_text_field( $params['response_type'] ) : false;
430
 
431
  if ( empty( $id ) ) {
432
  return new WP_REST_Response( 'ID not passed through', 400 );
441
  $level->confirmation = '';
442
  }
443
 
444
+ if ( 'json' === $response_type ) {
445
+ wp_send_json_success( array( 'level' => $level ) );
446
+ }
447
+
448
  return new WP_REST_Response( $level, 200 );
449
  }
450
 
463
  $method = $request->get_method();
464
 
465
  $id = isset( $params['id'] ) ? intval( $params['id'] ) : '';
466
+ $response_type = isset( $params['response_type'] ) ? sanitize_text_field( $params['response_type'] ) : null;
467
 
468
  // Pass through an ID only for PUT/PATCH methods. POST treats it as a brand new level.
469
  if ( ! empty( $id ) && ( $method === 'PUT' || $method === 'PATCH' ) ) {
504
  $level->categories = $categories;
505
  $level->save();
506
 
507
+ if ( 'json' === $response_type ) {
508
+ wp_send_json_success( array( "level" => $level ) );
509
+ }
510
+
511
  return new WP_REST_Response( $level, 200 );
512
 
513
  }
546
  }
547
 
548
  $params = $request->get_params();
549
+ $code = isset( $params['code'] ) ? sanitize_text_field( $params['code'] ) : null;
550
 
551
  if ( empty( $code ) ) {
552
  return new WP_REST_Response( 'No discount code sent.', 400 );
573
  $uses = isset( $params['uses'] ) ? intval( $params['uses'] ) : '';
574
  $starts = isset( $params['starts'] ) ? sanitize_text_field( $params['starts'] ) : '';
575
  $expires = isset( $params['expires'] ) ? sanitize_text_field( $params['expires'] ) : '';
576
+ $levels = isset( $params['levels'] ) ? sanitize_text_field( $params['levels'] ) : null;
577
 
578
  if ( ! empty( $levels ) ) {
579
  $levels = json_decode( $levels, true );
615
 
616
  $discount_code->code = $code;
617
  $discount_code->starts = $starts;
618
+ $discount_code->expires = $expires;
619
  $discount_code->uses = $uses;
620
  $discount_code->levels = !empty( $levels_array ) ? $levels_array : $levels;
621
  $discount_code->save();
632
  function pmpro_rest_api_get_checkout_level( $request ) {
633
  $params = $request->get_params();
634
 
635
+ if ( isset( $params['level_id'] ) ) {
636
+ $level_id = intval( $params['level_id'] );
637
+ } elseif ( isset( $params['level'] ) ) {
638
+ $level_id = intval( $params['level'] );
639
+ }
640
+
641
  if ( empty( $level_id ) ) {
642
  return new WP_REST_Response( 'No level found.', 400 );
643
  }
644
 
645
+ $discount_code = isset( $params['discount_code'] ) ? sanitize_text_field( $params['discount_code'] ) : null;
646
  $checkout_level = pmpro_getLevelAtCheckout( $level_id, $discount_code );
647
 
648
  // Hide confirmation message if not an admin or member.
666
  if ( ! empty( $pmpro_checkout_level_ids ) ) {
667
  // MMPU Compatibility...
668
  $level_ids = $pmpro_checkout_level_ids;
669
+ } elseif ( isset( $params['level_id'] ) ) {
670
+ $level_ids = explode( '+', intval( $params['level_id'] ) );
671
+ } elseif ( isset( $params['level'] ) ) {
672
+ $level_ids = explode( '+', intval( $params['level'] ) );
673
  }
674
 
675
  if ( empty( $level_ids ) ) {
676
  return new WP_REST_Response( 'No levels found.', 400 );
677
  }
678
+ $discount_code = isset( $params['discount_code'] ) ? sanitize_text_field( $params['discount_code'] ) : null;
679
 
680
  $r = array();
681
  $r['initial_payment'] = 0.00;
682
  foreach ( $level_ids as $level_id ) {
683
  $r[ $level_id ] = pmpro_getLevelAtCheckout( $level_id, $discount_code );
684
  if ( ! empty( $r[ $level_id ]->initial_payment ) ) {
685
+ $r['initial_payment'] += floatval( $r[ $level_id ]->initial_payment );
686
  }
687
  }
688
  $r['initial_payment_formatted'] = pmpro_formatPrice( $r['initial_payment'] );
689
  return new WP_REST_Response( $r );
690
  }
691
 
692
+
693
+ /// ZAPIER TRIGGERS
694
+ /**
695
+ * Handle authentication testing for the Zapier API.
696
+ *
697
+ * @since 2.6.0
698
+ *
699
+ * @param WP_REST_Request $request The REST request.
700
+ */
701
+ public function validate_me( $request ) {
702
+
703
+ $params = $request->get_params();
704
+
705
+ if ( is_user_logged_in() ) {
706
+ $me = wp_get_current_user()->display_name;
707
+ } else {
708
+ $me = false;
709
+ }
710
+
711
+ wp_send_json_success( array( 'username' => $me ) );
712
+ }
713
+
714
+ /**
715
+ * Handle requests for the list of recent memberships.
716
+ *
717
+ * @since 2.6.0
718
+ *
719
+ * @param WP_REST_Request $request The REST request.
720
+ *
721
+ * @return WP_REST_Response The REST response.
722
+ */
723
+ public function pmpro_rest_api_recent_memberships( $request ) {
724
+ $params = $request->get_params();
725
+ if ( isset($params['limit']) ) {
726
+ $limit = intval( $params['limit'] );
727
+ } else {
728
+ $limit = 1;
729
+ }
730
+ /**
731
+ * Allow filtering the total number of recent members to show in the /recent_memberships PMPro endpoint.
732
+ *
733
+ * @param int $limit The total number of recent members to show.
734
+ */
735
+ $limit = apply_filters( 'pmpro_trigger_recent_members_limit', $limit );
736
+
737
+ $response_type = isset( $params['response_type'] ) ? sanitize_text_field( $params['response_type'] ) : null;
738
+
739
+ if ( empty( $params['level_status'] ) ) {
740
+ $level_status = [ 'active' ];
741
+ } else {
742
+ $level_status = sanitize_text_field( trim( $params['level_status'] ) );
743
+
744
+ // Force it into an array so we can implode it in the query itself.
745
+ $level_status = explode( ',', $level_status );
746
+ }
747
+
748
+ // Set up values to prepare.
749
+ $prepare = $level_status;
750
+ $prepare[] = $limit;
751
+
752
+ // Set up the placeholders we want to use.
753
+ $level_status_placeholders = implode( ', ', array_fill( 0, count( $level_status ), '%s' ) );
754
+
755
+ // Grab the useful information.
756
+ global $wpdb;
757
+
758
+ $sql = "
759
+ SELECT
760
+ `mu`.`user_id` as `id`,
761
+ `u`.`user_email`,
762
+ `u`.`user_nicename`,
763
+ `mu`.`membership_id`,
764
+ `ml`.`name` as membership_name,
765
+ `mu`.`status`,
766
+ `mu`.`modified`
767
+ FROM `{$wpdb->pmpro_memberships_users}` AS `mu`
768
+ LEFT JOIN `{$wpdb->users}` AS `u`
769
+ ON `mu`.`user_id` = `u`.`id`
770
+ LEFT JOIN `{$wpdb->pmpro_membership_levels}` AS `ml`
771
+ ON `ml`.`id` = `mu`.`membership_id`
772
+ WHERE
773
+ `mu`.`status` IN ( {$level_status_placeholders} )
774
+ ORDER BY
775
+ `mu`.`modified` DESC
776
+ LIMIT %d
777
+ ";
778
+
779
+ $results = $wpdb->get_results( $wpdb->prepare( $sql, $prepare ) );
780
+
781
+ // Let's format the date to ISO8601
782
+ $results[0]->modified = pmpro_format_date_iso8601( $results[0]->modified );
783
+
784
+ return new WP_REST_Response( $results, 200 );
785
+
786
+ }
787
+
788
+ /**
789
+ * Handle requests for the list of recent orders.
790
+ *
791
+ * @since 2.6.0
792
+ *
793
+ * @param WP_REST_Request $request The REST request.
794
+ *
795
+ * @return WP_REST_Response The REST response.
796
+ */
797
+ public function pmpro_rest_api_recent_orders( $request ) {
798
+ $params = $request->get_params();
799
+
800
+ if ( isset($params['limit']) ) {
801
+ $orders_limit = intval( $params['limit'] );
802
+ } else {
803
+ $orders_limit = 1;
804
+ }
805
+
806
+ $limit = apply_filters( 'pmpro_trigger_recent_orders_limit', $orders_limit );
807
+
808
+ global $wpdb;
809
+
810
+ $sql = "
811
+ SELECT
812
+ `o`.`id`,
813
+ `o`.`code`,
814
+ `u`.`ID` AS `user_id`,
815
+ `u`.`user_email`,
816
+ `u`.`user_nicename`,
817
+ `o`.`billing_name`,
818
+ `o`.`billing_street`,
819
+ `o`.`billing_city`,
820
+ `o`.`billing_state`,
821
+ `o`.`billing_zip`,
822
+ `o`.`billing_country`,
823
+ `o`.`billing_phone`,
824
+ `o`.`subtotal`,
825
+ `o`.`tax`,
826
+ `o`.`total`,
827
+ `o`.`status`,
828
+ `o`.`gateway`,
829
+ `o`.`gateway_environment`,
830
+ `o`.`timestamp`
831
+ FROM `{$wpdb->pmpro_membership_orders}` AS `o`
832
+ LEFT JOIN `{$wpdb->users}` AS `u`
833
+ ON `o`.`user_id` = `u`.`ID`
834
+ ORDER BY
835
+ `o`.`timestamp` DESC
836
+ LIMIT %d
837
+ ";
838
+
839
+ $results = $wpdb->get_results( $wpdb->prepare( $sql, $limit ) );
840
+
841
+ $results[0]->timestamp = pmpro_format_date_iso8601( $results[0]->timestamp );
842
+
843
+ return new WP_REST_Response( $results, 200 );
844
+ }
845
+
846
  /**
847
  * Default permissions check for endpoints/routes.
848
  * Defaults to 'subscriber' for all GET requests and
868
  '/pmpro/v1/membership_level' => true,
869
  '/pmpro/v1/discount_code' => 'pmpro_discountcodes',
870
  '/pmpro/v1/checkout_level' => true,
871
+ '/pmpro/v1/checkout_levels' => true,
872
+ '/pmpro/v1/me' => true,
873
+ '/pmpro/v1/recent_memberships' => 'pmpro_edit_memberships',
874
+ '/pmpro/v1/recent_orders' => 'pmpro_orders'
875
  );
876
  $route_caps = apply_filters( 'pmpro_rest_api_route_capabilities', $route_caps, $request );
877
+
878
  if ( isset( $route_caps[$route] ) ) {
879
  if ( $route_caps[$route] === true ) {
880
  // public
881
  $permission = true;
882
+ } else {
883
+ $permission = current_user_can( $route_caps[$route] );
884
  }
885
+ }
886
 
887
  // Is the request method allowed? We disable DELETE by default.
888
  if ( ! in_array( $method, pmpro_get_rest_api_methods( $route ) ) ) {
890
  }
891
 
892
  $permission = apply_filters( 'pmpro_rest_api_permissions', $permission, $request );
 
893
  return $permission;
894
  }
895
 
900
  function pmpro_rest_api_convert_to_array( $string ) {
901
  return explode( ',', $string );
902
  }
 
 
903
  } // End of class
904
 
905
  /**
914
  add_action( 'rest_api_init', 'pmpro_rest_api_register_custom_routes', 5 );
915
  }
916
 
917
+
918
  /**
919
  * Get the allowed methods for PMPro REST API endpoints.
920
  * To enable DELETE, hook into this filter.
924
  $methods = array( 'GET', 'POST', 'PUT', 'PATCH' );
925
  $methods = apply_filters( 'pmpro_rest_api_methods', $methods, $route );
926
  return $methods;
927
+ }
includes/scripts.php CHANGED
@@ -5,27 +5,26 @@
5
  function pmpro_enqueue_scripts() {
6
  global $pmpro_pages;
7
 
8
- // Frontend styles.
9
- $frontend_css_rtl = false;
10
- if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/css/frontend.css")) {
11
- $frontend_css = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/frontend.css";
12
- if( is_rtl() && file_exists(get_stylesheet_directory() . "/paid-memberships-pro/css/frontend-rtl.css") ) {
 
 
 
 
 
 
 
 
13
  $frontend_css_rtl = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/frontend-rtl.css";
14
- }
15
- } elseif(file_exists(get_template_directory() . "/paid-memberships-pro/frontend.css")) {
16
- $frontend_css = get_template_directory_uri() . "/paid-memberships-pro/frontend.css";
17
- if( is_rtl() && file_exists(get_template_directory() . "/paid-memberships-pro/css/frontend-rtl.css") ) {
18
  $frontend_css_rtl = get_template_directory_uri() . "/paid-memberships-pro/css/frontend-rtl.css";
19
- }
20
- } else {
21
- $frontend_css = plugins_url('css/frontend.css',dirname(__FILE__) );
22
- if( is_rtl() ) {
23
  $frontend_css_rtl = plugins_url('css/frontend-rtl.css',dirname(__FILE__) );
24
- }
25
- }
26
- wp_enqueue_style('pmpro_frontend', $frontend_css, array(), PMPRO_VERSION, "screen");
27
- if( $frontend_css_rtl ) {
28
- wp_enqueue_style('pmpro_frontend_rtl', $frontend_css_rtl, array(), PMPRO_VERSION, "screen");
29
  }
30
 
31
  // Print styles.
@@ -94,43 +93,53 @@ add_action( 'wp_enqueue_scripts', 'pmpro_enqueue_scripts' );
94
  * Enqueue admin JavaScript and CSS
95
  */
96
  function pmpro_admin_enqueue_scripts() {
97
- // Admin JS
98
- wp_register_script( 'pmpro_admin',
99
- plugins_url( 'js/pmpro-admin.js', dirname(__FILE__) ),
100
- array( 'jquery', 'jquery-ui-sortable' ),
101
- PMPRO_VERSION );
102
- $all_levels = pmpro_getAllLevels( true, true );
103
- $all_level_values_and_labels = array();
104
- foreach( $all_levels as $level ) {
105
- $all_level_values_and_labels[] = array( 'value' => $level->id, 'label' => $level->name );
106
- }
107
- wp_localize_script( 'pmpro_admin', 'pmpro', array(
108
- 'all_levels' => $all_levels,
109
- 'all_level_values_and_labels' => $all_level_values_and_labels
110
- ));
111
- wp_enqueue_script( 'pmpro_admin' );
112
 
113
- // Admin CSS
114
- $admin_css_rtl = false;
115
- if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/css/admin.css")) {
116
- $admin_css = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/admin.css";
117
- if( is_rtl() && file_exists(get_stylesheet_directory() . "/paid-memberships-pro/css/admin-rtl.css") ) {
118
- $admin_css_rtl = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/admin-rtl.css";
119
- }
120
- } elseif(file_exists(get_template_directory() . "/paid-memberships-pro/admin.css")) {
121
- $admin_css = get_template_directory_uri() . "/paid-memberships-pro/admin.css";
122
- if( is_rtl() && file_exists(get_template_directory() . "/paid-memberships-pro/css/admin-rtl.css") ) {
123
- $admin_css_rtl = get_template_directory_uri() . "/paid-memberships-pro/css/admin-rtl.css";
124
- }
125
- } else {
126
- $admin_css = plugins_url('css/admin.css',dirname(__FILE__) );
127
- if( is_rtl() ) {
128
- $admin_css_rtl = plugins_url('css/admin-rtl.css',dirname(__FILE__) );
129
- }
130
- }
131
- wp_enqueue_style('pmpro_admin', $admin_css, array(), PMPRO_VERSION, "screen");
132
- if( $admin_css_rtl ) {
133
- wp_enqueue_style('pmpro_admin_rtl', $admin_css_rtl, array(), PMPRO_VERSION, "screen");
134
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  }
136
- add_action( 'admin_enqueue_scripts', 'pmpro_admin_enqueue_scripts' );
5
  function pmpro_enqueue_scripts() {
6
  global $pmpro_pages;
7
 
8
+ // Figure out which frontend.css file to load.
9
+ if( file_exists( get_stylesheet_directory() . "/paid-memberships-pro/css/frontend.css" ) ) {
10
+ $frontend_css = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/frontend.css";
11
+ } elseif( file_exists( get_template_directory() . "/paid-memberships-pro/frontend.css" ) ) {
12
+ $frontend_css = get_template_directory_uri() . "/paid-memberships-pro/frontend.css";
13
+ } else {
14
+ $frontend_css = plugins_url( 'css/frontend.css',dirname(__FILE__) );
15
+ }
16
+ wp_enqueue_style( 'pmpro_frontend', $frontend_css, array(), PMPRO_VERSION, "screen" );
17
+
18
+ // Figure out which frontend-rlt.css file to load if applicable.
19
+ if( is_rtl() ) {
20
+ if( file_exists( get_stylesheet_directory() . "/paid-memberships-pro/css/frontend-rtl.css" ) ) {
21
  $frontend_css_rtl = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/frontend-rtl.css";
22
+ } elseif ( file_exists( get_template_directory() . "/paid-memberships-pro/css/frontend-rtl.css" ) ) {
 
 
 
23
  $frontend_css_rtl = get_template_directory_uri() . "/paid-memberships-pro/css/frontend-rtl.css";
24
+ } else {
 
 
 
25
  $frontend_css_rtl = plugins_url('css/frontend-rtl.css',dirname(__FILE__) );
26
+ }
27
+ wp_enqueue_style( 'pmpro_frontend_rtl', $frontend_css_rtl, array(), PMPRO_VERSION, "screen" );
 
 
 
28
  }
29
 
30
  // Print styles.
93
  * Enqueue admin JavaScript and CSS
94
  */
95
  function pmpro_admin_enqueue_scripts() {
96
+ // Admin JS
97
+ wp_register_script( 'pmpro_admin', plugins_url( 'js/pmpro-admin.js', __DIR__ ), [
98
+ 'jquery',
99
+ 'jquery-ui-sortable',
100
+ ], PMPRO_VERSION );
 
 
 
 
 
 
 
 
 
 
101
 
102
+ $all_levels = pmpro_getAllLevels( true, true );
103
+ $all_level_values_and_labels = [];
104
+
105
+ foreach ( $all_levels as $level ) {
106
+ $all_level_values_and_labels[] = [
107
+ 'value' => $level->id,
108
+ 'label' => $level->name,
109
+ ];
110
+ }
111
+
112
+ wp_localize_script( 'pmpro_admin', 'pmpro', [
113
+ 'all_levels' => $all_levels,
114
+ 'all_level_values_and_labels' => $all_level_values_and_labels,
115
+ ] );
116
+
117
+ // Figure out which admin.css to load.
118
+ if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/css/admin.css' ) ) {
119
+ $admin_css = get_stylesheet_directory_uri() . '/paid-memberships-pro/css/admin.css';
120
+ } elseif ( file_exists( get_template_directory() . '/paid-memberships-pro/admin.css' ) ) {
121
+ $admin_css = get_template_directory_uri() . '/paid-memberships-pro/admin.css';
122
+ } else {
123
+ $admin_css = plugins_url( 'css/admin.css', __DIR__ );
124
+ }
125
+
126
+ // Figure out which admin-rtl.css to load if applicable.
127
+ if ( file_exists( get_stylesheet_directory() . '/paid-memberships-pro/css/admin-rtl.css' ) ) {
128
+ $admin_css_rtl = get_stylesheet_directory_uri() . '/paid-memberships-pro/css/admin-rtl.css';
129
+ } elseif( file_exists( get_template_directory() . '/paid-memberships-pro/css/admin-rtl.css' ) ) {
130
+ $admin_css_rtl = get_template_directory_uri() . '/paid-memberships-pro/css/admin-rtl.css';
131
+ } else {
132
+ $admin_css_rtl = plugins_url( 'css/admin-rtl.css', __DIR__ );
133
+ }
134
+
135
+ wp_register_style( 'pmpro_admin', $admin_css, [], PMPRO_VERSION, 'screen' );
136
+ wp_register_style( 'pmpro_admin_rtl', $admin_css_rtl, [], PMPRO_VERSION, 'screen' );
137
+
138
+ wp_enqueue_script( 'pmpro_admin' );
139
+ wp_enqueue_style( 'pmpro_admin' );
140
+
141
+ if ( is_rtl() ) {
142
+ wp_enqueue_style( 'pmpro_admin_rtl' );
143
+ }
144
  }
145
+ add_action( 'admin_enqueue_scripts', 'pmpro_admin_enqueue_scripts' );
includes/services.php CHANGED
@@ -86,7 +86,12 @@ add_action('wp_ajax_pmpro_orders_print_view', 'pmpro_orders_print_view');
86
  * @since 1.8.6
87
  */
88
  function pmpro_get_order_json() {
89
- $order_id = $_REQUEST['order_id'];
 
 
 
 
 
90
  $order = new MemberOrder($order_id);
91
  echo json_encode($order);
92
  exit;
@@ -94,7 +99,11 @@ function pmpro_get_order_json() {
94
  add_action('wp_ajax_pmpro_get_order_json', 'pmpro_get_order_json');
95
 
96
  function pmpro_update_level_order() {
97
-
 
 
 
 
98
  $level_order = null;
99
 
100
  if ( isset( $_REQUEST['level_order'] ) && is_array( $_REQUEST['level_order'] ) ) {
86
  * @since 1.8.6
87
  */
88
  function pmpro_get_order_json() {
89
+ // only admins can get this
90
+ if ( ! function_exists( 'current_user_can' ) || ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'pmpro_orders' ) ) ) {
91
+ die( __( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) );
92
+ }
93
+
94
+ $order_id = intval( $_REQUEST['order_id'] );
95
  $order = new MemberOrder($order_id);
96
  echo json_encode($order);
97
  exit;
99
  add_action('wp_ajax_pmpro_get_order_json', 'pmpro_get_order_json');
100
 
101
  function pmpro_update_level_order() {
102
+ // only admins can get this
103
+ if ( ! function_exists( 'current_user_can' ) || ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'pmpro_membershiplevels' ) ) ) {
104
+ die( __( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) );
105
+ }
106
+
107
  $level_order = null;
108
 
109
  if ( isset( $_REQUEST['level_order'] ) && is_array( $_REQUEST['level_order'] ) ) {
includes/setup.sql CHANGED
@@ -45,7 +45,7 @@ CREATE TABLE `wp_pmpro_discount_codes_levels` (
45
  `trial_amount` decimal(18,8) NOT NULL DEFAULT '0.00',
46
  `trial_limit` int(11) NOT NULL DEFAULT '0',
47
  `expiration_number` int(10) unsigned NOT NULL,
48
- `expiration_period` enum('Day','Week','Month','Year') NOT NULL,
49
  PRIMARY KEY (`code_id`,`level_id`),
50
  KEY `initial_payment` (`initial_payment`)
51
  );
@@ -87,7 +87,7 @@ CREATE TABLE `wp_pmpro_membership_levels` (
87
  `trial_limit` int(11) NOT NULL DEFAULT '0',
88
  `allow_signups` tinyint(4) NOT NULL DEFAULT '1',
89
  `expiration_number` int(10) unsigned NOT NULL,
90
- `expiration_period` enum('Day','Week','Month','Year') NOT NULL,
91
  PRIMARY KEY (`id`),
92
  KEY `allow_signups` (`allow_signups`),
93
  KEY `initial_payment` (`initial_payment`),
@@ -112,6 +112,22 @@ CREATE TABLE `wp_pmpro_membership_levelmeta` (
112
 
113
  -- --------------------------------------------------------
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  --
116
  -- Table structure for table `wp_pmpro_membership_orders`
117
  --
@@ -177,7 +193,7 @@ CREATE TABLE `wp_pmpro_memberships_categories` (
177
  `membership_id` int(11) unsigned NOT NULL,
178
  `category_id` int(11) unsigned NOT NULL,
179
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
180
- UNIQUE KEY `membership_category` (`membership_id`,`category_id`),
181
  UNIQUE KEY `category_membership` (`category_id`,`membership_id`)
182
  );
183
 
@@ -191,7 +207,7 @@ CREATE TABLE `wp_pmpro_memberships_pages` (
191
  `membership_id` int(11) unsigned NOT NULL,
192
  `page_id` int(11) unsigned NOT NULL,
193
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
194
- UNIQUE KEY `category_membership` (`page_id`,`membership_id`),
195
  UNIQUE KEY `membership_page` (`membership_id`,`page_id`)
196
  );
197
 
45
  `trial_amount` decimal(18,8) NOT NULL DEFAULT '0.00',
46
  `trial_limit` int(11) NOT NULL DEFAULT '0',
47
  `expiration_number` int(10) unsigned NOT NULL,
48
+ `expiration_period` enum('Hour','Day','Week','Month','Year') NOT NULL,
49
  PRIMARY KEY (`code_id`,`level_id`),
50
  KEY `initial_payment` (`initial_payment`)
51
  );
87
  `trial_limit` int(11) NOT NULL DEFAULT '0',
88
  `allow_signups` tinyint(4) NOT NULL DEFAULT '1',
89
  `expiration_number` int(10) unsigned NOT NULL,
90
+ `expiration_period` enum('Hour','Day','Week','Month','Year') NOT NULL,
91
  PRIMARY KEY (`id`),
92
  KEY `allow_signups` (`allow_signups`),
93
  KEY `initial_payment` (`initial_payment`),
112
 
113
  -- --------------------------------------------------------
114
 
115
+ --
116
+ -- Table structure for table `wp_pmpro_membership_ordermeta`
117
+ --
118
+
119
+ CREATE TABLE `wp_pmpro_membership_ordermeta` (
120
+ `meta_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
121
+ `pmpro_membership_order_id` int(10) unsigned NOT NULL,
122
+ `meta_key` varchar(255) NOT NULL,
123
+ `meta_value` longtext,
124
+ PRIMARY KEY (`meta_id`),
125
+ KEY `pmpro_membership_order_id` (`pmpro_membership_order_id`),
126
+ KEY `meta_key` (`meta_key`)
127
+ );
128
+
129
+ -- --------------------------------------------------------
130
+
131
  --
132
  -- Table structure for table `wp_pmpro_membership_orders`
133
  --
193
  `membership_id` int(11) unsigned NOT NULL,
194
  `category_id` int(11) unsigned NOT NULL,
195
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
196
+ PRIMARY KEY (`membership_id`,`category_id`),
197
  UNIQUE KEY `category_membership` (`category_id`,`membership_id`)
198
  );
199
 
207
  `membership_id` int(11) unsigned NOT NULL,
208
  `page_id` int(11) unsigned NOT NULL,
209
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
210
+ PRIMARY KEY (`page_id`,`membership_id`),
211
  UNIQUE KEY `membership_page` (`membership_id`,`page_id`)
212
  );
213
 
includes/spam.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Code related to spam detection and prevention.
4
+ */
5
+ // Constants. Define these in wp-config.php to override.
6
+ if ( ! defined( 'PMPRO_SPAM_ACTION_NUM_LIMIT' ) ) {
7
+ define( 'PMPRO_SPAM_ACTION_NUM_LIMIT', 10 );
8
+ }
9
+ if ( ! defined( 'PMPRO_SPAM_ACTION_TIME_LIMIT' ) ) {
10
+ define( 'PMPRO_SPAM_ACTION_TIME_LIMIT', 900 ); // in seconds
11
+ }
12
+
13
+ /**
14
+ * Determine whether the current visitor a spammer.
15
+ *
16
+ * @since 2.7
17
+ *
18
+ * @return bool Whether the current visitor a spammer.
19
+ */
20
+ function pmpro_is_spammer() {
21
+ $is_spammer = false;
22
+
23
+ $activity = pmpro_get_spam_activity();
24
+ if ( false !== $activity && count( $activity ) >= PMPRO_SPAM_ACTION_NUM_LIMIT ) {
25
+ $is_spammer = true;
26
+ }
27
+
28
+ /**
29
+ * Allow filtering whether the current visitor is a spammer.
30
+ *
31
+ * @since 2.7
32
+ *
33
+ * @param bool $is_spammer Whether the current visitor is a spammer.
34
+ * @param array $activity The list of potential spam activity.
35
+ */
36
+ return apply_filters( 'pmpro_is_spammer', $is_spammer, $activity );
37
+ }
38
+
39
+ /**
40
+ * Get the list of potential spam activity.
41
+ *
42
+ * @since 2.7
43
+ *
44
+ * @param string|null $ip The IP address to get activity for, or leave as null to attempt to determine current IP address.
45
+ *
46
+ * @return array|false The list of potential spam activity if successful, or false if IP could not be determined.
47
+ */
48
+ function pmpro_get_spam_activity( $ip = null ) {
49
+ if ( empty( $ip ) ) {
50
+ $ip = pmpro_get_ip();
51
+ }
52
+
53
+ // If we can't determine the IP, let's bail.
54
+ if ( empty( $ip ) ) {
55
+ return false;
56
+ }
57
+
58
+ $ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $ip );
59
+ $transient_key = 'pmpro_spam_activity_' . $ip;
60
+ $activity = get_transient( $transient_key );
61
+ if ( empty( $activity ) || ! is_array( $activity ) ) {
62
+ $activity = [];
63
+ }
64
+
65
+ // Remove old items.
66
+ $new_activity = [];
67
+ $now = current_time( 'timestamp', true ); // UTC
68
+ foreach( $activity as $item ) {
69
+ // Determine whether this item is recent enough to include.
70
+ if ( $item > $now-( PMPRO_SPAM_ACTION_TIME_LIMIT ) ) {
71
+ $new_activity[] = $item;
72
+ }
73
+ }
74
+
75
+ return $new_activity;
76
+ }
77
+
78
+ /**
79
+ * Track spam activity.
80
+ * When we hit a certain number, the spam flag will trigger.
81
+ * For now we are only tracking credit card declines their timestamps.
82
+ * IP address isn't a perfect way to track this, but it's the best we have.
83
+ *
84
+ * @since 2.7
85
+ *
86
+ * @param string|null $ip The IP address to track activity for, or leave as null to attempt to determine current IP address.
87
+ *
88
+ * @return bool True if the tracking of activity was successful, or false if IP could not be determined.
89
+ */
90
+ function pmpro_track_spam_activity( $ip = null ) {
91
+ if ( empty( $ip ) ) {
92
+ $ip = pmpro_get_ip();
93
+ }
94
+
95
+ // If we can't determine the IP, let's bail.
96
+ if ( empty( $ip ) ) {
97
+ return false;
98
+ }
99
+
100
+ $activity = pmpro_get_spam_activity( $ip );
101
+ $now = current_time( 'timestamp', true ); // UTC
102
+ array_unshift( $activity, $now );
103
+
104
+ // If we have more than the limit, don't bother storing them.
105
+ if ( count( $activity ) > PMPRO_SPAM_ACTION_NUM_LIMIT ) {
106
+ rsort( $activity );
107
+ $activity = array_slice( $activity, 0, PMPRO_SPAM_ACTION_NUM_LIMIT );
108
+ }
109
+
110
+ // Save to transient.
111
+ $ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $ip );
112
+ $transient_key = 'pmpro_spam_activity_' . $ip;
113
+ set_transient( $transient_key, $activity, (int) PMPRO_SPAM_ACTION_TIME_LIMIT );
114
+
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * Clears all stored spam activity for an IP address.
120
+ * Note that the pmpro_get_spam_activity function clears out old values
121
+ * automatically, and this should only be used to completely clear the activity.
122
+ *
123
+ * @since 2.7
124
+ *
125
+ * @param string|null $ip The IP address to clear activity for, or leave as null to attempt to determine current IP address.
126
+ *
127
+ * @return bool True if the clearing of activity was successful, or false if IP could not be determined.
128
+ */
129
+ function pmpro_clear_spam_activity( $ip = null ) {
130
+ if ( empty( $ip ) ) {
131
+ $ip = pmpro_get_ip();
132
+ }
133
+
134
+ // If we can't determine the IP, let's bail.
135
+ if ( empty( $ip ) ) {
136
+ return false;
137
+ }
138
+
139
+ $transient_key = 'pmpro_spam_activity_' . $ip;
140
+
141
+ delete_transient( $transient_key );
142
+
143
+ return true;
144
+ }
145
+
146
+ /**
147
+ * Track spam activity when checkouts or billing updates fail.
148
+ *
149
+ * @since 2.7
150
+ * @param MemberOrder $morder The order object used at checkout. We ignore it.
151
+ */
152
+ function pmpro_track_failed_checkouts_for_spam( $morder ) {
153
+ // Bail if Spam Protection is disabled.
154
+ $spamprotection = pmpro_getOption("spamprotection");
155
+ if ( empty( $spamprotection ) ) {
156
+ return;
157
+ }
158
+
159
+ pmpro_track_spam_activity();
160
+ }
161
+ add_action( 'pmpro_checkout_processing_failed', 'pmpro_track_failed_checkouts_for_spam' );
162
+ add_action( 'pmpro_update_billing_failed', 'pmpro_track_failed_checkouts_for_spam' );
163
+
164
+ /**
165
+ * Disable checkout and billing update forms for spammers.
166
+ *
167
+ * We're using the pmpro_required_billing_fields filter because it is
168
+ * consistently used on the checkout and update billing pages.
169
+ * The pmpro_setMessage() function sets the $pmpro_msgt value to error,
170
+ * which stops the forms from working and shows the corresponding error.
171
+ * We return the $required_fields parameter to keep the filter working.
172
+ *
173
+ * @since 2.7
174
+ *
175
+ * @param array $required_fields The list of required fields.
176
+ *
177
+ * @return array The list of required fields.
178
+ */
179
+ function pmpro_disable_checkout_for_spammers( $required_fields ) {
180
+ // Bail if Spam Protection is disabled.
181
+ $spamprotection = pmpro_getOption("spamprotection");
182
+ if ( empty( $spamprotection ) ) {
183
+ return $required_fields;
184
+ }
185
+
186
+ if ( pmpro_was_checkout_form_submitted() && pmpro_is_spammer() ) {
187
+ pmpro_setMessage( __( 'Suspicious activity detected. Try again in a few minutes.', 'paid-memberships-pro' ), 'pmpro_error' );
188
+ }
189
+
190
+ return $required_fields;
191
+ }
192
+ add_filter( 'pmpro_required_billing_fields', 'pmpro_disable_checkout_for_spammers' );
includes/updates/upgrade_2_4.php CHANGED
@@ -32,16 +32,7 @@ function pmpro_upgrade_2_4() {
32
  pmpro_addUpdate( 'pmpro_upgrade_2_4_ajax' );
33
  } else {
34
  //less than 10, let's just do them now
35
- $stripe = new PMProGateway_stripe();
36
- require_once( ABSPATH . "/wp-includes/pluggable.php" );
37
- foreach($orders as $order) {
38
- $subscription = $stripe->getSubscription( $order );
39
-
40
- if ( ! empty( $subscription ) ) {
41
- $sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET subscription_transaction_id = '" . esc_sql( $subscription->id ) . "' WHERE id = '" . esc_sql( $order->id ) . "' LIMIT 1";
42
- $wpdb->query( $sqlQuery );
43
- }
44
- }
45
  }
46
  }
47
 
@@ -75,20 +66,55 @@ function pmpro_upgrade_2_4_ajax() {
75
  pmpro_removeUpdate('pmpro_upgrade_2_4_ajax');
76
  delete_option( 'pmpro_upgrade_2_4_last_order_id' );
77
  } else {
78
- //less than 10, let's just do them now
79
- $stripe = new PMProGateway_stripe();
80
- require_once( ABSPATH . "/wp-includes/pluggable.php" );
81
- foreach($orders as $order) {
82
- $subscription = $stripe->getSubscription( $order );
83
-
84
- if ( ! empty( $subscription ) ) {
85
- $sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET subscription_transaction_id = '" . esc_sql( $subscription->id ) . "' WHERE id = '" . esc_sql( $order->id ) . "' LIMIT 1";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  $wpdb->query( $sqlQuery );
 
87
  }
88
-
89
- $last_order_id = $order->id;
90
  }
 
 
91
 
 
92
  update_option( 'pmpro_upgrade_2_4_last_order_id', $last_order_id );
93
  }
94
  }
32
  pmpro_addUpdate( 'pmpro_upgrade_2_4_ajax' );
33
  } else {
34
  //less than 10, let's just do them now
35
+ pmpro_upgrade_2_4_helper_get_subscriptions_for_orders( $orders, false );
 
 
 
 
 
 
 
 
 
36
  }
37
  }
38
 
66
  pmpro_removeUpdate('pmpro_upgrade_2_4_ajax');
67
  delete_option( 'pmpro_upgrade_2_4_last_order_id' );
68
  } else {
69
+ pmpro_upgrade_2_4_helper_get_subscriptions_for_orders( $orders, true );
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Populate subscription_ids for Stripe orders.
75
+ *
76
+ * @param array $orders to find subscription_id for.
77
+ * @param bool $update_last_order_id. Should be true if updating via ajax.
78
+ */
79
+ function pmpro_upgrade_2_4_helper_get_subscriptions_for_orders( $orders, $update_last_order_id ) {
80
+ global $wpdb;
81
+ $stripe = new PMProGateway_stripe();
82
+ require_once( ABSPATH . "/wp-includes/pluggable.php" );
83
+ foreach($orders as $order) {
84
+ if ( empty( $order->code ) ) {
85
+ continue;
86
+ }
87
+ $customer = $stripe->get_customer_for_user( $order->user_id );
88
+
89
+ //no customer or no subscriptions?
90
+ if ( empty( $customer ) || empty( $customer->subscriptions ) ) {
91
+ continue;
92
+ }
93
+
94
+ //find subscription based on customer id and order/plan id
95
+ $subscriptions = $customer->subscriptions->all();
96
+
97
+
98
+ //no subscriptions
99
+ if ( empty( $subscriptions ) || empty( $subscriptions->data ) ) {
100
+ return false;
101
+ }
102
+
103
+ //we really want to test against the order codes of all orders with the same subscription_transaction_id (customer id)
104
+ $codes = $wpdb->get_col( "SELECT code FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . esc_sql( $order->user_id ) . "' AND subscription_transaction_id = '' AND status NOT IN('refunded', 'review', 'token', 'error')" );
105
+
106
+ //find the one for this order
107
+ foreach ( $subscriptions->data as $sub ) {
108
+ if ( in_array( $sub->plan->id, $codes ) ) {
109
+ $sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET subscription_transaction_id = '" . esc_sql( $sub->id ) . "' WHERE id = '" . esc_sql( $order->id ) . "' LIMIT 1";
110
  $wpdb->query( $sqlQuery );
111
+ break;
112
  }
 
 
113
  }
114
+ $last_order_id = $order->id;
115
+ }
116
 
117
+ if ( $update_last_order_id ) {
118
  update_option( 'pmpro_upgrade_2_4_last_order_id', $last_order_id );
119
  }
120
  }
includes/updates/upgrade_2_6.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Upgrade to 2.6
4
+ * We changed the pmpro_cron_expire_memberships cron
5
+ * to run hourly instead of daily.
6
+ * To ensure that existing members still expire at least
7
+ * 1 calendar day after their expiration date, we are
8
+ * updating old expiration date timestamps to set
9
+ * the time component to 11:59. This way e.g.
10
+ * someone who checked out at 3pm on Dec 31 won't expire
11
+ * until Jan 1 at midnight.
12
+ * Going forward, we will always set the expiration time to 11:59
13
+ * unless the level is set up to expire hourly.
14
+ */
15
+ function pmpro_upgrade_2_6() {
16
+ // Map email settings to new email template settings.
17
+ // Note: the old settings were true to enable, the new settings are true to disable.
18
+ $admin_checkout = pmpro_getOption( 'email_admin_checkout' );
19
+ if ( empty( $admin_checkout ) ) {
20
+ pmpro_setOption( 'email_checkout_check_admin_disabled', 'true' );
21
+ pmpro_setOption( 'email_checkout_express_admin_disabled', 'true' );
22
+ pmpro_setOption( 'email_checkout_free_admin_disabled', 'true' );
23
+ pmpro_setOption( 'email_checkout_freetrial_admin_disabled', 'true' );
24
+ pmpro_setOption( 'email_checkout_paid_admin_disabled', 'true' );
25
+ pmpro_setOption( 'email_checkout_trial_admin_disabled', 'true' );
26
+ }
27
+ $admin_changes = pmpro_getOption( 'email_admin_changes' );
28
+ if ( empty( $admin_changes ) ) {
29
+ pmpro_setOption( 'email_admin_change_admin_disabled', 'true' );
30
+ }
31
+ $admin_cancels = pmpro_getOption( 'email_admin_cancels' );
32
+ if ( empty( $admin_cancels ) ) {
33
+ pmpro_setOption( 'email_cancel_admin_disabled', 'true' );
34
+ }
35
+ $admin_billing = pmpro_getOption( 'email_admin_billing' );
36
+ if ( empty( $admin_billing ) ) {
37
+ pmpro_setOption( 'email_billing_admin_disabled', 'true' );
38
+ }
39
+
40
+ // Reschedule cron job for hourly checks.
41
+ $next = wp_next_scheduled( 'pmpro_cron_expire_memberships' );
42
+ if ( ! empty( $next ) ) {
43
+ wp_unschedule_event( $next, 'pmpro_cron_expire_memberships' );
44
+ }
45
+ pmpro_maybe_schedule_event( current_time( 'timestamp' ), 'hourly', 'pmpro_cron_expire_memberships' );
46
+ }
includes/upgradecheck.php CHANGED
@@ -135,7 +135,7 @@ function pmpro_checkForUpgrades()
135
 
136
  //fix subscription ids on stripe orders
137
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_6_9.php"); //need to include this for AJAX calls
138
- if($pmpro_db_version < 1.869) {
139
  $pmpro_db_version = pmpro_upgrade_1_8_6_9();
140
  }
141
 
@@ -144,27 +144,27 @@ function pmpro_checkForUpgrades()
144
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_7.php");
145
  $pmpro_db_version = pmpro_upgrade_1_8_7();
146
  }
147
-
148
  /*
149
  v1.8.8
150
  * Running the cron job cleanup again.
151
  * Fixing old $0 Stripe orders.
152
  * Fixing old Authorize.net orders with empty status.
153
- */
154
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_8.php");
155
- if($pmpro_db_version < 1.88) {
156
- $pmpro_db_version = pmpro_upgrade_1_8_8();
157
  }
158
-
159
  /*
160
  v1.8.9.1
161
  * Fixing Stripe orders where user_id/membership_id = 0
162
  * Updated in v1.9.2.2 to check for namespace compatibility first,
163
  since the Stripe class isn't loaded for PHP < 5.3.29
164
- */
165
  if (version_compare( PHP_VERSION, '5.3.29', '>=' )) {
166
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_9_1.php");
167
- if($pmpro_db_version < 1.891) {
168
  $pmpro_db_version = pmpro_upgrade_1_8_9_1();
169
  }
170
  } elseif($pmpro_db_version < 1.891) {
@@ -177,18 +177,18 @@ function pmpro_checkForUpgrades()
177
  */
178
  if($pmpro_db_version < 1.892) {
179
  pmpro_db_delta();
180
-
181
  $pmpro_db_version = 1.892;
182
  pmpro_setOption("db_version", "1.892");
183
  }
184
 
185
  /*
186
  v1.8.9.3 (db v1.91)
187
- * Fixing incorrect start and end dates.
188
  */
189
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_9_3.php");
190
  if($pmpro_db_version < 1.91) {
191
- $pmpro_db_version = pmpro_upgrade_1_8_9_3();
192
  }
193
 
194
  /*
@@ -198,7 +198,7 @@ function pmpro_checkForUpgrades()
198
  */
199
  if($pmpro_db_version < 1.92) {
200
  pmpro_db_delta();
201
-
202
  $pmpro_db_version = 1.92;
203
  pmpro_setOption("db_version", "1.92");
204
  }
@@ -210,16 +210,16 @@ function pmpro_checkForUpgrades()
210
  */
211
  if($pmpro_db_version < 1.93) {
212
  pmpro_db_delta();
213
-
214
  $pmpro_db_version = 1.93;
215
  pmpro_setOption("db_version", "1.93");
216
  }
217
 
218
- require_once( PMPRO_DIR . "/includes/updates/upgrade_1_9_4.php" );
219
  if($pmpro_db_version < 1.94) {
220
  $pmpro_db_version = pmpro_upgrade_1_9_4();
221
  }
222
-
223
  if($pmpro_db_version < 1.944) {
224
  pmpro_cleanup_memberships_users_table();
225
  $pmpro_db_version = '1.944';
@@ -232,21 +232,51 @@ function pmpro_checkForUpgrades()
232
  $pmpro_db_version = 2.1;
233
  pmpro_setOption( 'db_version', '2.1' );
234
  }
235
-
236
  if ( $pmpro_db_version < 2.3 ) {
237
  pmpro_maybe_schedule_event( strtotime( '10:30:00' ) - ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ), 'daily', 'pmpro_cron_admin_activity_email' );
238
  pmpro_setOption( 'db_version', '2.3' );
239
  }
240
-
241
  /**
242
  * Version 2.4
243
  * Fixing subscription_transaction_id
244
  * for orders created through a Stripe Update.
245
  */
246
- require_once( PMPRO_DIR . "/includes/updates/upgrade_2_4.php" );
247
  if($pmpro_db_version < 2.4) {
248
  $pmpro_db_version = pmpro_upgrade_2_4();
249
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  }
251
 
252
  function pmpro_db_delta()
@@ -264,6 +294,7 @@ function pmpro_db_delta()
264
  $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels';
265
  $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses';
266
  $wpdb->pmpro_membership_levelmeta = $wpdb->prefix . 'pmpro_membership_levelmeta';
 
267
 
268
  //wp_pmpro_membership_levels
269
  $sqlQuery = "
@@ -281,7 +312,7 @@ function pmpro_db_delta()
281
  `trial_limit` int(11) NOT NULL DEFAULT '0',
282
  `allow_signups` tinyint(4) NOT NULL DEFAULT '1',
283
  `expiration_number` int(10) unsigned NOT NULL,
284
- `expiration_period` enum('Day','Week','Month','Year') NOT NULL,
285
  PRIMARY KEY (`id`),
286
  KEY `allow_signups` (`allow_signups`),
287
  KEY `initial_payment` (`initial_payment`),
@@ -293,10 +324,10 @@ function pmpro_db_delta()
293
  //wp_pmpro_membership_orders
294
  $sqlQuery = "
295
  CREATE TABLE `" . $wpdb->pmpro_membership_orders . "` (
296
- `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
297
  `code` varchar(32) NOT NULL,
298
  `session_id` varchar(64) NOT NULL DEFAULT '',
299
- `user_id` int(11) unsigned NOT NULL DEFAULT '0',
300
  `membership_id` int(11) unsigned NOT NULL DEFAULT '0',
301
  `paypal_token` varchar(64) NOT NULL DEFAULT '',
302
  `billing_name` varchar(128) NOT NULL DEFAULT '',
@@ -309,7 +340,7 @@ function pmpro_db_delta()
309
  `subtotal` varchar(16) NOT NULL DEFAULT '',
310
  `tax` varchar(16) NOT NULL DEFAULT '',
311
  `couponamount` varchar(16) NOT NULL DEFAULT '',
312
- `checkout_id` int(11) NOT NULL DEFAULT '0',
313
  `certificate_id` int(11) NOT NULL DEFAULT '0',
314
  `certificateamount` varchar(16) NOT NULL DEFAULT '',
315
  `total` varchar(16) NOT NULL DEFAULT '',
@@ -349,9 +380,9 @@ function pmpro_db_delta()
349
  $sqlQuery = "
350
  CREATE TABLE `" . $wpdb->pmpro_memberships_categories . "` (
351
  `membership_id` int(11) unsigned NOT NULL,
352
- `category_id` int(11) unsigned NOT NULL,
353
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
354
- UNIQUE KEY `membership_category` (`membership_id`,`category_id`),
355
  UNIQUE KEY `category_membership` (`category_id`,`membership_id`)
356
  );
357
  ";
@@ -361,9 +392,9 @@ function pmpro_db_delta()
361
  $sqlQuery = "
362
  CREATE TABLE `" . $wpdb->pmpro_memberships_pages . "` (
363
  `membership_id` int(11) unsigned NOT NULL,
364
- `page_id` int(11) unsigned NOT NULL,
365
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
366
- UNIQUE KEY `category_membership` (`page_id`,`membership_id`),
367
  UNIQUE KEY `membership_page` (`membership_id`,`page_id`)
368
  );
369
  ";
@@ -373,9 +404,9 @@ function pmpro_db_delta()
373
  $sqlQuery = "
374
  CREATE TABLE `" . $wpdb->pmpro_memberships_users . "` (
375
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
376
- `user_id` int(11) unsigned NOT NULL,
377
  `membership_id` int(11) unsigned NOT NULL,
378
- `code_id` int(11) unsigned NOT NULL,
379
  `initial_payment` decimal(18,8) NOT NULL,
380
  `billing_amount` decimal(18,8) NOT NULL,
381
  `cycle_number` int(11) NOT NULL,
@@ -401,7 +432,7 @@ function pmpro_db_delta()
401
  //wp_pmpro_discount_codes
402
  $sqlQuery = "
403
  CREATE TABLE `" . $wpdb->pmpro_discount_codes . "` (
404
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
405
  `code` varchar(32) NOT NULL,
406
  `starts` date NOT NULL,
407
  `expires` date NOT NULL,
@@ -417,7 +448,7 @@ function pmpro_db_delta()
417
  //wp_pmpro_discount_codes_levels
418
  $sqlQuery = "
419
  CREATE TABLE `" . $wpdb->pmpro_discount_codes_levels . "` (
420
- `code_id` int(11) unsigned NOT NULL,
421
  `level_id` int(11) unsigned NOT NULL,
422
  `initial_payment` decimal(18,8) NOT NULL DEFAULT '0.00',
423
  `billing_amount` decimal(18,8) NOT NULL DEFAULT '0.00',
@@ -427,7 +458,7 @@ function pmpro_db_delta()
427
  `trial_amount` decimal(18,8) NOT NULL DEFAULT '0.00',
428
  `trial_limit` int(11) NOT NULL DEFAULT '0',
429
  `expiration_number` int(10) unsigned NOT NULL,
430
- `expiration_period` enum('Day','Week','Month','Year') NOT NULL,
431
  PRIMARY KEY (`code_id`,`level_id`),
432
  KEY `initial_payment` (`initial_payment`)
433
  );
@@ -437,10 +468,10 @@ function pmpro_db_delta()
437
  //wp_pmpro_discount_codes_uses
438
  $sqlQuery = "
439
  CREATE TABLE `" . $wpdb->pmpro_discount_codes_uses . "` (
440
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
441
- `code_id` int(10) unsigned NOT NULL,
442
- `user_id` int(10) unsigned NOT NULL,
443
- `order_id` int(10) unsigned NOT NULL,
444
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
445
  PRIMARY KEY (`id`),
446
  KEY `user_id` (`user_id`),
@@ -452,8 +483,8 @@ function pmpro_db_delta()
452
  //pmpro_membership_levelmeta
453
  $sqlQuery = "
454
  CREATE TABLE `" . $wpdb->pmpro_membership_levelmeta . "` (
455
- `meta_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
456
- `pmpro_membership_level_id` int(10) unsigned NOT NULL,
457
  `meta_key` varchar(255) NOT NULL,
458
  `meta_value` longtext,
459
  PRIMARY KEY (`meta_id`),
@@ -462,4 +493,18 @@ function pmpro_db_delta()
462
  );
463
  ";
464
  dbDelta($sqlQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  }
135
 
136
  //fix subscription ids on stripe orders
137
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_6_9.php"); //need to include this for AJAX calls
138
+ if($pmpro_db_version < 1.869) {
139
  $pmpro_db_version = pmpro_upgrade_1_8_6_9();
140
  }
141
 
144
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_7.php");
145
  $pmpro_db_version = pmpro_upgrade_1_8_7();
146
  }
147
+
148
  /*
149
  v1.8.8
150
  * Running the cron job cleanup again.
151
  * Fixing old $0 Stripe orders.
152
  * Fixing old Authorize.net orders with empty status.
153
+ */
154
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_8.php");
155
+ if($pmpro_db_version < 1.88) {
156
+ $pmpro_db_version = pmpro_upgrade_1_8_8();
157
  }
158
+
159
  /*
160
  v1.8.9.1
161
  * Fixing Stripe orders where user_id/membership_id = 0
162
  * Updated in v1.9.2.2 to check for namespace compatibility first,
163
  since the Stripe class isn't loaded for PHP < 5.3.29
164
+ */
165
  if (version_compare( PHP_VERSION, '5.3.29', '>=' )) {
166
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_9_1.php");
167
+ if($pmpro_db_version < 1.891) {
168
  $pmpro_db_version = pmpro_upgrade_1_8_9_1();
169
  }
170
  } elseif($pmpro_db_version < 1.891) {
177
  */
178
  if($pmpro_db_version < 1.892) {
179
  pmpro_db_delta();
180
+
181
  $pmpro_db_version = 1.892;
182
  pmpro_setOption("db_version", "1.892");
183
  }
184
 
185
  /*
186
  v1.8.9.3 (db v1.91)
187
+ * Fixing incorrect start and end dates.
188
  */
189
  require_once(PMPRO_DIR . "/includes/updates/upgrade_1_8_9_3.php");
190
  if($pmpro_db_version < 1.91) {
191
+ $pmpro_db_version = pmpro_upgrade_1_8_9_3();
192
  }
193
 
194
  /*
198
  */
199
  if($pmpro_db_version < 1.92) {
200
  pmpro_db_delta();
201
+
202
  $pmpro_db_version = 1.92;
203
  pmpro_setOption("db_version", "1.92");
204
  }
210
  */
211
  if($pmpro_db_version < 1.93) {
212
  pmpro_db_delta();
213
+
214
  $pmpro_db_version = 1.93;
215
  pmpro_setOption("db_version", "1.93");
216
  }
217
 
218
+ require_once( PMPRO_DIR . "/includes/updates/upgrade_1_9_4.php" );
219
  if($pmpro_db_version < 1.94) {
220
  $pmpro_db_version = pmpro_upgrade_1_9_4();
221
  }
222
+
223
  if($pmpro_db_version < 1.944) {
224
  pmpro_cleanup_memberships_users_table();
225
  $pmpro_db_version = '1.944';
232
  $pmpro_db_version = 2.1;
233
  pmpro_setOption( 'db_version', '2.1' );
234
  }
235
+
236
  if ( $pmpro_db_version < 2.3 ) {
237
  pmpro_maybe_schedule_event( strtotime( '10:30:00' ) - ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ), 'daily', 'pmpro_cron_admin_activity_email' );
238
  pmpro_setOption( 'db_version', '2.3' );
239
  }
240
+
241
  /**
242
  * Version 2.4
243
  * Fixing subscription_transaction_id
244
  * for orders created through a Stripe Update.
245
  */
246
+ require_once( PMPRO_DIR . "/includes/updates/upgrade_2_4.php" );
247
  if($pmpro_db_version < 2.4) {
248
  $pmpro_db_version = pmpro_upgrade_2_4();
249
  }
250
+
251
+ /**
252
+ * Version 2.5
253
+ * Running pmpro_db_delta to install the ordermeta table.
254
+ */
255
+ if( $pmpro_db_version < 2.5 ) {
256
+ pmpro_db_delta();
257
+ $pmpro_db_version = 2.5;
258
+ pmpro_setOption( 'db_version', '2.5' );
259
+ }
260
+
261
+ /**
262
+ * Version 2.6
263
+ * Running pmpro_db_delta to update column types to bigint/etc
264
+ */
265
+ require_once( PMPRO_DIR . "/includes/updates/upgrade_2_6.php" );
266
+ if( $pmpro_db_version < 2.6 ) {
267
+ pmpro_db_delta();
268
+ $pmpro_db_version = pmpro_upgrade_2_6();
269
+ pmpro_setOption( 'db_version', '2.6' );
270
+ }
271
+
272
+ /**
273
+ * Version 2.7.1
274
+ * Running pmpro_db_delta to fix the primary key in a couple tables.
275
+ */
276
+ if( $pmpro_db_version < 2.71 ) {
277
+ pmpro_db_delta();
278
+ pmpro_setOption( 'db_version', '2.71' );
279
+ }
280
  }
281
 
282
  function pmpro_db_delta()
294
  $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels';
295
  $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses';
296
  $wpdb->pmpro_membership_levelmeta = $wpdb->prefix . 'pmpro_membership_levelmeta';
297
+ $wpdb->pmpro_membership_ordermeta = $wpdb->prefix . 'pmpro_membership_ordermeta';
298
 
299
  //wp_pmpro_membership_levels
300
  $sqlQuery = "
312
  `trial_limit` int(11) NOT NULL DEFAULT '0',
313
  `allow_signups` tinyint(4) NOT NULL DEFAULT '1',
314
  `expiration_number` int(10) unsigned NOT NULL,
315
+ `expiration_period` enum('Hour','Day','Week','Month','Year') NOT NULL,
316
  PRIMARY KEY (`id`),
317
  KEY `allow_signups` (`allow_signups`),
318
  KEY `initial_payment` (`initial_payment`),
324
  //wp_pmpro_membership_orders
325
  $sqlQuery = "
326
  CREATE TABLE `" . $wpdb->pmpro_membership_orders . "` (
327
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
328
  `code` varchar(32) NOT NULL,
329
  `session_id` varchar(64) NOT NULL DEFAULT '',
330
+ `user_id` bigint(20) unsigned NOT NULL DEFAULT '0',
331
  `membership_id` int(11) unsigned NOT NULL DEFAULT '0',
332
  `paypal_token` varchar(64) NOT NULL DEFAULT '',
333
  `billing_name` varchar(128) NOT NULL DEFAULT '',
340
  `subtotal` varchar(16) NOT NULL DEFAULT '',
341
  `tax` varchar(16) NOT NULL DEFAULT '',
342
  `couponamount` varchar(16) NOT NULL DEFAULT '',
343
+ `checkout_id` bigint(20) NOT NULL DEFAULT '0',
344
  `certificate_id` int(11) NOT NULL DEFAULT '0',
345
  `certificateamount` varchar(16) NOT NULL DEFAULT '',
346
  `total` varchar(16) NOT NULL DEFAULT '',
380
  $sqlQuery = "
381
  CREATE TABLE `" . $wpdb->pmpro_memberships_categories . "` (
382
  `membership_id` int(11) unsigned NOT NULL,
383
+ `category_id` bigint(20) unsigned NOT NULL,
384
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
385
+ PRIMARY KEY (`membership_id`,`category_id`),
386
  UNIQUE KEY `category_membership` (`category_id`,`membership_id`)
387
  );
388
  ";
392
  $sqlQuery = "
393
  CREATE TABLE `" . $wpdb->pmpro_memberships_pages . "` (
394
  `membership_id` int(11) unsigned NOT NULL,
395
+ `page_id` bigint(20) unsigned NOT NULL,
396
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
397
+ PRIMARY KEY (`page_id`,`membership_id`),
398
  UNIQUE KEY `membership_page` (`membership_id`,`page_id`)
399
  );
400
  ";
404
  $sqlQuery = "
405
  CREATE TABLE `" . $wpdb->pmpro_memberships_users . "` (
406
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
407
+ `user_id` bigint(20) unsigned NOT NULL,
408
  `membership_id` int(11) unsigned NOT NULL,
409
+ `code_id` bigint(20) unsigned NOT NULL,
410
  `initial_payment` decimal(18,8) NOT NULL,
411
  `billing_amount` decimal(18,8) NOT NULL,
412
  `cycle_number` int(11) NOT NULL,
432
  //wp_pmpro_discount_codes
433
  $sqlQuery = "
434
  CREATE TABLE `" . $wpdb->pmpro_discount_codes . "` (
435
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
436
  `code` varchar(32) NOT NULL,
437
  `starts` date NOT NULL,
438
  `expires` date NOT NULL,
448
  //wp_pmpro_discount_codes_levels
449
  $sqlQuery = "
450
  CREATE TABLE `" . $wpdb->pmpro_discount_codes_levels . "` (
451
+ `code_id` bigint(20) unsigned NOT NULL,
452
  `level_id` int(11) unsigned NOT NULL,
453
  `initial_payment` decimal(18,8) NOT NULL DEFAULT '0.00',
454
  `billing_amount` decimal(18,8) NOT NULL DEFAULT '0.00',
458
  `trial_amount` decimal(18,8) NOT NULL DEFAULT '0.00',
459
  `trial_limit` int(11) NOT NULL DEFAULT '0',
460
  `expiration_number` int(10) unsigned NOT NULL,
461
+ `expiration_period` enum('Hour','Day','Week','Month','Year') NOT NULL,
462
  PRIMARY KEY (`code_id`,`level_id`),
463
  KEY `initial_payment` (`initial_payment`)
464
  );
468
  //wp_pmpro_discount_codes_uses
469
  $sqlQuery = "
470
  CREATE TABLE `" . $wpdb->pmpro_discount_codes_uses . "` (
471
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
472
+ `code_id` bigint(20) unsigned NOT NULL,
473
+ `user_id` bigint(20) unsigned NOT NULL,
474
+ `order_id` bigint(20) unsigned NOT NULL,
475
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
476
  PRIMARY KEY (`id`),
477
  KEY `user_id` (`user_id`),
483
  //pmpro_membership_levelmeta
484
  $sqlQuery = "
485
  CREATE TABLE `" . $wpdb->pmpro_membership_levelmeta . "` (
486
+ `meta_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
487
+ `pmpro_membership_level_id` int(11) unsigned NOT NULL,
488
  `meta_key` varchar(255) NOT NULL,
489
  `meta_value` longtext,
490
  PRIMARY KEY (`meta_id`),
493
  );
494
  ";
495
  dbDelta($sqlQuery);
496
+
497
+ //pmpro_membership_ordermeta
498
+ $sqlQuery = "
499
+ CREATE TABLE `" . $wpdb->pmpro_membership_ordermeta . "` (
500
+ `meta_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
501
+ `pmpro_membership_order_id` int(11) unsigned NOT NULL,
502
+ `meta_key` varchar(255) NOT NULL,
503
+ `meta_value` longtext,
504
+ PRIMARY KEY (`meta_id`),
505
+ KEY `pmpro_membership_order_id` (`pmpro_membership_order_id`),
506
+ KEY `meta_key` (`meta_key`)
507
+ );
508
+ ";
509
+ dbDelta($sqlQuery);
510
  }
js/blocks.build.js CHANGED
@@ -1 +1,1883 @@
1
- !function(e){var t={};function r(c){if(t[c])return t[c].exports;var o=t[c]={i:c,l:!1,exports:{}};return e[c].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,c){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:c})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var c=Object.create(null);if(r.r(c),Object.defineProperty(c,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(c,o,function(t){return e[t]}.bind(null,o));return c},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=11)}([function(e,t){!function(){e.exports=this.wp.element}()},function(e,t){function r(t){return e.exports=r=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},r(t)}e.exports=r},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t){function r(e,t){for(var r=0;r<t.length;r++){var c=t[r];c.enumerable=c.enumerable||!1,c.configurable=!0,"value"in c&&(c.writable=!0),Object.defineProperty(e,c.key,c)}}e.exports=function(e,t,c){return t&&r(e.prototype,t),c&&r(e,c),e}},function(e,t,r){var c=r(8);e.exports=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&c(e,t)}},function(e,t,r){var c=r(9),o=r(10);e.exports=function(e,t){return!t||"object"!==c(t)&&"function"!=typeof t?o(e):t}},function(e,t){e.exports=function(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}},function(e,t){wp.i18n.setLocaleData({"":{}},"paid-memberships-pro")},function(e,t){function r(t,c){return e.exports=r=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},r(t,c)}e.exports=r},function(e,t){function r(t){return"function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?e.exports=r=function(e){return typeof e}:e.exports=r=function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(t)}e.exports=r},function(e,t){e.exports=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}},function(e,t,r){"use strict";r.r(t);var c=r(0),o=(r(7),r(6)),n=r.n(o),p=r(2),s=r.n(p),i=r(3),l=r.n(i),a=r(4),m=r.n(a),u=r(5),b=r.n(u),f=r(1),d=r.n(f);function h(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,c=d()(e);if(t){var o=d()(this).constructor;r=Reflect.construct(c,arguments,o)}else r=c.apply(this,arguments);return b()(this,r)}}var y=wp.i18n.__,v=wp.element.Component,g=wp.components,O=g.PanelBody,k=g.TextControl,j=g.SelectControl,w=wp.blockEditor.InspectorControls,C=function(e){m()(r,e);var t=h(r);function r(){return s()(this,r),t.apply(this,arguments)}return l()(r,[{key:"render",value:function(){var e=this.props,t=e.attributes,r=t.text,o=t.level,n=t.css_class,p=e.setAttributes;return Object(c.createElement)(w,null,Object(c.createElement)(O,null,Object(c.createElement)(k,{label:y("Button Text","paid-memberships-pro"),help:y("Text for checkout button","paid-memberships-pro"),value:r,onChange:function(e){return p({text:e})}})),Object(c.createElement)(O,null,Object(c.createElement)(j,{label:y("Level","paid-memberships-pro"),help:y("The level to link to for checkout button","paid-memberships-pro"),value:o,onChange:function(e){return p({level:e})},options:window.pmpro.all_level_values_and_labels})),Object(c.createElement)(O,null,Object(c.createElement)(k,{label:y("CSS Class","paid-memberships-pro"),help:y("Additional styling for checkout button","paid-memberships-pro"),value:n,onChange:function(e){return p({css_class:e})}})))}}]),r}(v);function E(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);t&&(c=c.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,c)}return r}function _(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?E(Object(r),!0).forEach((function(t){n()(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):E(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}var P=wp.i18n.__,M=wp.blocks.registerBlockType,N=wp.components,S=N.TextControl,x=N.SelectControl;M("pmpro/checkout-button",{title:P("Membership Checkout Button","paid-memberships-pro"),description:P("Displays a button-styled link to Membership Checkout for the specified level.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"migrate"},keywords:[P("pmpro","paid-memberships-pro"),P("buy","paid-memberships-pro"),P("level","paid-memberships-pro")],supports:{},attributes:{text:{type:"string",default:"Buy Now"},css_class:{type:"string",default:"pmpro_btn"},level:{type:"string"}},edit:function(e){var t=e.attributes,r=t.text,o=t.level,n=t.css_class,p=e.className,s=e.setAttributes,i=e.isSelected;return[i&&Object(c.createElement)(C,_({setAttributes:s},e)),Object(c.createElement)("div",{className:p},Object(c.createElement)("a",{class:n},r)),i&&Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)(S,{label:P("Button Text","paid-memberships-pro"),value:r,onChange:function(e){return s({text:e})}}),Object(c.createElement)(x,{label:P("Membership Level","paid-memberships-pro"),value:o,onChange:function(e){return s({level:e})},options:window.pmpro.all_level_values_and_labels}),Object(c.createElement)(S,{label:P("CSS Class","paid-memberships-pro"),value:n,onChange:function(e){return s({css_class:e})}}))]},save:function(){return null}});function D(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,c=d()(e);if(t){var o=d()(this).constructor;r=Reflect.construct(c,arguments,o)}else r=c.apply(this,arguments);return b()(this,r)}}var A=wp.i18n.__,B=wp.element.Component,L=wp.components,T=L.PanelBody,R=L.CheckboxControl,I=wp.blockEditor.InspectorControls,z=function(e){m()(r,e);var t=D(r);function r(){return s()(this,r),t.apply(this,arguments)}return l()(r,[{key:"render",value:function(){var e=this.props,t=e.attributes,r=t.membership,o=t.profile,n=t.invoices,p=t.links,s=e.setAttributes;return Object(c.createElement)(I,null,Object(c.createElement)(T,null,Object(c.createElement)(R,{label:A("Show 'My Memberships' Section","paid-memberships-pro"),checked:r,onChange:function(e){return s({membership:e})}})),Object(c.createElement)(T,null,Object(c.createElement)(R,{label:A("Show 'Profile' Section","paid-memberships-pro"),checked:o,onChange:function(e){return s({profile:e})}})),Object(c.createElement)(T,null,Object(c.createElement)(R,{label:A("Show 'Invoices' Section","paid-memberships-pro"),checked:n,onChange:function(e){return s({invoices:e})}})),Object(c.createElement)(T,null,Object(c.createElement)(R,{label:A("Show 'Member Links' Section","paid-memberships-pro"),checked:p,onChange:function(e){return s({links:e})}})))}}]),r}(B);function F(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);t&&(c=c.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,c)}return r}function q(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?F(Object(r),!0).forEach((function(t){n()(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):F(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}var W=wp.i18n.__,G=((0,wp.blocks.registerBlockType)("pmpro/account-page",{title:W("Membership Account Page","paid-memberships-pro"),description:W("Displays the sections of the Membership Account page as selected below.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"admin-users"},keywords:[W("pmpro","paid-memberships-pro")],supports:{},attributes:{membership:{type:"boolean",default:!1},profile:{type:"boolean",default:!1},invoices:{type:"boolean",default:!1},links:{type:"boolean",default:!1}},edit:function(e){var t=e.setAttributes;return[e.isSelected&&Object(c.createElement)(z,q({setAttributes:t},e)),Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},W("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},W("Membership Account Page","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),H=((0,wp.blocks.registerBlockType)("pmpro/account-membership-section",{title:G("Membership Account: Memberships","paid-memberships-pro"),description:G("Displays the member's membership information.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"groups"},keywords:[G("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},G("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},G("Membership Account: My Memberships","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),J=((0,wp.blocks.registerBlockType)("pmpro/account-profile-section",{title:H("Membership Account: Profile","paid-memberships-pro"),description:H("Displays the member's profile information.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"admin-users"},keywords:[H("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},H("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},H("Membership Account: Profile","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),K=((0,wp.blocks.registerBlockType)("pmpro/account-invoices-section",{title:J("Membership Account: Invoices","paid-memberships-pro"),description:J("Displays the member's invoices.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"archive"},keywords:[J("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},J("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"}," ",J("Membership Account: Invoices","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),Q=((0,wp.blocks.registerBlockType)("pmpro/account-links-section",{title:K("Membership Account: Links","paid-memberships-pro"),description:K("Displays the member's member links. This block is only visible if other Add Ons or custom code have added links.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"external"},keywords:[K("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},K("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},K("Membership Account: Member Links","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),U=((0,wp.blocks.registerBlockType)("pmpro/billing-page",{title:Q("Membership Billing Page","paid-memberships-pro"),description:Q("Displays the member's billing information and allows them to update the payment method.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"list-view"},keywords:[Q("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},Q("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},Q("Membership Billing Page","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__);(0,wp.blocks.registerBlockType)("pmpro/cancel-page",{title:U("Membership Cancel Page","paid-memberships-pro"),description:U("Generates the Membership Cancel page.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"no"},keywords:[U("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},U("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},U("Membership Cancel Page","paid-memberships-pro")))]},save:function(){return null}});function V(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,c=d()(e);if(t){var o=d()(this).constructor;r=Reflect.construct(c,arguments,o)}else r=c.apply(this,arguments);return b()(this,r)}}var X=wp.i18n.__,Y=wp.element.Component,Z=wp.components,$=Z.PanelBody,ee=(Z.PanelRow,Z.SelectControl),te=wp.blockEditor.InspectorControls,re=function(e){m()(r,e);var t=V(r);function r(){return s()(this,r),t.apply(this,arguments)}return l()(r,[{key:"render",value:function(){var e=this.props,t=e.attributes.pmpro_default_level,r=e.setAttributes;return Object(c.createElement)(te,null,Object(c.createElement)($,null,Object(c.createElement)(ee,{label:X("Membership Level","paid-memberships-pro"),help:X("Choose a default level for Membership Checkout.","paid-memberships-pro"),value:t,onChange:function(e){return r({pmpro_default_level:e})},options:[""].concat(window.pmpro.all_level_values_and_labels)})))}}]),r}(Y);function ce(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);t&&(c=c.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,c)}return r}function oe(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?ce(Object(r),!0).forEach((function(t){n()(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):ce(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}var ne=wp.i18n.__,pe=wp.blocks.registerBlockType,se=wp.components.SelectControl,ie=(pe("pmpro/checkout-page",{title:ne("Membership Checkout Form","paid-memberships-pro"),description:ne("Displays the Membership Checkout form.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"list-view"},keywords:[ne("pmpro","paid-memberships-pro")],supports:{},attributes:{pmpro_default_level:{type:"string",source:"meta",meta:"pmpro_default_level"}},edit:function(e){var t=e.attributes.pmpro_default_level,r=(e.className,e.setAttributes);return[e.isSelected&&Object(c.createElement)(re,oe({setAttributes:r},e)),Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},ne("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},ne("Membership Checkout Form","paid-memberships-pro")),Object(c.createElement)("hr",null),Object(c.createElement)(se,{label:ne("Membership Level","paid-memberships-pro"),value:t,onChange:function(e){return r({pmpro_default_level:e})},options:window.pmpro.all_level_values_and_labels}))]},save:function(){return null}}),wp.i18n.__),le=((0,wp.blocks.registerBlockType)("pmpro/confirmation-page",{title:ie("Membership Confirmation Page","paid-memberships-pro"),description:ie("Displays the member's Membership Confirmation after Membership Checkout.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"yes"},keywords:[ie("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},ie("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},ie("Membership Confirmation Page","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),ae=((0,wp.blocks.registerBlockType)("pmpro/invoice-page",{title:le("Membership Invoice Page","paid-memberships-pro"),description:le("Displays the member's Membership Invoices.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"archive"},keywords:[le("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},le("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},le("Membership Invoices","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),me=((0,wp.blocks.registerBlockType)("pmpro/levels-page",{title:ae("Membership Levels List","paid-memberships-pro"),description:ae("Displays a list of Membership Levels. To change the order, go to Memberships > Settings > Levels.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"list-view"},keywords:[ae("pmpro","paid-memberships-pro")],supports:{},attributes:{},edit:function(){return[Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},ae("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},ae("Membership Levels List","paid-memberships-pro")))]},save:function(){return null}}),wp.i18n.__),ue=wp.blocks.registerBlockType,be=wp.components,fe=be.PanelBody,de=be.SelectControl,he=wp.blockEditor,ye=he.InspectorControls,ve=he.InnerBlocks,ge=[{value:0,label:"Non-Members"}].concat(pmpro.all_level_values_and_labels),Oe=(ue("pmpro/membership",{title:me("Require Membership Block","paid-memberships-pro"),description:me("Control the visibility of nested blocks for members or non-members.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"visibility"},keywords:[me("pmpro","paid-memberships-pro")],attributes:{levels:{type:"array",default:[]},uid:{type:"string",default:""}},edit:function(e){var t=e.attributes,r=t.levels,o=t.uid,n=e.setAttributes,p=e.isSelected;if(""==o){var s=Math.random()+"";n({uid:s})}return[p&&Object(c.createElement)(ye,null,Object(c.createElement)(fe,null,Object(c.createElement)(de,{multiple:!0,label:me("Select levels to show content to:","paid-memberships-pro"),value:r,onChange:function(e){n({levels:e})},options:ge}))),p&&Object(c.createElement)("div",{className:"pmpro-block-require-membership-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},me("Require Membership","paid-memberships-pro")),Object(c.createElement)(fe,null,Object(c.createElement)(de,{multiple:!0,label:me("Select levels to show content to:","paid-memberships-pro"),value:r,onChange:function(e){n({levels:e})},options:ge})),Object(c.createElement)(ve,{renderAppender:function(){return Object(c.createElement)(ve.ButtonBlockAppender,null)},templateLock:!1})),!p&&Object(c.createElement)("div",{className:"pmpro-block-require-membership-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},me("Require Membership","paid-memberships-pro")),Object(c.createElement)(ve,{renderAppender:function(){return Object(c.createElement)(ve.ButtonBlockAppender,null)},templateLock:!1}))]},save:function(e){var t=e.className;return Object(c.createElement)("div",{className:t},Object(c.createElement)(ve.Content,null))}}),wp.i18n.__);(0,wp.blocks.registerBlockType)("pmpro/member-profile-edit",{title:Oe("Member Profile Edit","paid-memberships-pro"),description:Oe("Allow member profile editing.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"admin-users"},keywords:[Oe("pmpro","paid-memberships-pro"),Oe("member","paid-memberships-pro"),Oe("profile","paid-memberships-pro")],edit:function(e){return Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},Oe("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},Oe("Member Profile Edit","paid-memberships-pro")))},save:function(){return null}});function ke(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,c=d()(e);if(t){var o=d()(this).constructor;r=Reflect.construct(c,arguments,o)}else r=c.apply(this,arguments);return b()(this,r)}}var je,we=wp.i18n.__,Ce=wp.element.Component,Ee=wp.components,_e=Ee.PanelBody,Pe=(Ee.SelectControl,Ee.ToggleControl),Me=wp.blockEditor.InspectorControls,Ne=function(e){m()(r,e);var t=ke(r);function r(){return s()(this,r),t.apply(this,arguments)}return l()(r,[{key:"render",value:function(){var e=this,t=this.props,r=t.attributes,o=(t.setAttributes,r.display_if_logged_in),n=r.show_menu,p=r.show_logout_link;r.location;return Object(c.createElement)(Me,null,Object(c.createElement)(_e,null,Object(c.createElement)(Pe,{label:we("Display 'Welcome' content when logged in.","paid-memberships-pro"),checked:o,onChange:function(t){e.props.setAttributes({display_if_logged_in:t})}}),Object(c.createElement)(Pe,{label:we("Display the 'Log In Widget' menu.","paid-memberships-pro"),help:we("Assign the menu under Appearance > Menus."),checked:n,onChange:function(t){e.props.setAttributes({show_menu:t})}}),Object(c.createElement)(Pe,{label:we("Display a 'Log Out' link.","paid-memberships-pro"),checked:p,onChange:function(t){e.props.setAttributes({show_logout_link:t})}})))}}]),r}(Ce),Se=wp.i18n.__,xe=wp.blocks.registerBlockType,De=wp.element.Fragment;xe("pmpro/login-form",{title:Se("Log in Form","paid-memberships-pro"),description:Se("Displays a Log In Form for Paid Memberships Pro.","paid-memberships-pro"),category:"pmpro",icon:{background:"#2997c8",foreground:"#ffffff",src:"unlock"},keywords:[Se("pmpro","paid-memberships-pro"),Se("login","paid-memberships-pro"),Se("form","paid-memberships-pro"),Se("log in","paid-memberships-pro")],supports:{},edit:function(e){return[Object(c.createElement)(De,null,Object(c.createElement)(Ne,e),Object(c.createElement)("div",{className:"pmpro-block-element"},Object(c.createElement)("span",{className:"pmpro-block-title"},Se("Paid Memberships Pro","paid-memberships-pro")),Object(c.createElement)("span",{className:"pmpro-block-subtitle"},Se("Log in Form","paid-memberships-pro"))))]},save:function(){return null}});je=Object(c.createElement)("svg",{version:"1.1",id:"Layer_1",x:"0px",y:"0px",viewBox:"0 0 18 18"},Object(c.createElement)("path",{d:"M17.99,4.53c-0.35,0.12-0.7,0.26-1.06,0.4c-0.35,0.14-0.7,0.3-1.05,0.46c-0.35,0.16-0.69,0.33-1.03,0.51 c-0.34,0.18-0.68,0.37-1.02,0.56c-0.15,0.09-0.31,0.18-0.46,0.27c-0.15,0.09-0.3,0.19-0.45,0.28c-0.15,0.1-0.3,0.19-0.45,0.29 c-0.15,0.1-0.3,0.2-0.44,0.3c-0.08,0.05-0.16,0.11-0.23,0.16c-0.08,0.05-0.16,0.11-0.23,0.17c-0.08,0.06-0.15,0.11-0.23,0.17 c-0.08,0.06-0.15,0.11-0.23,0.17c-0.07,0.05-0.13,0.1-0.2,0.15c-0.07,0.05-0.13,0.1-0.2,0.15c-0.07,0.05-0.13,0.1-0.2,0.15 c-0.07,0.05-0.13,0.1-0.2,0.16c-0.04,0.03-0.09,0.07-0.13,0.1c-0.04,0.03-0.09,0.07-0.13,0.1C10,9.13,9.95,9.17,9.91,9.2 C9.87,9.24,9.83,9.27,9.79,9.31C9.77,9.32,9.75,9.33,9.74,9.35C9.72,9.36,9.71,9.37,9.69,9.39C9.67,9.4,9.66,9.42,9.64,9.43 C9.63,9.44,9.61,9.46,9.59,9.47C9.54,9.52,9.49,9.56,9.43,9.61C9.38,9.65,9.33,9.7,9.27,9.74C9.22,9.79,9.17,9.84,9.11,9.88 c-0.05,0.05-0.11,0.09-0.16,0.14c-0.27,0.24-0.54,0.49-0.81,0.75c-0.26,0.25-0.53,0.51-0.78,0.78c-0.26,0.26-0.51,0.53-0.76,0.81 c-0.25,0.27-0.49,0.55-0.73,0.84c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.07,0.08-0.1,0.12 c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.06,0.08-0.1,0.12c-0.03,0.04-0.06,0.08-0.1,0.12 c-0.03,0.04-0.06,0.08-0.1,0.12c0,0.01-0.01,0.01-0.01,0.02c0,0.01-0.01,0.01-0.01,0.02c0,0.01-0.01,0.01-0.01,0.02 c0,0.01-0.01,0.01-0.01,0.02c-0.03,0.03-0.05,0.07-0.08,0.1c-0.03,0.03-0.05,0.07-0.08,0.1c-0.03,0.03-0.05,0.07-0.08,0.11 c-0.03,0.03-0.05,0.07-0.08,0.11c-0.03,0.04-0.06,0.08-0.09,0.12c-0.03,0.04-0.06,0.08-0.09,0.12C4.5,14.96,4.47,15,4.44,15.05 c-0.03,0.04-0.06,0.08-0.09,0.13c0,0-0.01,0.01-0.01,0.01c0,0-0.01,0.01-0.01,0.01c0,0-0.01,0.01-0.01,0.01c0,0-0.01,0.01-0.01,0.01 c-0.15,0.22-0.31,0.44-0.46,0.67c-0.15,0.22-0.3,0.45-0.44,0.68c-0.14,0.23-0.29,0.46-0.43,0.7C2.85,17.52,2.71,17.76,2.58,18 c-0.08-0.19-0.16-0.38-0.23-0.56c-0.07-0.18-0.14-0.35-0.21-0.51c-0.07-0.16-0.13-0.32-0.19-0.47c-0.06-0.15-0.12-0.3-0.18-0.45 l-0.01,0.01l0.01-0.03c-0.01-0.03-0.02-0.05-0.03-0.08c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07 c-0.01-0.02-0.02-0.05-0.03-0.07c0-0.01-0.01-0.02-0.01-0.02c0-0.01-0.01-0.02-0.01-0.02c0-0.01-0.01-0.02-0.01-0.02 c0-0.01-0.01-0.02-0.01-0.02c-0.01-0.02-0.01-0.04-0.02-0.05c-0.01-0.02-0.01-0.04-0.02-0.05c-0.01-0.02-0.01-0.04-0.02-0.05 c-0.01-0.02-0.01-0.04-0.02-0.05c-0.01-0.03-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07 c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07 c-0.01-0.02-0.02-0.05-0.03-0.07c-0.02-0.05-0.04-0.1-0.06-0.16c-0.02-0.05-0.04-0.1-0.06-0.16c-0.02-0.05-0.04-0.11-0.06-0.16 c-0.02-0.05-0.04-0.11-0.06-0.16c-0.08-0.23-0.16-0.47-0.25-0.72c-0.08-0.25-0.17-0.5-0.26-0.77c-0.09-0.27-0.18-0.55-0.27-0.84 c-0.09-0.29-0.19-0.6-0.29-0.93c0.05,0.07,0.1,0.15,0.15,0.22c0.05,0.07,0.1,0.14,0.14,0.2c0.05,0.07,0.09,0.13,0.14,0.19 c0.04,0.06,0.09,0.12,0.13,0.18c0.09,0.13,0.18,0.24,0.27,0.35c0.09,0.11,0.17,0.21,0.24,0.3c0.08,0.09,0.15,0.18,0.23,0.27 c0.07,0.09,0.15,0.17,0.22,0.25c0.02,0.02,0.03,0.04,0.05,0.06c0.02,0.02,0.03,0.04,0.05,0.06c0.02,0.02,0.03,0.04,0.05,0.06 c0.02,0.02,0.03,0.04,0.05,0.06c0.07,0.07,0.13,0.14,0.2,0.22c0.07,0.08,0.14,0.16,0.22,0.24c0.08,0.08,0.16,0.17,0.24,0.27 c0.09,0.1,0.18,0.2,0.27,0.31c0.01,0.01,0.02,0.02,0.03,0.03c0.01,0.01,0.02,0.02,0.03,0.03c0.01,0.01,0.02,0.02,0.03,0.04 c0.01,0.01,0.02,0.02,0.03,0.04c0.02-0.02,0.04-0.05,0.06-0.07c0.02-0.02,0.04-0.05,0.06-0.07c0.02-0.02,0.04-0.05,0.06-0.07 C2.96,14.03,2.98,14,3,13.98c0.03-0.03,0.05-0.06,0.08-0.09c0.03-0.03,0.05-0.06,0.08-0.09c0.03-0.03,0.05-0.06,0.08-0.09 c0.03-0.03,0.05-0.06,0.08-0.09c0.28-0.33,0.58-0.65,0.88-0.97c0.31-0.32,0.63-0.62,0.95-0.92c0.33-0.3,0.67-0.6,1.02-0.88 c0.35-0.29,0.72-0.57,1.09-0.84c0.06-0.04,0.11-0.08,0.17-0.12C7.49,9.83,7.55,9.79,7.6,9.75c0.06-0.04,0.11-0.08,0.17-0.12 c0.06-0.04,0.12-0.08,0.17-0.12C7.97,9.5,7.98,9.49,8,9.48c0.02-0.01,0.03-0.02,0.05-0.03C8.06,9.43,8.08,9.42,8.1,9.41 C8.11,9.4,8.13,9.38,8.14,9.37c0.05-0.03,0.1-0.06,0.14-0.1c0.05-0.03,0.1-0.06,0.14-0.1c0.05-0.03,0.1-0.06,0.14-0.1 c0.05-0.03,0.1-0.06,0.15-0.09C8.79,8.94,8.87,8.9,8.94,8.85C9.01,8.8,9.09,8.76,9.16,8.71c0.07-0.05,0.15-0.09,0.22-0.14 c0.07-0.05,0.15-0.09,0.22-0.14c0.09-0.05,0.17-0.11,0.26-0.16c0.09-0.05,0.17-0.1,0.26-0.16c0.09-0.05,0.18-0.1,0.27-0.15 c0.09-0.05,0.18-0.1,0.27-0.15c0.25-0.14,0.51-0.28,0.76-0.42c0.26-0.14,0.52-0.27,0.78-0.41c0.26-0.13,0.53-0.27,0.79-0.4 c0.27-0.13,0.54-0.26,0.81-0.38c0.01,0,0.02-0.01,0.03-0.01c0.01,0,0.02-0.01,0.03-0.01c0.01,0,0.02-0.01,0.03-0.01 c0.01,0,0.02-0.01,0.03-0.01c0.33-0.15,0.67-0.3,1-0.44c0.34-0.15,0.68-0.29,1.02-0.42c0.34-0.14,0.69-0.27,1.03-0.4 C17.31,4.77,17.65,4.64,17.99,4.53z M15.73,9.59l0.65,4.56l-10.4-0.05c-0.02,0.02-0.04,0.04-0.05,0.07 c-0.02,0.02-0.04,0.04-0.05,0.07c-0.02,0.02-0.04,0.04-0.05,0.07c-0.02,0.02-0.04,0.04-0.05,0.07c-0.02,0.02-0.03,0.04-0.05,0.06 c-0.02,0.02-0.03,0.04-0.05,0.06c-0.02,0.02-0.03,0.04-0.05,0.06c-0.02,0.02-0.03,0.04-0.05,0.06l11.23,0.2l-0.78-5.24L15.73,9.59z M6.75,13.2c-0.04,0.04-0.08,0.09-0.11,0.13c-0.04,0.04-0.08,0.09-0.11,0.13c-0.04,0.04-0.07,0.09-0.11,0.13l9.22-0.07L15.04,9.1 l-0.07-0.53l-0.39,0.04l0.55,4.3l-8.27,0.17C6.83,13.12,6.79,13.16,6.75,13.2z M13.78,7.66l-0.59,0.08 c-0.06,0.04-0.12,0.08-0.18,0.12c-0.06,0.04-0.12,0.08-0.18,0.12c-0.06,0.04-0.12,0.08-0.18,0.12c-0.06,0.04-0.12,0.08-0.18,0.12 c-0.08,0.05-0.16,0.11-0.24,0.16c-0.08,0.06-0.16,0.11-0.24,0.17c-0.08,0.06-0.16,0.11-0.24,0.17c-0.08,0.06-0.16,0.11-0.24,0.17 c-0.07,0.05-0.14,0.1-0.21,0.15c-0.07,0.05-0.14,0.1-0.21,0.15c-0.07,0.05-0.14,0.1-0.2,0.16c-0.07,0.05-0.14,0.11-0.2,0.16 c-0.04,0.03-0.09,0.07-0.13,0.1c-0.04,0.03-0.09,0.07-0.13,0.1c-0.04,0.04-0.09,0.07-0.13,0.11c-0.04,0.04-0.09,0.07-0.13,0.11 c-0.02,0.01-0.03,0.03-0.05,0.04c-0.02,0.01-0.03,0.03-0.05,0.04c-0.02,0.01-0.03,0.03-0.05,0.04c-0.02,0.01-0.03,0.03-0.05,0.04 c-0.06,0.05-0.11,0.09-0.16,0.14c-0.05,0.05-0.11,0.09-0.16,0.14c-0.05,0.05-0.11,0.09-0.16,0.14c-0.05,0.05-0.11,0.09-0.16,0.14 c-0.17,0.15-0.34,0.3-0.51,0.46c-0.17,0.16-0.33,0.31-0.5,0.47c-0.16,0.16-0.33,0.32-0.49,0.48c-0.16,0.16-0.32,0.33-0.48,0.49 l6.98-0.23l-0.48-4.16L13.78,7.66z M13.32,5.73c-0.06,0.03-0.11,0.05-0.17,0.08c-0.06,0.03-0.12,0.06-0.17,0.09 c-0.03,0.01-0.06,0.03-0.08,0.04c0,0,0,0,0,0c-0.02-0.01-0.04-0.03-0.06-0.04c-0.06-0.04-0.13-0.07-0.21-0.09 c-0.07-0.02-0.15-0.04-0.23-0.04c-0.08,0-0.16,0-0.24,0.01l-0.14,0.02c0.07-0.04,0.13-0.08,0.18-0.14c0.05-0.05,0.1-0.11,0.14-0.18 c0.04-0.06,0.06-0.13,0.08-0.2c0.02-0.07,0.02-0.15,0.01-0.22c-0.01-0.1-0.04-0.18-0.08-0.26c-0.05-0.08-0.11-0.14-0.18-0.19 c-0.07-0.05-0.16-0.08-0.25-0.1c-0.09-0.02-0.19-0.02-0.29,0c-0.1,0.02-0.19,0.06-0.27,0.11c-0.08,0.05-0.15,0.11-0.21,0.19 C11.08,4.9,11.03,4.98,11,5.07c-0.03,0.09-0.04,0.18-0.03,0.27c0.01,0.07,0.02,0.14,0.05,0.2c0.03,0.06,0.06,0.12,0.11,0.17 c0.05,0.05,0.1,0.09,0.16,0.12c0.06,0.03,0.13,0.06,0.2,0.07l-0.17,0.03C11.18,5.96,11.06,6,10.94,6.07 c-0.11,0.07-0.21,0.15-0.29,0.25c-0.08,0.1-0.14,0.21-0.19,0.33c-0.04,0.12-0.06,0.25-0.05,0.38l0.02,0.33 c-0.09,0.05-0.17,0.1-0.26,0.16c-0.02,0-0.05,0-0.07,0c0.02-0.01,0.04-0.02,0.06-0.03c-0.06-0.06-0.13-0.11-0.21-0.16 c-0.07-0.04-0.15-0.08-0.24-0.1C9.63,7.2,9.54,7.18,9.45,7.18c-0.09-0.01-0.18,0-0.27,0.01L9.01,7.21c0.08-0.05,0.16-0.1,0.23-0.17 C9.3,6.97,9.36,6.9,9.41,6.81C9.46,6.73,9.5,6.64,9.52,6.55c0.02-0.09,0.03-0.19,0.03-0.29C9.54,6.13,9.51,6.02,9.46,5.92 c-0.05-0.1-0.12-0.18-0.21-0.25C9.17,5.6,9.07,5.56,8.96,5.53c-0.11-0.02-0.22-0.03-0.34,0C8.5,5.55,8.39,5.6,8.29,5.66 C8.19,5.72,8.1,5.81,8.02,5.9C7.95,5.99,7.89,6.1,7.85,6.21C7.81,6.32,7.79,6.44,7.79,6.56c0,0.09,0.02,0.18,0.05,0.26 c0.03,0.08,0.07,0.16,0.12,0.22c0.05,0.07,0.11,0.12,0.18,0.17c0.07,0.04,0.15,0.08,0.23,0.1l-0.2,0.03 C8.01,7.37,7.85,7.42,7.72,7.51C7.58,7.59,7.46,7.7,7.35,7.82C7.25,7.95,7.17,8.1,7.11,8.25c-0.06,0.16-0.09,0.33-0.08,0.5 l0.01,0.74C6.98,9.53,6.93,9.58,6.88,9.62C6.81,9.49,6.74,9.38,6.65,9.28c-0.1-0.11-0.21-0.2-0.33-0.27 C6.2,8.94,6.07,8.89,5.93,8.87C5.8,8.84,5.66,8.83,5.51,8.85L5.3,8.88c0.1-0.06,0.2-0.13,0.29-0.22c0.09-0.09,0.16-0.19,0.23-0.3 c0.06-0.11,0.12-0.23,0.15-0.35C6,7.88,6.02,7.75,6.02,7.62c0-0.17-0.03-0.32-0.08-0.46C5.88,7.03,5.8,6.91,5.71,6.82 C5.61,6.73,5.5,6.67,5.37,6.63c-0.12-0.04-0.26-0.04-0.4-0.02c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0 c-0.14,0.03-0.28,0.08-0.4,0.16c-0.12,0.08-0.23,0.18-0.33,0.3C4.14,7.2,4.07,7.33,4.01,7.48c-0.06,0.15-0.09,0.3-0.1,0.46 c0,0.12,0.01,0.24,0.03,0.35c0.03,0.11,0.07,0.21,0.12,0.3c0.05,0.09,0.12,0.17,0.2,0.23c0.08,0.06,0.17,0.11,0.27,0.14L4.3,9 C4.1,9.03,3.92,9.09,3.75,9.2C3.58,9.3,3.43,9.44,3.3,9.6c-0.13,0.16-0.24,0.35-0.32,0.56c-0.08,0.21-0.13,0.43-0.14,0.67 l-0.12,2.26l-0.53-0.6l0.49-6.3C2.68,6.09,2.71,6,2.74,5.91c0.04-0.09,0.08-0.17,0.14-0.24c0.06-0.07,0.12-0.14,0.2-0.19 C3.15,5.44,3.23,5.4,3.32,5.38l0.71-0.17l0-0.02l0.18-0.04l0.06-1.19C4.3,3.56,4.39,3.15,4.55,2.77c0.16-0.38,0.37-0.75,0.64-1.08 C5.45,1.35,5.76,1.05,6.11,0.8c0.35-0.26,0.74-0.47,1.16-0.61C7.7,0.05,8.12-0.01,8.51,0c0.4,0.02,0.77,0.12,1.1,0.29 c0.33,0.18,0.62,0.43,0.83,0.75c0.21,0.33,0.35,0.73,0.38,1.19l0.1,1.36l0.3-0.07l0,0.02l0.89-0.21c0.13-0.03,0.25-0.03,0.36-0.02 c0.12,0.02,0.22,0.05,0.32,0.11c0.09,0.05,0.17,0.13,0.23,0.21c0.06,0.09,0.1,0.19,0.11,0.31L13.32,5.73z M9.46,3.96L9.4,2.61 C9.39,2.33,9.31,2.09,9.19,1.88C9.07,1.68,8.91,1.51,8.71,1.4C8.52,1.28,8.29,1.21,8.05,1.19C7.81,1.17,7.55,1.2,7.28,1.28 C7.01,1.37,6.76,1.49,6.53,1.65c-0.22,0.16-0.43,0.35-0.6,0.57C5.77,2.43,5.63,2.67,5.53,2.91c-0.1,0.25-0.16,0.5-0.17,0.76 L5.33,4.91L9.46,3.96z"})),wp.blocks.updateCategory("pmpro",{icon:je})}]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******/ (function(modules) { // webpackBootstrap
2
+ /******/ // The module cache
3
+ /******/ var installedModules = {};
4
+ /******/
5
+ /******/ // The require function
6
+ /******/ function __webpack_require__(moduleId) {
7
+ /******/
8
+ /******/ // Check if module is in cache
9
+ /******/ if(installedModules[moduleId]) {
10
+ /******/ return installedModules[moduleId].exports;
11
+ /******/ }
12
+ /******/ // Create a new module (and put it into the cache)
13
+ /******/ var module = installedModules[moduleId] = {
14
+ /******/ i: moduleId,
15
+ /******/ l: false,
16
+ /******/ exports: {}
17
+ /******/ };
18
+ /******/
19
+ /******/ // Execute the module function
20
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21
+ /******/
22
+ /******/ // Flag the module as loaded
23
+ /******/ module.l = true;
24
+ /******/
25
+ /******/ // Return the exports of the module
26
+ /******/ return module.exports;
27
+ /******/ }
28
+ /******/
29
+ /******/
30
+ /******/ // expose the modules object (__webpack_modules__)
31
+ /******/ __webpack_require__.m = modules;
32
+ /******/
33
+ /******/ // expose the module cache
34
+ /******/ __webpack_require__.c = installedModules;
35
+ /******/
36
+ /******/ // define getter function for harmony exports
37
+ /******/ __webpack_require__.d = function(exports, name, getter) {
38
+ /******/ if(!__webpack_require__.o(exports, name)) {
39
+ /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
40
+ /******/ }
41
+ /******/ };
42
+ /******/
43
+ /******/ // define __esModule on exports
44
+ /******/ __webpack_require__.r = function(exports) {
45
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
46
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
47
+ /******/ }
48
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
49
+ /******/ };
50
+ /******/
51
+ /******/ // create a fake namespace object
52
+ /******/ // mode & 1: value is a module id, require it
53
+ /******/ // mode & 2: merge all properties of value into the ns
54
+ /******/ // mode & 4: return value when already ns object
55
+ /******/ // mode & 8|1: behave like require
56
+ /******/ __webpack_require__.t = function(value, mode) {
57
+ /******/ if(mode & 1) value = __webpack_require__(value);
58
+ /******/ if(mode & 8) return value;
59
+ /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
60
+ /******/ var ns = Object.create(null);
61
+ /******/ __webpack_require__.r(ns);
62
+ /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
63
+ /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
64
+ /******/ return ns;
65
+ /******/ };
66
+ /******/
67
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
68
+ /******/ __webpack_require__.n = function(module) {
69
+ /******/ var getter = module && module.__esModule ?
70
+ /******/ function getDefault() { return module['default']; } :
71
+ /******/ function getModuleExports() { return module; };
72
+ /******/ __webpack_require__.d(getter, 'a', getter);
73
+ /******/ return getter;
74
+ /******/ };
75
+ /******/
76
+ /******/ // Object.prototype.hasOwnProperty.call
77
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
78
+ /******/
79
+ /******/ // __webpack_public_path__
80
+ /******/ __webpack_require__.p = "";
81
+ /******/
82
+ /******/
83
+ /******/ // Load entry module and return exports
84
+ /******/ return __webpack_require__(__webpack_require__.s = "./blocks/blocks.js");
85
+ /******/ })
86
+ /************************************************************************/
87
+ /******/ ({
88
+
89
+ /***/ "./blocks/account-invoices-section/block.js":
90
+ /*!**************************************************!*\
91
+ !*** ./blocks/account-invoices-section/block.js ***!
92
+ \**************************************************/
93
+ /*! exports provided: default */
94
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
95
+
96
+ "use strict";
97
+ __webpack_require__.r(__webpack_exports__);
98
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
99
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
100
+
101
+
102
+ /**
103
+ * Block: PMPro Membership Account: Invoices
104
+ *
105
+ * Displays the Membership Account > Invoices page section.
106
+ *
107
+ */
108
+
109
+ /**
110
+ * Internal block libraries
111
+ */
112
+ var __ = wp.i18n.__;
113
+ var registerBlockType = wp.blocks.registerBlockType;
114
+ /**
115
+ * Register block
116
+ */
117
+
118
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/account-invoices-section', {
119
+ title: __('Membership Account: Invoices', 'paid-memberships-pro'),
120
+ description: __('Displays the member\'s invoices.', 'paid-memberships-pro'),
121
+ category: 'pmpro',
122
+ icon: {
123
+ background: '#2997c8',
124
+ foreground: '#ffffff',
125
+ src: 'archive'
126
+ },
127
+ keywords: [__('pmpro', 'paid-memberships-pro')],
128
+ supports: {},
129
+ attributes: {},
130
+ edit: function edit() {
131
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
132
+ className: "pmpro-block-element"
133
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
134
+ className: "pmpro-block-title"
135
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
136
+ className: "pmpro-block-subtitle"
137
+ }, " ", __('Membership Account: Invoices', 'paid-memberships-pro')))];
138
+ },
139
+ save: function save() {
140
+ return null;
141
+ }
142
+ }));
143
+
144
+ /***/ }),
145
+
146
+ /***/ "./blocks/account-links-section/block.js":
147
+ /*!***********************************************!*\
148
+ !*** ./blocks/account-links-section/block.js ***!
149
+ \***********************************************/
150
+ /*! exports provided: default */
151
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
152
+
153
+ "use strict";
154
+ __webpack_require__.r(__webpack_exports__);
155
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
156
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
157
+
158
+
159
+ /**
160
+ * Block: PMPro Membership Account: Member Links
161
+ *
162
+ * Displays the Membership Account > Member Links page section.
163
+ *
164
+ */
165
+
166
+ /**
167
+ * Internal block libraries
168
+ */
169
+ var __ = wp.i18n.__;
170
+ var registerBlockType = wp.blocks.registerBlockType;
171
+ /**
172
+ * Register block
173
+ */
174
+
175
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/account-links-section', {
176
+ title: __('Membership Account: Links', 'paid-memberships-pro'),
177
+ description: __('Displays the member\'s member links. This block is only visible if other Add Ons or custom code have added links.', 'paid-memberships-pro'),
178
+ category: 'pmpro',
179
+ icon: {
180
+ background: '#2997c8',
181
+ foreground: '#ffffff',
182
+ src: 'external'
183
+ },
184
+ keywords: [__('pmpro', 'paid-memberships-pro')],
185
+ supports: {},
186
+ attributes: {},
187
+ edit: function edit() {
188
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
189
+ className: "pmpro-block-element"
190
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
191
+ className: "pmpro-block-title"
192
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
193
+ className: "pmpro-block-subtitle"
194
+ }, __('Membership Account: Member Links', 'paid-memberships-pro')))];
195
+ },
196
+ save: function save() {
197
+ return null;
198
+ }
199
+ }));
200
+
201
+ /***/ }),
202
+
203
+ /***/ "./blocks/account-membership-section/block.js":
204
+ /*!****************************************************!*\
205
+ !*** ./blocks/account-membership-section/block.js ***!
206
+ \****************************************************/
207
+ /*! exports provided: default */
208
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
209
+
210
+ "use strict";
211
+ __webpack_require__.r(__webpack_exports__);
212
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
213
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
214
+
215
+
216
+ /**
217
+ * Block: PMPro Membership Account: Memberships
218
+ *
219
+ * Displays the Membership Account > My Memberships page section.
220
+ *
221
+ */
222
+
223
+ /**
224
+ * Internal block libraries
225
+ */
226
+ var __ = wp.i18n.__;
227
+ var registerBlockType = wp.blocks.registerBlockType;
228
+ /**
229
+ * Register block
230
+ */
231
+
232
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/account-membership-section', {
233
+ title: __('Membership Account: Memberships', 'paid-memberships-pro'),
234
+ description: __('Displays the member\'s membership information.', 'paid-memberships-pro'),
235
+ category: 'pmpro',
236
+ icon: {
237
+ background: '#2997c8',
238
+ foreground: '#ffffff',
239
+ src: 'groups'
240
+ },
241
+ keywords: [__('pmpro', 'paid-memberships-pro')],
242
+ supports: {},
243
+ attributes: {},
244
+ edit: function edit() {
245
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
246
+ className: "pmpro-block-element"
247
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
248
+ className: "pmpro-block-title"
249
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
250
+ className: "pmpro-block-subtitle"
251
+ }, __('Membership Account: My Memberships', 'paid-memberships-pro')))];
252
+ },
253
+ save: function save() {
254
+ return null;
255
+ }
256
+ }));
257
+
258
+ /***/ }),
259
+
260
+ /***/ "./blocks/account-page/block.js":
261
+ /*!**************************************!*\
262
+ !*** ./blocks/account-page/block.js ***!
263
+ \**************************************/
264
+ /*! exports provided: default */
265
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
266
+
267
+ "use strict";
268
+ __webpack_require__.r(__webpack_exports__);
269
+ /* harmony import */ var _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/defineProperty */ "./node_modules/@babel/runtime/helpers/defineProperty.js");
270
+ /* harmony import */ var _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0__);
271
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
272
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__);
273
+ /* harmony import */ var _inspector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./inspector */ "./blocks/account-page/inspector.js");
274
+
275
+
276
+
277
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
278
+
279
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0___default()(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
280
+
281
+ /**
282
+ * Block: PMPro Membership Account
283
+ *
284
+ * Displays the Membership Account page.
285
+ *
286
+ */
287
+
288
+ /**
289
+ * Block dependencies
290
+ */
291
+
292
+ /**
293
+ * Internal block libraries
294
+ */
295
+
296
+ var __ = wp.i18n.__;
297
+ var registerBlockType = wp.blocks.registerBlockType;
298
+ /**
299
+ * Register block
300
+ */
301
+
302
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/account-page', {
303
+ title: __('Membership Account Page', 'paid-memberships-pro'),
304
+ description: __('Displays the sections of the Membership Account page as selected below.', 'paid-memberships-pro'),
305
+ category: 'pmpro',
306
+ icon: {
307
+ background: '#2997c8',
308
+ foreground: '#ffffff',
309
+ src: 'admin-users'
310
+ },
311
+ keywords: [__('pmpro', 'paid-memberships-pro')],
312
+ supports: {},
313
+ attributes: {
314
+ membership: {
315
+ type: 'boolean',
316
+ default: false
317
+ },
318
+ profile: {
319
+ type: 'boolean',
320
+ default: false
321
+ },
322
+ invoices: {
323
+ type: 'boolean',
324
+ default: false
325
+ },
326
+ links: {
327
+ type: 'boolean',
328
+ default: false
329
+ }
330
+ },
331
+ edit: function edit(props) {
332
+ var setAttributes = props.setAttributes,
333
+ isSelected = props.isSelected;
334
+ return [isSelected && Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])(_inspector__WEBPACK_IMPORTED_MODULE_2__["default"], _objectSpread({
335
+ setAttributes: setAttributes
336
+ }, props)), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("div", {
337
+ className: "pmpro-block-element"
338
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("span", {
339
+ className: "pmpro-block-title"
340
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("span", {
341
+ className: "pmpro-block-subtitle"
342
+ }, __('Membership Account Page', 'paid-memberships-pro')))];
343
+ },
344
+ save: function save() {
345
+ return null;
346
+ }
347
+ }));
348
+
349
+ /***/ }),
350
+
351
+ /***/ "./blocks/account-page/inspector.js":
352
+ /*!******************************************!*\
353
+ !*** ./blocks/account-page/inspector.js ***!
354
+ \******************************************/
355
+ /*! exports provided: default */
356
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
357
+
358
+ "use strict";
359
+ __webpack_require__.r(__webpack_exports__);
360
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Inspector; });
361
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/classCallCheck */ "./node_modules/@babel/runtime/helpers/classCallCheck.js");
362
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__);
363
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/helpers/createClass */ "./node_modules/@babel/runtime/helpers/createClass.js");
364
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__);
365
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime/helpers/inherits */ "./node_modules/@babel/runtime/helpers/inherits.js");
366
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__);
367
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babel/runtime/helpers/possibleConstructorReturn */ "./node_modules/@babel/runtime/helpers/possibleConstructorReturn.js");
368
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__);
369
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @babel/runtime/helpers/getPrototypeOf */ "./node_modules/@babel/runtime/helpers/getPrototypeOf.js");
370
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__);
371
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
372
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__);
373
+
374
+
375
+
376
+
377
+
378
+
379
+
380
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default()(this, result); }; }
381
+
382
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
383
+
384
+ /**
385
+ * Internal block libraries
386
+ */
387
+ var __ = wp.i18n.__;
388
+ var Component = wp.element.Component;
389
+ var _wp$components = wp.components,
390
+ PanelBody = _wp$components.PanelBody,
391
+ CheckboxControl = _wp$components.CheckboxControl;
392
+ var InspectorControls = wp.blockEditor.InspectorControls;
393
+ /**
394
+ * Create an Inspector Controls wrapper Component
395
+ */
396
+
397
+ var Inspector = /*#__PURE__*/function (_Component) {
398
+ _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default()(Inspector, _Component);
399
+
400
+ var _super = _createSuper(Inspector);
401
+
402
+ function Inspector() {
403
+ _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default()(this, Inspector);
404
+
405
+ return _super.apply(this, arguments);
406
+ }
407
+
408
+ _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default()(Inspector, [{
409
+ key: "render",
410
+ value: function render() {
411
+ var _this$props = this.props,
412
+ _this$props$attribute = _this$props.attributes,
413
+ membership = _this$props$attribute.membership,
414
+ profile = _this$props$attribute.profile,
415
+ invoices = _this$props$attribute.invoices,
416
+ links = _this$props$attribute.links,
417
+ setAttributes = _this$props.setAttributes;
418
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(InspectorControls, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(CheckboxControl, {
419
+ label: __("Show 'My Memberships' Section", 'paid-memberships-pro'),
420
+ checked: membership,
421
+ onChange: function onChange(membership) {
422
+ return setAttributes({
423
+ membership: membership
424
+ });
425
+ }
426
+ })), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(CheckboxControl, {
427
+ label: __("Show 'Profile' Section", 'paid-memberships-pro'),
428
+ checked: profile,
429
+ onChange: function onChange(profile) {
430
+ return setAttributes({
431
+ profile: profile
432
+ });
433
+ }
434
+ })), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(CheckboxControl, {
435
+ label: __("Show 'Invoices' Section", 'paid-memberships-pro'),
436
+ checked: invoices,
437
+ onChange: function onChange(invoices) {
438
+ return setAttributes({
439
+ invoices: invoices
440
+ });
441
+ }
442
+ })), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(CheckboxControl, {
443
+ label: __("Show 'Member Links' Section", 'paid-memberships-pro'),
444
+ checked: links,
445
+ onChange: function onChange(links) {
446
+ return setAttributes({
447
+ links: links
448
+ });
449
+ }
450
+ })));
451
+ }
452
+ }]);
453
+
454
+ return Inspector;
455
+ }(Component);
456
+
457
+
458
+
459
+ /***/ }),
460
+
461
+ /***/ "./blocks/account-profile-section/block.js":
462
+ /*!*************************************************!*\
463
+ !*** ./blocks/account-profile-section/block.js ***!
464
+ \*************************************************/
465
+ /*! exports provided: default */
466
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
467
+
468
+ "use strict";
469
+ __webpack_require__.r(__webpack_exports__);
470
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
471
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
472
+
473
+
474
+ /**
475
+ * Block: PMPro Checkout Button
476
+ *
477
+ * Add a styled link to the PMPro checkout page for a
478
+ * specific level.
479
+ *
480
+ */
481
+
482
+ /**
483
+ * Internal block libraries
484
+ */
485
+ var __ = wp.i18n.__;
486
+ var registerBlockType = wp.blocks.registerBlockType;
487
+ /**
488
+ * Register block
489
+ */
490
+
491
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/account-profile-section', {
492
+ title: __('Membership Account: Profile', 'paid-memberships-pro'),
493
+ description: __('Displays the member\'s profile information.', 'paid-memberships-pro'),
494
+ category: 'pmpro',
495
+ icon: {
496
+ background: '#2997c8',
497
+ foreground: '#ffffff',
498
+ src: 'admin-users'
499
+ },
500
+ keywords: [__('pmpro', 'paid-memberships-pro')],
501
+ supports: {},
502
+ attributes: {},
503
+ edit: function edit() {
504
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
505
+ className: "pmpro-block-element"
506
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
507
+ className: "pmpro-block-title"
508
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
509
+ className: "pmpro-block-subtitle"
510
+ }, __('Membership Account: Profile', 'paid-memberships-pro')))];
511
+ },
512
+ save: function save() {
513
+ return null;
514
+ }
515
+ }));
516
+
517
+ /***/ }),
518
+
519
+ /***/ "./blocks/billing-page/block.js":
520
+ /*!**************************************!*\
521
+ !*** ./blocks/billing-page/block.js ***!
522
+ \**************************************/
523
+ /*! exports provided: default */
524
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
525
+
526
+ "use strict";
527
+ __webpack_require__.r(__webpack_exports__);
528
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
529
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
530
+
531
+
532
+ /**
533
+ * Block: PMPro Membership Billing
534
+ *
535
+ * Displays the Membership Billing page and form.
536
+ *
537
+ */
538
+
539
+ /**
540
+ * Internal block libraries
541
+ */
542
+ var __ = wp.i18n.__;
543
+ var registerBlockType = wp.blocks.registerBlockType;
544
+ /**
545
+ * Register block
546
+ */
547
+
548
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/billing-page', {
549
+ title: __('Membership Billing Page', 'paid-memberships-pro'),
550
+ description: __('Displays the member\'s billing information and allows them to update the payment method.', 'paid-memberships-pro'),
551
+ category: 'pmpro',
552
+ icon: {
553
+ background: '#2997c8',
554
+ foreground: '#ffffff',
555
+ src: 'list-view'
556
+ },
557
+ keywords: [__('pmpro', 'paid-memberships-pro')],
558
+ supports: {},
559
+ attributes: {},
560
+ edit: function edit() {
561
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
562
+ className: "pmpro-block-element"
563
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
564
+ className: "pmpro-block-title"
565
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
566
+ className: "pmpro-block-subtitle"
567
+ }, __('Membership Billing Page', 'paid-memberships-pro')))];
568
+ },
569
+ save: function save() {
570
+ return null;
571
+ }
572
+ }));
573
+
574
+ /***/ }),
575
+
576
+ /***/ "./blocks/blocks.js":
577
+ /*!**************************!*\
578
+ !*** ./blocks/blocks.js ***!
579
+ \**************************/
580
+ /*! no exports provided */
581
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
582
+
583
+ "use strict";
584
+ __webpack_require__.r(__webpack_exports__);
585
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
586
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
587
+ /* harmony import */ var _i18n_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./i18n.js */ "./blocks/i18n.js");
588
+ /* harmony import */ var _i18n_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_i18n_js__WEBPACK_IMPORTED_MODULE_1__);
589
+ /* harmony import */ var _checkout_button_block_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./checkout-button/block.js */ "./blocks/checkout-button/block.js");
590
+ /* harmony import */ var _account_page_block_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./account-page/block.js */ "./blocks/account-page/block.js");
591
+ /* harmony import */ var _account_membership_section_block_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./account-membership-section/block.js */ "./blocks/account-membership-section/block.js");
592
+ /* harmony import */ var _account_profile_section_block_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./account-profile-section/block.js */ "./blocks/account-profile-section/block.js");
593
+ /* harmony import */ var _account_invoices_section_block_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./account-invoices-section/block.js */ "./blocks/account-invoices-section/block.js");
594
+ /* harmony import */ var _account_links_section_block_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./account-links-section/block.js */ "./blocks/account-links-section/block.js");
595
+ /* harmony import */ var _billing_page_block_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./billing-page/block.js */ "./blocks/billing-page/block.js");
596
+ /* harmony import */ var _cancel_page_block_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./cancel-page/block.js */ "./blocks/cancel-page/block.js");
597
+ /* harmony import */ var _checkout_page_block_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./checkout-page/block.js */ "./blocks/checkout-page/block.js");
598
+ /* harmony import */ var _confirmation_page_block_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./confirmation-page/block.js */ "./blocks/confirmation-page/block.js");
599
+ /* harmony import */ var _invoice_page_block_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./invoice-page/block.js */ "./blocks/invoice-page/block.js");
600
+ /* harmony import */ var _levels_page_block_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./levels-page/block.js */ "./blocks/levels-page/block.js");
601
+ /* harmony import */ var _membership_block_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./membership/block.js */ "./blocks/membership/block.js");
602
+ /* harmony import */ var _member_profile_edit_block_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./member-profile-edit/block.js */ "./blocks/member-profile-edit/block.js");
603
+ /* harmony import */ var _login_block_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./login/block.js */ "./blocks/login/block.js");
604
+
605
+
606
+ /**
607
+ * Import internationalization
608
+ */
609
+
610
+ /**
611
+ * Import registerBlockType blocks
612
+ */
613
+
614
+
615
+
616
+
617
+
618
+
619
+
620
+
621
+
622
+
623
+
624
+
625
+
626
+
627
+
628
+
629
+
630
+ (function () {
631
+ var PMProSVG = Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("svg", {
632
+ version: "1.1",
633
+ id: "Layer_1",
634
+ x: "0px",
635
+ y: "0px",
636
+ viewBox: "0 0 18 18"
637
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("path", {
638
+ d: "M17.99,4.53c-0.35,0.12-0.7,0.26-1.06,0.4c-0.35,0.14-0.7,0.3-1.05,0.46c-0.35,0.16-0.69,0.33-1.03,0.51 c-0.34,0.18-0.68,0.37-1.02,0.56c-0.15,0.09-0.31,0.18-0.46,0.27c-0.15,0.09-0.3,0.19-0.45,0.28c-0.15,0.1-0.3,0.19-0.45,0.29 c-0.15,0.1-0.3,0.2-0.44,0.3c-0.08,0.05-0.16,0.11-0.23,0.16c-0.08,0.05-0.16,0.11-0.23,0.17c-0.08,0.06-0.15,0.11-0.23,0.17 c-0.08,0.06-0.15,0.11-0.23,0.17c-0.07,0.05-0.13,0.1-0.2,0.15c-0.07,0.05-0.13,0.1-0.2,0.15c-0.07,0.05-0.13,0.1-0.2,0.15 c-0.07,0.05-0.13,0.1-0.2,0.16c-0.04,0.03-0.09,0.07-0.13,0.1c-0.04,0.03-0.09,0.07-0.13,0.1C10,9.13,9.95,9.17,9.91,9.2 C9.87,9.24,9.83,9.27,9.79,9.31C9.77,9.32,9.75,9.33,9.74,9.35C9.72,9.36,9.71,9.37,9.69,9.39C9.67,9.4,9.66,9.42,9.64,9.43 C9.63,9.44,9.61,9.46,9.59,9.47C9.54,9.52,9.49,9.56,9.43,9.61C9.38,9.65,9.33,9.7,9.27,9.74C9.22,9.79,9.17,9.84,9.11,9.88 c-0.05,0.05-0.11,0.09-0.16,0.14c-0.27,0.24-0.54,0.49-0.81,0.75c-0.26,0.25-0.53,0.51-0.78,0.78c-0.26,0.26-0.51,0.53-0.76,0.81 c-0.25,0.27-0.49,0.55-0.73,0.84c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.07,0.08-0.1,0.12 c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.07,0.08-0.1,0.12c-0.03,0.04-0.06,0.08-0.1,0.12c-0.03,0.04-0.06,0.08-0.1,0.12 c-0.03,0.04-0.06,0.08-0.1,0.12c0,0.01-0.01,0.01-0.01,0.02c0,0.01-0.01,0.01-0.01,0.02c0,0.01-0.01,0.01-0.01,0.02 c0,0.01-0.01,0.01-0.01,0.02c-0.03,0.03-0.05,0.07-0.08,0.1c-0.03,0.03-0.05,0.07-0.08,0.1c-0.03,0.03-0.05,0.07-0.08,0.11 c-0.03,0.03-0.05,0.07-0.08,0.11c-0.03,0.04-0.06,0.08-0.09,0.12c-0.03,0.04-0.06,0.08-0.09,0.12C4.5,14.96,4.47,15,4.44,15.05 c-0.03,0.04-0.06,0.08-0.09,0.13c0,0-0.01,0.01-0.01,0.01c0,0-0.01,0.01-0.01,0.01c0,0-0.01,0.01-0.01,0.01c0,0-0.01,0.01-0.01,0.01 c-0.15,0.22-0.31,0.44-0.46,0.67c-0.15,0.22-0.3,0.45-0.44,0.68c-0.14,0.23-0.29,0.46-0.43,0.7C2.85,17.52,2.71,17.76,2.58,18 c-0.08-0.19-0.16-0.38-0.23-0.56c-0.07-0.18-0.14-0.35-0.21-0.51c-0.07-0.16-0.13-0.32-0.19-0.47c-0.06-0.15-0.12-0.3-0.18-0.45 l-0.01,0.01l0.01-0.03c-0.01-0.03-0.02-0.05-0.03-0.08c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07 c-0.01-0.02-0.02-0.05-0.03-0.07c0-0.01-0.01-0.02-0.01-0.02c0-0.01-0.01-0.02-0.01-0.02c0-0.01-0.01-0.02-0.01-0.02 c0-0.01-0.01-0.02-0.01-0.02c-0.01-0.02-0.01-0.04-0.02-0.05c-0.01-0.02-0.01-0.04-0.02-0.05c-0.01-0.02-0.01-0.04-0.02-0.05 c-0.01-0.02-0.01-0.04-0.02-0.05c-0.01-0.03-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07 c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07c-0.01-0.02-0.02-0.05-0.03-0.07 c-0.01-0.02-0.02-0.05-0.03-0.07c-0.02-0.05-0.04-0.1-0.06-0.16c-0.02-0.05-0.04-0.1-0.06-0.16c-0.02-0.05-0.04-0.11-0.06-0.16 c-0.02-0.05-0.04-0.11-0.06-0.16c-0.08-0.23-0.16-0.47-0.25-0.72c-0.08-0.25-0.17-0.5-0.26-0.77c-0.09-0.27-0.18-0.55-0.27-0.84 c-0.09-0.29-0.19-0.6-0.29-0.93c0.05,0.07,0.1,0.15,0.15,0.22c0.05,0.07,0.1,0.14,0.14,0.2c0.05,0.07,0.09,0.13,0.14,0.19 c0.04,0.06,0.09,0.12,0.13,0.18c0.09,0.13,0.18,0.24,0.27,0.35c0.09,0.11,0.17,0.21,0.24,0.3c0.08,0.09,0.15,0.18,0.23,0.27 c0.07,0.09,0.15,0.17,0.22,0.25c0.02,0.02,0.03,0.04,0.05,0.06c0.02,0.02,0.03,0.04,0.05,0.06c0.02,0.02,0.03,0.04,0.05,0.06 c0.02,0.02,0.03,0.04,0.05,0.06c0.07,0.07,0.13,0.14,0.2,0.22c0.07,0.08,0.14,0.16,0.22,0.24c0.08,0.08,0.16,0.17,0.24,0.27 c0.09,0.1,0.18,0.2,0.27,0.31c0.01,0.01,0.02,0.02,0.03,0.03c0.01,0.01,0.02,0.02,0.03,0.03c0.01,0.01,0.02,0.02,0.03,0.04 c0.01,0.01,0.02,0.02,0.03,0.04c0.02-0.02,0.04-0.05,0.06-0.07c0.02-0.02,0.04-0.05,0.06-0.07c0.02-0.02,0.04-0.05,0.06-0.07 C2.96,14.03,2.98,14,3,13.98c0.03-0.03,0.05-0.06,0.08-0.09c0.03-0.03,0.05-0.06,0.08-0.09c0.03-0.03,0.05-0.06,0.08-0.09 c0.03-0.03,0.05-0.06,0.08-0.09c0.28-0.33,0.58-0.65,0.88-0.97c0.31-0.32,0.63-0.62,0.95-0.92c0.33-0.3,0.67-0.6,1.02-0.88 c0.35-0.29,0.72-0.57,1.09-0.84c0.06-0.04,0.11-0.08,0.17-0.12C7.49,9.83,7.55,9.79,7.6,9.75c0.06-0.04,0.11-0.08,0.17-0.12 c0.06-0.04,0.12-0.08,0.17-0.12C7.97,9.5,7.98,9.49,8,9.48c0.02-0.01,0.03-0.02,0.05-0.03C8.06,9.43,8.08,9.42,8.1,9.41 C8.11,9.4,8.13,9.38,8.14,9.37c0.05-0.03,0.1-0.06,0.14-0.1c0.05-0.03,0.1-0.06,0.14-0.1c0.05-0.03,0.1-0.06,0.14-0.1 c0.05-0.03,0.1-0.06,0.15-0.09C8.79,8.94,8.87,8.9,8.94,8.85C9.01,8.8,9.09,8.76,9.16,8.71c0.07-0.05,0.15-0.09,0.22-0.14 c0.07-0.05,0.15-0.09,0.22-0.14c0.09-0.05,0.17-0.11,0.26-0.16c0.09-0.05,0.17-0.1,0.26-0.16c0.09-0.05,0.18-0.1,0.27-0.15 c0.09-0.05,0.18-0.1,0.27-0.15c0.25-0.14,0.51-0.28,0.76-0.42c0.26-0.14,0.52-0.27,0.78-0.41c0.26-0.13,0.53-0.27,0.79-0.4 c0.27-0.13,0.54-0.26,0.81-0.38c0.01,0,0.02-0.01,0.03-0.01c0.01,0,0.02-0.01,0.03-0.01c0.01,0,0.02-0.01,0.03-0.01 c0.01,0,0.02-0.01,0.03-0.01c0.33-0.15,0.67-0.3,1-0.44c0.34-0.15,0.68-0.29,1.02-0.42c0.34-0.14,0.69-0.27,1.03-0.4 C17.31,4.77,17.65,4.64,17.99,4.53z M15.73,9.59l0.65,4.56l-10.4-0.05c-0.02,0.02-0.04,0.04-0.05,0.07 c-0.02,0.02-0.04,0.04-0.05,0.07c-0.02,0.02-0.04,0.04-0.05,0.07c-0.02,0.02-0.04,0.04-0.05,0.07c-0.02,0.02-0.03,0.04-0.05,0.06 c-0.02,0.02-0.03,0.04-0.05,0.06c-0.02,0.02-0.03,0.04-0.05,0.06c-0.02,0.02-0.03,0.04-0.05,0.06l11.23,0.2l-0.78-5.24L15.73,9.59z M6.75,13.2c-0.04,0.04-0.08,0.09-0.11,0.13c-0.04,0.04-0.08,0.09-0.11,0.13c-0.04,0.04-0.07,0.09-0.11,0.13l9.22-0.07L15.04,9.1 l-0.07-0.53l-0.39,0.04l0.55,4.3l-8.27,0.17C6.83,13.12,6.79,13.16,6.75,13.2z M13.78,7.66l-0.59,0.08 c-0.06,0.04-0.12,0.08-0.18,0.12c-0.06,0.04-0.12,0.08-0.18,0.12c-0.06,0.04-0.12,0.08-0.18,0.12c-0.06,0.04-0.12,0.08-0.18,0.12 c-0.08,0.05-0.16,0.11-0.24,0.16c-0.08,0.06-0.16,0.11-0.24,0.17c-0.08,0.06-0.16,0.11-0.24,0.17c-0.08,0.06-0.16,0.11-0.24,0.17 c-0.07,0.05-0.14,0.1-0.21,0.15c-0.07,0.05-0.14,0.1-0.21,0.15c-0.07,0.05-0.14,0.1-0.2,0.16c-0.07,0.05-0.14,0.11-0.2,0.16 c-0.04,0.03-0.09,0.07-0.13,0.1c-0.04,0.03-0.09,0.07-0.13,0.1c-0.04,0.04-0.09,0.07-0.13,0.11c-0.04,0.04-0.09,0.07-0.13,0.11 c-0.02,0.01-0.03,0.03-0.05,0.04c-0.02,0.01-0.03,0.03-0.05,0.04c-0.02,0.01-0.03,0.03-0.05,0.04c-0.02,0.01-0.03,0.03-0.05,0.04 c-0.06,0.05-0.11,0.09-0.16,0.14c-0.05,0.05-0.11,0.09-0.16,0.14c-0.05,0.05-0.11,0.09-0.16,0.14c-0.05,0.05-0.11,0.09-0.16,0.14 c-0.17,0.15-0.34,0.3-0.51,0.46c-0.17,0.16-0.33,0.31-0.5,0.47c-0.16,0.16-0.33,0.32-0.49,0.48c-0.16,0.16-0.32,0.33-0.48,0.49 l6.98-0.23l-0.48-4.16L13.78,7.66z M13.32,5.73c-0.06,0.03-0.11,0.05-0.17,0.08c-0.06,0.03-0.12,0.06-0.17,0.09 c-0.03,0.01-0.06,0.03-0.08,0.04c0,0,0,0,0,0c-0.02-0.01-0.04-0.03-0.06-0.04c-0.06-0.04-0.13-0.07-0.21-0.09 c-0.07-0.02-0.15-0.04-0.23-0.04c-0.08,0-0.16,0-0.24,0.01l-0.14,0.02c0.07-0.04,0.13-0.08,0.18-0.14c0.05-0.05,0.1-0.11,0.14-0.18 c0.04-0.06,0.06-0.13,0.08-0.2c0.02-0.07,0.02-0.15,0.01-0.22c-0.01-0.1-0.04-0.18-0.08-0.26c-0.05-0.08-0.11-0.14-0.18-0.19 c-0.07-0.05-0.16-0.08-0.25-0.1c-0.09-0.02-0.19-0.02-0.29,0c-0.1,0.02-0.19,0.06-0.27,0.11c-0.08,0.05-0.15,0.11-0.21,0.19 C11.08,4.9,11.03,4.98,11,5.07c-0.03,0.09-0.04,0.18-0.03,0.27c0.01,0.07,0.02,0.14,0.05,0.2c0.03,0.06,0.06,0.12,0.11,0.17 c0.05,0.05,0.1,0.09,0.16,0.12c0.06,0.03,0.13,0.06,0.2,0.07l-0.17,0.03C11.18,5.96,11.06,6,10.94,6.07 c-0.11,0.07-0.21,0.15-0.29,0.25c-0.08,0.1-0.14,0.21-0.19,0.33c-0.04,0.12-0.06,0.25-0.05,0.38l0.02,0.33 c-0.09,0.05-0.17,0.1-0.26,0.16c-0.02,0-0.05,0-0.07,0c0.02-0.01,0.04-0.02,0.06-0.03c-0.06-0.06-0.13-0.11-0.21-0.16 c-0.07-0.04-0.15-0.08-0.24-0.1C9.63,7.2,9.54,7.18,9.45,7.18c-0.09-0.01-0.18,0-0.27,0.01L9.01,7.21c0.08-0.05,0.16-0.1,0.23-0.17 C9.3,6.97,9.36,6.9,9.41,6.81C9.46,6.73,9.5,6.64,9.52,6.55c0.02-0.09,0.03-0.19,0.03-0.29C9.54,6.13,9.51,6.02,9.46,5.92 c-0.05-0.1-0.12-0.18-0.21-0.25C9.17,5.6,9.07,5.56,8.96,5.53c-0.11-0.02-0.22-0.03-0.34,0C8.5,5.55,8.39,5.6,8.29,5.66 C8.19,5.72,8.1,5.81,8.02,5.9C7.95,5.99,7.89,6.1,7.85,6.21C7.81,6.32,7.79,6.44,7.79,6.56c0,0.09,0.02,0.18,0.05,0.26 c0.03,0.08,0.07,0.16,0.12,0.22c0.05,0.07,0.11,0.12,0.18,0.17c0.07,0.04,0.15,0.08,0.23,0.1l-0.2,0.03 C8.01,7.37,7.85,7.42,7.72,7.51C7.58,7.59,7.46,7.7,7.35,7.82C7.25,7.95,7.17,8.1,7.11,8.25c-0.06,0.16-0.09,0.33-0.08,0.5 l0.01,0.74C6.98,9.53,6.93,9.58,6.88,9.62C6.81,9.49,6.74,9.38,6.65,9.28c-0.1-0.11-0.21-0.2-0.33-0.27 C6.2,8.94,6.07,8.89,5.93,8.87C5.8,8.84,5.66,8.83,5.51,8.85L5.3,8.88c0.1-0.06,0.2-0.13,0.29-0.22c0.09-0.09,0.16-0.19,0.23-0.3 c0.06-0.11,0.12-0.23,0.15-0.35C6,7.88,6.02,7.75,6.02,7.62c0-0.17-0.03-0.32-0.08-0.46C5.88,7.03,5.8,6.91,5.71,6.82 C5.61,6.73,5.5,6.67,5.37,6.63c-0.12-0.04-0.26-0.04-0.4-0.02c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0 c-0.14,0.03-0.28,0.08-0.4,0.16c-0.12,0.08-0.23,0.18-0.33,0.3C4.14,7.2,4.07,7.33,4.01,7.48c-0.06,0.15-0.09,0.3-0.1,0.46 c0,0.12,0.01,0.24,0.03,0.35c0.03,0.11,0.07,0.21,0.12,0.3c0.05,0.09,0.12,0.17,0.2,0.23c0.08,0.06,0.17,0.11,0.27,0.14L4.3,9 C4.1,9.03,3.92,9.09,3.75,9.2C3.58,9.3,3.43,9.44,3.3,9.6c-0.13,0.16-0.24,0.35-0.32,0.56c-0.08,0.21-0.13,0.43-0.14,0.67 l-0.12,2.26l-0.53-0.6l0.49-6.3C2.68,6.09,2.71,6,2.74,5.91c0.04-0.09,0.08-0.17,0.14-0.24c0.06-0.07,0.12-0.14,0.2-0.19 C3.15,5.44,3.23,5.4,3.32,5.38l0.71-0.17l0-0.02l0.18-0.04l0.06-1.19C4.3,3.56,4.39,3.15,4.55,2.77c0.16-0.38,0.37-0.75,0.64-1.08 C5.45,1.35,5.76,1.05,6.11,0.8c0.35-0.26,0.74-0.47,1.16-0.61C7.7,0.05,8.12-0.01,8.51,0c0.4,0.02,0.77,0.12,1.1,0.29 c0.33,0.18,0.62,0.43,0.83,0.75c0.21,0.33,0.35,0.73,0.38,1.19l0.1,1.36l0.3-0.07l0,0.02l0.89-0.21c0.13-0.03,0.25-0.03,0.36-0.02 c0.12,0.02,0.22,0.05,0.32,0.11c0.09,0.05,0.17,0.13,0.23,0.21c0.06,0.09,0.1,0.19,0.11,0.31L13.32,5.73z M9.46,3.96L9.4,2.61 C9.39,2.33,9.31,2.09,9.19,1.88C9.07,1.68,8.91,1.51,8.71,1.4C8.52,1.28,8.29,1.21,8.05,1.19C7.81,1.17,7.55,1.2,7.28,1.28 C7.01,1.37,6.76,1.49,6.53,1.65c-0.22,0.16-0.43,0.35-0.6,0.57C5.77,2.43,5.63,2.67,5.53,2.91c-0.1,0.25-0.16,0.5-0.17,0.76 L5.33,4.91L9.46,3.96z"
639
+ }));
640
+ wp.blocks.updateCategory('pmpro', {
641
+ icon: PMProSVG
642
+ });
643
+ })();
644
+
645
+ /***/ }),
646
+
647
+ /***/ "./blocks/cancel-page/block.js":
648
+ /*!*************************************!*\
649
+ !*** ./blocks/cancel-page/block.js ***!
650
+ \*************************************/
651
+ /*! exports provided: default */
652
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
653
+
654
+ "use strict";
655
+ __webpack_require__.r(__webpack_exports__);
656
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
657
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
658
+
659
+
660
+ /**
661
+ * Block: PMPro Membership Cancel
662
+ *
663
+ * Displays the Membership Cancel page.
664
+ *
665
+ */
666
+
667
+ /**
668
+ * Internal block libraries
669
+ */
670
+ var __ = wp.i18n.__;
671
+ var registerBlockType = wp.blocks.registerBlockType;
672
+ /**
673
+ * Register block
674
+ */
675
+
676
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/cancel-page', {
677
+ title: __('Membership Cancel Page', 'paid-memberships-pro'),
678
+ description: __('Generates the Membership Cancel page.', 'paid-memberships-pro'),
679
+ category: 'pmpro',
680
+ icon: {
681
+ background: '#2997c8',
682
+ foreground: '#ffffff',
683
+ src: 'no'
684
+ },
685
+ keywords: [__('pmpro', 'paid-memberships-pro')],
686
+ supports: {},
687
+ attributes: {},
688
+ edit: function edit() {
689
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
690
+ className: "pmpro-block-element"
691
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
692
+ className: "pmpro-block-title"
693
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
694
+ className: "pmpro-block-subtitle"
695
+ }, __('Membership Cancel Page', 'paid-memberships-pro')))];
696
+ },
697
+ save: function save() {
698
+ return null;
699
+ }
700
+ }));
701
+
702
+ /***/ }),
703
+
704
+ /***/ "./blocks/checkout-button/block.js":
705
+ /*!*****************************************!*\
706
+ !*** ./blocks/checkout-button/block.js ***!
707
+ \*****************************************/
708
+ /*! exports provided: default */
709
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
710
+
711
+ "use strict";
712
+ __webpack_require__.r(__webpack_exports__);
713
+ /* harmony import */ var _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/defineProperty */ "./node_modules/@babel/runtime/helpers/defineProperty.js");
714
+ /* harmony import */ var _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0__);
715
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
716
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__);
717
+ /* harmony import */ var _inspector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./inspector */ "./blocks/checkout-button/inspector.js");
718
+
719
+
720
+
721
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
722
+
723
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0___default()(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
724
+
725
+ /**
726
+ * Block: PMPro Checkout Button
727
+ *
728
+ * Add a styled link to the PMPro checkout page for a specific level.
729
+ *
730
+ */
731
+
732
+ /**
733
+ * Block dependencies
734
+ */
735
+
736
+ /**
737
+ * Internal block libraries
738
+ */
739
+
740
+ var __ = wp.i18n.__;
741
+ var registerBlockType = wp.blocks.registerBlockType;
742
+ var _wp$components = wp.components,
743
+ TextControl = _wp$components.TextControl,
744
+ SelectControl = _wp$components.SelectControl;
745
+ /**
746
+ * Register block
747
+ */
748
+
749
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/checkout-button', {
750
+ title: __('Membership Checkout Button', 'paid-memberships-pro'),
751
+ description: __('Displays a button-styled link to Membership Checkout for the specified level.', 'paid-memberships-pro'),
752
+ category: 'pmpro',
753
+ icon: {
754
+ background: '#2997c8',
755
+ foreground: '#ffffff',
756
+ src: 'migrate'
757
+ },
758
+ keywords: [__('pmpro', 'paid-memberships-pro'), __('buy', 'paid-memberships-pro'), __('level', 'paid-memberships-pro')],
759
+ supports: {},
760
+ attributes: {
761
+ text: {
762
+ type: 'string',
763
+ default: 'Buy Now'
764
+ },
765
+ css_class: {
766
+ type: 'string',
767
+ default: 'pmpro_btn'
768
+ },
769
+ level: {
770
+ type: 'string'
771
+ }
772
+ },
773
+ edit: function edit(props) {
774
+ var _props$attributes = props.attributes,
775
+ text = _props$attributes.text,
776
+ level = _props$attributes.level,
777
+ css_class = _props$attributes.css_class,
778
+ className = props.className,
779
+ setAttributes = props.setAttributes,
780
+ isSelected = props.isSelected;
781
+ return [isSelected && Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])(_inspector__WEBPACK_IMPORTED_MODULE_2__["default"], _objectSpread({
782
+ setAttributes: setAttributes
783
+ }, props)), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("div", {
784
+ className: className
785
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("a", {
786
+ class: css_class
787
+ }, text)), isSelected && Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("div", {
788
+ className: "pmpro-block-element"
789
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])(TextControl, {
790
+ label: __('Button Text', 'paid-memberships-pro'),
791
+ value: text,
792
+ onChange: function onChange(text) {
793
+ return setAttributes({
794
+ text: text
795
+ });
796
+ }
797
+ }), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])(SelectControl, {
798
+ label: __('Membership Level', 'paid-memberships-pro'),
799
+ value: level,
800
+ onChange: function onChange(level) {
801
+ return setAttributes({
802
+ level: level
803
+ });
804
+ },
805
+ options: window.pmpro.all_level_values_and_labels
806
+ }), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])(TextControl, {
807
+ label: __('CSS Class', 'paid-memberships-pro'),
808
+ value: css_class,
809
+ onChange: function onChange(css_class) {
810
+ return setAttributes({
811
+ css_class: css_class
812
+ });
813
+ }
814
+ }))];
815
+ },
816
+ save: function save() {
817
+ return null;
818
+ }
819
+ }));
820
+
821
+ /***/ }),
822
+
823
+ /***/ "./blocks/checkout-button/inspector.js":
824
+ /*!*********************************************!*\
825
+ !*** ./blocks/checkout-button/inspector.js ***!
826
+ \*********************************************/
827
+ /*! exports provided: default */
828
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
829
+
830
+ "use strict";
831
+ __webpack_require__.r(__webpack_exports__);
832
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Inspector; });
833
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/classCallCheck */ "./node_modules/@babel/runtime/helpers/classCallCheck.js");
834
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__);
835
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/helpers/createClass */ "./node_modules/@babel/runtime/helpers/createClass.js");
836
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__);
837
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime/helpers/inherits */ "./node_modules/@babel/runtime/helpers/inherits.js");
838
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__);
839
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babel/runtime/helpers/possibleConstructorReturn */ "./node_modules/@babel/runtime/helpers/possibleConstructorReturn.js");
840
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__);
841
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @babel/runtime/helpers/getPrototypeOf */ "./node_modules/@babel/runtime/helpers/getPrototypeOf.js");
842
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__);
843
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
844
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__);
845
+
846
+
847
+
848
+
849
+
850
+
851
+
852
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default()(this, result); }; }
853
+
854
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
855
+
856
+ /**
857
+ * Internal block libraries
858
+ */
859
+ var __ = wp.i18n.__;
860
+ var Component = wp.element.Component;
861
+ var _wp$components = wp.components,
862
+ PanelBody = _wp$components.PanelBody,
863
+ TextControl = _wp$components.TextControl,
864
+ SelectControl = _wp$components.SelectControl;
865
+ var InspectorControls = wp.blockEditor.InspectorControls;
866
+ /**
867
+ * Create an Inspector Controls wrapper Component
868
+ */
869
+
870
+ var Inspector = /*#__PURE__*/function (_Component) {
871
+ _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default()(Inspector, _Component);
872
+
873
+ var _super = _createSuper(Inspector);
874
+
875
+ function Inspector() {
876
+ _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default()(this, Inspector);
877
+
878
+ return _super.apply(this, arguments);
879
+ }
880
+
881
+ _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default()(Inspector, [{
882
+ key: "render",
883
+ value: function render() {
884
+ var _this$props = this.props,
885
+ _this$props$attribute = _this$props.attributes,
886
+ text = _this$props$attribute.text,
887
+ level = _this$props$attribute.level,
888
+ css_class = _this$props$attribute.css_class,
889
+ setAttributes = _this$props.setAttributes;
890
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(InspectorControls, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(TextControl, {
891
+ label: __('Button Text', 'paid-memberships-pro'),
892
+ help: __('Text for checkout button', 'paid-memberships-pro'),
893
+ value: text,
894
+ onChange: function onChange(text) {
895
+ return setAttributes({
896
+ text: text
897
+ });
898
+ }
899
+ })), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(SelectControl, {
900
+ label: __('Level', 'paid-memberships-pro'),
901
+ help: __('The level to link to for checkout button', 'paid-memberships-pro'),
902
+ value: level,
903
+ onChange: function onChange(level) {
904
+ return setAttributes({
905
+ level: level
906
+ });
907
+ },
908
+ options: window.pmpro.all_level_values_and_labels
909
+ })), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(TextControl, {
910
+ label: __('CSS Class', 'paid-memberships-pro'),
911
+ help: __('Additional styling for checkout button', 'paid-memberships-pro'),
912
+ value: css_class,
913
+ onChange: function onChange(css_class) {
914
+ return setAttributes({
915
+ css_class: css_class
916
+ });
917
+ }
918
+ })));
919
+ }
920
+ }]);
921
+
922
+ return Inspector;
923
+ }(Component);
924
+
925
+
926
+
927
+ /***/ }),
928
+
929
+ /***/ "./blocks/checkout-page/block.js":
930
+ /*!***************************************!*\
931
+ !*** ./blocks/checkout-page/block.js ***!
932
+ \***************************************/
933
+ /*! exports provided: default */
934
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
935
+
936
+ "use strict";
937
+ __webpack_require__.r(__webpack_exports__);
938
+ /* harmony import */ var _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/defineProperty */ "./node_modules/@babel/runtime/helpers/defineProperty.js");
939
+ /* harmony import */ var _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0__);
940
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
941
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__);
942
+ /* harmony import */ var _inspector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./inspector */ "./blocks/checkout-page/inspector.js");
943
+
944
+
945
+
946
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
947
+
948
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _babel_runtime_helpers_defineProperty__WEBPACK_IMPORTED_MODULE_0___default()(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
949
+
950
+ /**
951
+ * Block: PMPro Membership Checkout
952
+ *
953
+ * Displays the Membership Checkout form.
954
+ *
955
+ */
956
+
957
+ /**
958
+ * Block dependencies
959
+ */
960
+
961
+ /**
962
+ * Internal block libraries
963
+ */
964
+
965
+ var __ = wp.i18n.__;
966
+ var registerBlockType = wp.blocks.registerBlockType;
967
+ var SelectControl = wp.components.SelectControl;
968
+ /**
969
+ * Register block
970
+ */
971
+
972
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/checkout-page', {
973
+ title: __('Membership Checkout Form', 'paid-memberships-pro'),
974
+ description: __('Displays the Membership Checkout form.', 'paid-memberships-pro'),
975
+ category: 'pmpro',
976
+ icon: {
977
+ background: '#2997c8',
978
+ foreground: '#ffffff',
979
+ src: 'list-view'
980
+ },
981
+ keywords: [__('pmpro', 'paid-memberships-pro')],
982
+ supports: {},
983
+ attributes: {
984
+ pmpro_default_level: {
985
+ type: 'string',
986
+ source: 'meta',
987
+ meta: 'pmpro_default_level'
988
+ }
989
+ },
990
+ edit: function edit(props) {
991
+ var pmpro_default_level = props.attributes.pmpro_default_level,
992
+ className = props.className,
993
+ setAttributes = props.setAttributes,
994
+ isSelected = props.isSelected;
995
+ return [isSelected && Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])(_inspector__WEBPACK_IMPORTED_MODULE_2__["default"], _objectSpread({
996
+ setAttributes: setAttributes
997
+ }, props)), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("div", {
998
+ className: "pmpro-block-element"
999
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("span", {
1000
+ className: "pmpro-block-title"
1001
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("span", {
1002
+ className: "pmpro-block-subtitle"
1003
+ }, __('Membership Checkout Form', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])("hr", null), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__["createElement"])(SelectControl, {
1004
+ label: __('Membership Level', 'paid-memberships-pro'),
1005
+ value: pmpro_default_level,
1006
+ onChange: function onChange(pmpro_default_level) {
1007
+ return setAttributes({
1008
+ pmpro_default_level: pmpro_default_level
1009
+ });
1010
+ },
1011
+ options: window.pmpro.all_level_values_and_labels
1012
+ }))];
1013
+ },
1014
+ save: function save() {
1015
+ return null;
1016
+ }
1017
+ }));
1018
+
1019
+ /***/ }),
1020
+
1021
+ /***/ "./blocks/checkout-page/inspector.js":
1022
+ /*!*******************************************!*\
1023
+ !*** ./blocks/checkout-page/inspector.js ***!
1024
+ \*******************************************/
1025
+ /*! exports provided: default */
1026
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1027
+
1028
+ "use strict";
1029
+ __webpack_require__.r(__webpack_exports__);
1030
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Inspector; });
1031
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/classCallCheck */ "./node_modules/@babel/runtime/helpers/classCallCheck.js");
1032
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__);
1033
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/helpers/createClass */ "./node_modules/@babel/runtime/helpers/createClass.js");
1034
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__);
1035
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime/helpers/inherits */ "./node_modules/@babel/runtime/helpers/inherits.js");
1036
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__);
1037
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babel/runtime/helpers/possibleConstructorReturn */ "./node_modules/@babel/runtime/helpers/possibleConstructorReturn.js");
1038
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__);
1039
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @babel/runtime/helpers/getPrototypeOf */ "./node_modules/@babel/runtime/helpers/getPrototypeOf.js");
1040
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__);
1041
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1042
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__);
1043
+
1044
+
1045
+
1046
+
1047
+
1048
+
1049
+
1050
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default()(this, result); }; }
1051
+
1052
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
1053
+
1054
+ /**
1055
+ * Internal block libraries
1056
+ */
1057
+ var __ = wp.i18n.__;
1058
+ var Component = wp.element.Component;
1059
+ var _wp$components = wp.components,
1060
+ PanelBody = _wp$components.PanelBody,
1061
+ PanelRow = _wp$components.PanelRow,
1062
+ SelectControl = _wp$components.SelectControl;
1063
+ var InspectorControls = wp.blockEditor.InspectorControls;
1064
+ /**
1065
+ * Create an Inspector Controls wrapper Component
1066
+ */
1067
+
1068
+ var Inspector = /*#__PURE__*/function (_Component) {
1069
+ _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default()(Inspector, _Component);
1070
+
1071
+ var _super = _createSuper(Inspector);
1072
+
1073
+ function Inspector() {
1074
+ _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default()(this, Inspector);
1075
+
1076
+ return _super.apply(this, arguments);
1077
+ }
1078
+
1079
+ _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default()(Inspector, [{
1080
+ key: "render",
1081
+ value: function render() {
1082
+ var _this$props = this.props,
1083
+ pmpro_default_level = _this$props.attributes.pmpro_default_level,
1084
+ setAttributes = _this$props.setAttributes;
1085
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(InspectorControls, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(SelectControl, {
1086
+ label: __('Membership Level', 'paid-memberships-pro'),
1087
+ help: __('Choose a default level for Membership Checkout.', 'paid-memberships-pro'),
1088
+ value: pmpro_default_level,
1089
+ onChange: function onChange(pmpro_default_level) {
1090
+ return setAttributes({
1091
+ pmpro_default_level: pmpro_default_level
1092
+ });
1093
+ },
1094
+ options: [''].concat(window.pmpro.all_level_values_and_labels)
1095
+ })));
1096
+ }
1097
+ }]);
1098
+
1099
+ return Inspector;
1100
+ }(Component);
1101
+
1102
+
1103
+
1104
+ /***/ }),
1105
+
1106
+ /***/ "./blocks/confirmation-page/block.js":
1107
+ /*!*******************************************!*\
1108
+ !*** ./blocks/confirmation-page/block.js ***!
1109
+ \*******************************************/
1110
+ /*! exports provided: default */
1111
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1112
+
1113
+ "use strict";
1114
+ __webpack_require__.r(__webpack_exports__);
1115
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1116
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
1117
+
1118
+
1119
+ /**
1120
+ * Block: PMPro Membership Confirmation
1121
+ *
1122
+ * Displays the Membership Confirmation template.
1123
+ *
1124
+ */
1125
+
1126
+ /**
1127
+ * Internal block libraries
1128
+ */
1129
+ var __ = wp.i18n.__;
1130
+ var registerBlockType = wp.blocks.registerBlockType;
1131
+ /**
1132
+ * Register block
1133
+ */
1134
+
1135
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/confirmation-page', {
1136
+ title: __('Membership Confirmation Page', 'paid-memberships-pro'),
1137
+ description: __('Displays the member\'s Membership Confirmation after Membership Checkout.', 'paid-memberships-pro'),
1138
+ category: 'pmpro',
1139
+ icon: {
1140
+ background: '#2997c8',
1141
+ foreground: '#ffffff',
1142
+ src: 'yes'
1143
+ },
1144
+ keywords: [__('pmpro', 'paid-memberships-pro')],
1145
+ supports: {},
1146
+ attributes: {},
1147
+ edit: function edit() {
1148
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1149
+ className: "pmpro-block-element"
1150
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1151
+ className: "pmpro-block-title"
1152
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1153
+ className: "pmpro-block-subtitle"
1154
+ }, __('Membership Confirmation Page', 'paid-memberships-pro')))];
1155
+ },
1156
+ save: function save() {
1157
+ return null;
1158
+ }
1159
+ }));
1160
+
1161
+ /***/ }),
1162
+
1163
+ /***/ "./blocks/i18n.js":
1164
+ /*!************************!*\
1165
+ !*** ./blocks/i18n.js ***!
1166
+ \************************/
1167
+ /*! no static exports found */
1168
+ /***/ (function(module, exports) {
1169
+
1170
+ wp.i18n.setLocaleData({
1171
+ '': {}
1172
+ }, 'paid-memberships-pro');
1173
+
1174
+ /***/ }),
1175
+
1176
+ /***/ "./blocks/invoice-page/block.js":
1177
+ /*!**************************************!*\
1178
+ !*** ./blocks/invoice-page/block.js ***!
1179
+ \**************************************/
1180
+ /*! exports provided: default */
1181
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1182
+
1183
+ "use strict";
1184
+ __webpack_require__.r(__webpack_exports__);
1185
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1186
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
1187
+
1188
+
1189
+ /**
1190
+ * Block: PMPro Membership Invoices
1191
+ *
1192
+ * Displays the Membership Invoices template.
1193
+ *
1194
+ */
1195
+
1196
+ /**
1197
+ * Internal block libraries
1198
+ */
1199
+ var __ = wp.i18n.__;
1200
+ var registerBlockType = wp.blocks.registerBlockType;
1201
+ /**
1202
+ * Register block
1203
+ */
1204
+
1205
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/invoice-page', {
1206
+ title: __('Membership Invoice Page', 'paid-memberships-pro'),
1207
+ description: __('Displays the member\'s Membership Invoices.', 'paid-memberships-pro'),
1208
+ category: 'pmpro',
1209
+ icon: {
1210
+ background: '#2997c8',
1211
+ foreground: '#ffffff',
1212
+ src: 'archive'
1213
+ },
1214
+ keywords: [__('pmpro', 'paid-memberships-pro')],
1215
+ supports: {},
1216
+ attributes: {},
1217
+ edit: function edit() {
1218
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1219
+ className: "pmpro-block-element"
1220
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1221
+ className: "pmpro-block-title"
1222
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1223
+ className: "pmpro-block-subtitle"
1224
+ }, __('Membership Invoices', 'paid-memberships-pro')))];
1225
+ },
1226
+ save: function save() {
1227
+ return null;
1228
+ }
1229
+ }));
1230
+
1231
+ /***/ }),
1232
+
1233
+ /***/ "./blocks/levels-page/block.js":
1234
+ /*!*************************************!*\
1235
+ !*** ./blocks/levels-page/block.js ***!
1236
+ \*************************************/
1237
+ /*! exports provided: default */
1238
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1239
+
1240
+ "use strict";
1241
+ __webpack_require__.r(__webpack_exports__);
1242
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1243
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
1244
+
1245
+
1246
+ /**
1247
+ * Block: PMPro Membership Levels
1248
+ *
1249
+ * Displays the Membership Levels template.
1250
+ *
1251
+ */
1252
+
1253
+ /**
1254
+ * Internal block libraries
1255
+ */
1256
+ var __ = wp.i18n.__;
1257
+ var registerBlockType = wp.blocks.registerBlockType;
1258
+ /**
1259
+ * Register block
1260
+ */
1261
+
1262
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/levels-page', {
1263
+ title: __('Membership Levels List', 'paid-memberships-pro'),
1264
+ description: __('Displays a list of Membership Levels. To change the order, go to Memberships > Settings > Levels.', 'paid-memberships-pro'),
1265
+ category: 'pmpro',
1266
+ icon: {
1267
+ background: '#2997c8',
1268
+ foreground: '#ffffff',
1269
+ src: 'list-view'
1270
+ },
1271
+ keywords: [__('pmpro', 'paid-memberships-pro')],
1272
+ supports: {},
1273
+ attributes: {},
1274
+ edit: function edit() {
1275
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1276
+ className: "pmpro-block-element"
1277
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1278
+ className: "pmpro-block-title"
1279
+ }, __('Paid Memberships Pro', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1280
+ className: "pmpro-block-subtitle"
1281
+ }, __('Membership Levels List', 'paid-memberships-pro')))];
1282
+ },
1283
+ save: function save() {
1284
+ return null;
1285
+ }
1286
+ }));
1287
+
1288
+ /***/ }),
1289
+
1290
+ /***/ "./blocks/login/block.js":
1291
+ /*!*******************************!*\
1292
+ !*** ./blocks/login/block.js ***!
1293
+ \*******************************/
1294
+ /*! exports provided: default */
1295
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1296
+
1297
+ "use strict";
1298
+ __webpack_require__.r(__webpack_exports__);
1299
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1300
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
1301
+ /* harmony import */ var _inspector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./inspector */ "./blocks/login/inspector.js");
1302
+
1303
+
1304
+ /**
1305
+ * Block: PMPro Login Form
1306
+ *
1307
+ * Add a login form to any page or post.
1308
+ *
1309
+ */
1310
+
1311
+ /**
1312
+ * Block dependencies
1313
+ */
1314
+
1315
+ /**
1316
+ * Internal block libraries
1317
+ */
1318
+
1319
+ var __ = wp.i18n.__;
1320
+ var registerBlockType = wp.blocks.registerBlockType;
1321
+ var Fragment = wp.element.Fragment;
1322
+ /**
1323
+ * Register block
1324
+ */
1325
+
1326
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType("pmpro/login-form", {
1327
+ title: __("Log in Form", "paid-memberships-pro"),
1328
+ description: __("Displays a Log In Form for Paid Memberships Pro.", "paid-memberships-pro"),
1329
+ category: "pmpro",
1330
+ icon: {
1331
+ background: "#2997c8",
1332
+ foreground: "#ffffff",
1333
+ src: "unlock"
1334
+ },
1335
+ keywords: [__("pmpro", "paid-memberships-pro"), __("login", "paid-memberships-pro"), __("form", "paid-memberships-pro"), __("log in", "paid-memberships-pro")],
1336
+ supports: {},
1337
+ edit: function edit(props) {
1338
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(Fragment, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(_inspector__WEBPACK_IMPORTED_MODULE_1__["default"], props), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1339
+ className: "pmpro-block-element"
1340
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1341
+ className: "pmpro-block-title"
1342
+ }, __("Paid Memberships Pro", "paid-memberships-pro")), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1343
+ className: "pmpro-block-subtitle"
1344
+ }, __("Log in Form", "paid-memberships-pro"))))];
1345
+ },
1346
+ save: function save() {
1347
+ return null;
1348
+ }
1349
+ }));
1350
+
1351
+ /***/ }),
1352
+
1353
+ /***/ "./blocks/login/inspector.js":
1354
+ /*!***********************************!*\
1355
+ !*** ./blocks/login/inspector.js ***!
1356
+ \***********************************/
1357
+ /*! exports provided: default */
1358
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1359
+
1360
+ "use strict";
1361
+ __webpack_require__.r(__webpack_exports__);
1362
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Inspector; });
1363
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/classCallCheck */ "./node_modules/@babel/runtime/helpers/classCallCheck.js");
1364
+ /* harmony import */ var _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__);
1365
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/helpers/createClass */ "./node_modules/@babel/runtime/helpers/createClass.js");
1366
+ /* harmony import */ var _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1__);
1367
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime/helpers/inherits */ "./node_modules/@babel/runtime/helpers/inherits.js");
1368
+ /* harmony import */ var _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2__);
1369
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babel/runtime/helpers/possibleConstructorReturn */ "./node_modules/@babel/runtime/helpers/possibleConstructorReturn.js");
1370
+ /* harmony import */ var _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3__);
1371
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @babel/runtime/helpers/getPrototypeOf */ "./node_modules/@babel/runtime/helpers/getPrototypeOf.js");
1372
+ /* harmony import */ var _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4__);
1373
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1374
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__);
1375
+
1376
+
1377
+
1378
+
1379
+
1380
+
1381
+
1382
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _babel_runtime_helpers_getPrototypeOf__WEBPACK_IMPORTED_MODULE_4___default()(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _babel_runtime_helpers_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_3___default()(this, result); }; }
1383
+
1384
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
1385
+
1386
+ /**
1387
+ * Internal block libraries
1388
+ */
1389
+ var __ = wp.i18n.__;
1390
+ var Component = wp.element.Component;
1391
+ var _wp$components = wp.components,
1392
+ PanelBody = _wp$components.PanelBody,
1393
+ SelectControl = _wp$components.SelectControl,
1394
+ ToggleControl = _wp$components.ToggleControl;
1395
+ var InspectorControls = wp.blockEditor.InspectorControls;
1396
+ /**
1397
+ * Create an Inspector Controls wrapper Component
1398
+ */
1399
+
1400
+ var Inspector = /*#__PURE__*/function (_Component) {
1401
+ _babel_runtime_helpers_inherits__WEBPACK_IMPORTED_MODULE_2___default()(Inspector, _Component);
1402
+
1403
+ var _super = _createSuper(Inspector);
1404
+
1405
+ function Inspector() {
1406
+ _babel_runtime_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default()(this, Inspector);
1407
+
1408
+ return _super.apply(this, arguments);
1409
+ }
1410
+
1411
+ _babel_runtime_helpers_createClass__WEBPACK_IMPORTED_MODULE_1___default()(Inspector, [{
1412
+ key: "render",
1413
+ value: function render() {
1414
+ var _this = this;
1415
+
1416
+ var _this$props = this.props,
1417
+ attributes = _this$props.attributes,
1418
+ setAttributes = _this$props.setAttributes;
1419
+ var display_if_logged_in = attributes.display_if_logged_in,
1420
+ show_menu = attributes.show_menu,
1421
+ show_logout_link = attributes.show_logout_link,
1422
+ location = attributes.location;
1423
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(InspectorControls, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(ToggleControl, {
1424
+ label: __("Display 'Welcome' content when logged in.", "paid-memberships-pro"),
1425
+ checked: display_if_logged_in,
1426
+ onChange: function onChange(value) {
1427
+ _this.props.setAttributes({
1428
+ display_if_logged_in: value
1429
+ });
1430
+ }
1431
+ }), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(ToggleControl, {
1432
+ label: __("Display the 'Log In Widget' menu.", "paid-memberships-pro"),
1433
+ help: __("Assign the menu under Appearance > Menus."),
1434
+ checked: show_menu,
1435
+ onChange: function onChange(value) {
1436
+ _this.props.setAttributes({
1437
+ show_menu: value
1438
+ });
1439
+ }
1440
+ }), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__["createElement"])(ToggleControl, {
1441
+ label: __("Display a 'Log Out' link.", "paid-memberships-pro"),
1442
+ checked: show_logout_link,
1443
+ onChange: function onChange(value) {
1444
+ _this.props.setAttributes({
1445
+ show_logout_link: value
1446
+ });
1447
+ }
1448
+ })));
1449
+ }
1450
+ }]);
1451
+
1452
+ return Inspector;
1453
+ }(Component);
1454
+
1455
+
1456
+
1457
+ /***/ }),
1458
+
1459
+ /***/ "./blocks/member-profile-edit/block.js":
1460
+ /*!*********************************************!*\
1461
+ !*** ./blocks/member-profile-edit/block.js ***!
1462
+ \*********************************************/
1463
+ /*! exports provided: default */
1464
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1465
+
1466
+ "use strict";
1467
+ __webpack_require__.r(__webpack_exports__);
1468
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1469
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
1470
+
1471
+
1472
+ /**
1473
+ * Block: PMPro Member Profile Edit
1474
+ *
1475
+ *
1476
+ */
1477
+
1478
+ /**
1479
+ * Internal block libraries
1480
+ */
1481
+ var __ = wp.i18n.__;
1482
+ var registerBlockType = wp.blocks.registerBlockType;
1483
+ /**
1484
+ * Register block
1485
+ */
1486
+
1487
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType("pmpro/member-profile-edit", {
1488
+ title: __("Member Profile Edit", "paid-memberships-pro"),
1489
+ description: __("Allow member profile editing.", "paid-memberships-pro"),
1490
+ category: "pmpro",
1491
+ icon: {
1492
+ background: "#2997c8",
1493
+ foreground: "#ffffff",
1494
+ src: "admin-users"
1495
+ },
1496
+ keywords: [__("pmpro", "paid-memberships-pro"), __("member", "paid-memberships-pro"), __("profile", "paid-memberships-pro")],
1497
+ edit: function edit(props) {
1498
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1499
+ className: "pmpro-block-element"
1500
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1501
+ className: "pmpro-block-title"
1502
+ }, __("Paid Memberships Pro", "paid-memberships-pro")), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1503
+ className: "pmpro-block-subtitle"
1504
+ }, __("Member Profile Edit", "paid-memberships-pro")));
1505
+ },
1506
+ save: function save() {
1507
+ return null;
1508
+ }
1509
+ }));
1510
+
1511
+ /***/ }),
1512
+
1513
+ /***/ "./blocks/membership/block.js":
1514
+ /*!************************************!*\
1515
+ !*** ./blocks/membership/block.js ***!
1516
+ \************************************/
1517
+ /*! exports provided: default */
1518
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1519
+
1520
+ "use strict";
1521
+ __webpack_require__.r(__webpack_exports__);
1522
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
1523
+ /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
1524
+
1525
+
1526
+ /**
1527
+ * Block: PMPro Membership
1528
+ *
1529
+ *
1530
+ */
1531
+
1532
+ /**
1533
+ * Internal block libraries
1534
+ */
1535
+ var __ = wp.i18n.__;
1536
+ var registerBlockType = wp.blocks.registerBlockType;
1537
+ var _wp$components = wp.components,
1538
+ PanelBody = _wp$components.PanelBody,
1539
+ CheckboxControl = _wp$components.CheckboxControl;
1540
+ var _wp$blockEditor = wp.blockEditor,
1541
+ InspectorControls = _wp$blockEditor.InspectorControls,
1542
+ InnerBlocks = _wp$blockEditor.InnerBlocks;
1543
+ var all_levels = [{
1544
+ value: 0,
1545
+ label: "Non-Members"
1546
+ }].concat(pmpro.all_level_values_and_labels);
1547
+ /**
1548
+ * Register block
1549
+ */
1550
+
1551
+ /* harmony default export */ __webpack_exports__["default"] = (registerBlockType('pmpro/membership', {
1552
+ title: __('Require Membership Block', 'paid-memberships-pro'),
1553
+ description: __('Control the visibility of nested blocks for members or non-members.', 'paid-memberships-pro'),
1554
+ category: 'pmpro',
1555
+ icon: {
1556
+ background: '#2997c8',
1557
+ foreground: '#ffffff',
1558
+ src: 'visibility'
1559
+ },
1560
+ keywords: [__('pmpro', 'paid-memberships-pro')],
1561
+ attributes: {
1562
+ levels: {
1563
+ type: 'array',
1564
+ default: []
1565
+ },
1566
+ uid: {
1567
+ type: 'string',
1568
+ default: ''
1569
+ }
1570
+ },
1571
+ edit: function edit(props) {
1572
+ var _props$attributes = props.attributes,
1573
+ levels = _props$attributes.levels,
1574
+ uid = _props$attributes.uid,
1575
+ setAttributes = props.setAttributes,
1576
+ isSelected = props.isSelected;
1577
+
1578
+ if (uid == '') {
1579
+ var rand = Math.random() + "";
1580
+ setAttributes({
1581
+ uid: rand
1582
+ });
1583
+ } // Build an array of checkboxes for each level.
1584
+
1585
+
1586
+ var checkboxes = all_levels.map(function (level) {
1587
+ function setLevelsAttribute(nowChecked) {
1588
+ if (nowChecked && !levels.some(function (levelID) {
1589
+ return levelID == level.value;
1590
+ })) {
1591
+ // Add the level.
1592
+ var newLevels = levels.slice();
1593
+ newLevels.push(level.value + '');
1594
+ setAttributes({
1595
+ levels: newLevels
1596
+ });
1597
+ } else if (!nowChecked && levels.some(function (levelID) {
1598
+ return levelID == level.value;
1599
+ })) {
1600
+ // Remove the level.
1601
+ var _newLevels = levels.filter(function (levelID) {
1602
+ return levelID != level.value;
1603
+ });
1604
+
1605
+ setAttributes({
1606
+ levels: _newLevels
1607
+ });
1608
+ }
1609
+ }
1610
+
1611
+ return [Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(CheckboxControl, {
1612
+ label: level.label,
1613
+ checked: levels.some(function (levelID) {
1614
+ return levelID == level.value;
1615
+ }),
1616
+ onChange: setLevelsAttribute
1617
+ })];
1618
+ });
1619
+ return [isSelected && Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(InspectorControls, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(PanelBody, null, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1620
+ class: "pmpro-block-inspector-scrollable"
1621
+ }, checkboxes))), isSelected && Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1622
+ className: "pmpro-block-require-membership-element"
1623
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1624
+ className: "pmpro-block-title"
1625
+ }, __('Require Membership', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(PanelBody, null, checkboxes), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(InnerBlocks, {
1626
+ renderAppender: function renderAppender() {
1627
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(InnerBlocks.ButtonBlockAppender, null);
1628
+ },
1629
+ templateLock: false
1630
+ })), !isSelected && Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1631
+ className: "pmpro-block-require-membership-element"
1632
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("span", {
1633
+ className: "pmpro-block-title"
1634
+ }, __('Require Membership', 'paid-memberships-pro')), Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(InnerBlocks, {
1635
+ renderAppender: function renderAppender() {
1636
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(InnerBlocks.ButtonBlockAppender, null);
1637
+ },
1638
+ templateLock: false
1639
+ }))];
1640
+ },
1641
+ save: function save(props) {
1642
+ var className = props.className;
1643
+ return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])("div", {
1644
+ className: className
1645
+ }, Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(InnerBlocks.Content, null));
1646
+ }
1647
+ }));
1648
+
1649
+ /***/ }),
1650
+
1651
+ /***/ "./node_modules/@babel/runtime/helpers/assertThisInitialized.js":
1652
+ /*!**********************************************************************!*\
1653
+ !*** ./node_modules/@babel/runtime/helpers/assertThisInitialized.js ***!
1654
+ \**********************************************************************/
1655
+ /*! no static exports found */
1656
+ /***/ (function(module, exports) {
1657
+
1658
+ function _assertThisInitialized(self) {
1659
+ if (self === void 0) {
1660
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
1661
+ }
1662
+
1663
+ return self;
1664
+ }
1665
+
1666
+ module.exports = _assertThisInitialized;
1667
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1668
+
1669
+ /***/ }),
1670
+
1671
+ /***/ "./node_modules/@babel/runtime/helpers/classCallCheck.js":
1672
+ /*!***************************************************************!*\
1673
+ !*** ./node_modules/@babel/runtime/helpers/classCallCheck.js ***!
1674
+ \***************************************************************/
1675
+ /*! no static exports found */
1676
+ /***/ (function(module, exports) {
1677
+
1678
+ function _classCallCheck(instance, Constructor) {
1679
+ if (!(instance instanceof Constructor)) {
1680
+ throw new TypeError("Cannot call a class as a function");
1681
+ }
1682
+ }
1683
+
1684
+ module.exports = _classCallCheck;
1685
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1686
+
1687
+ /***/ }),
1688
+
1689
+ /***/ "./node_modules/@babel/runtime/helpers/createClass.js":
1690
+ /*!************************************************************!*\
1691
+ !*** ./node_modules/@babel/runtime/helpers/createClass.js ***!
1692
+ \************************************************************/
1693
+ /*! no static exports found */
1694
+ /***/ (function(module, exports) {
1695
+
1696
+ function _defineProperties(target, props) {
1697
+ for (var i = 0; i < props.length; i++) {
1698
+ var descriptor = props[i];
1699
+ descriptor.enumerable = descriptor.enumerable || false;
1700
+ descriptor.configurable = true;
1701
+ if ("value" in descriptor) descriptor.writable = true;
1702
+ Object.defineProperty(target, descriptor.key, descriptor);
1703
+ }
1704
+ }
1705
+
1706
+ function _createClass(Constructor, protoProps, staticProps) {
1707
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
1708
+ if (staticProps) _defineProperties(Constructor, staticProps);
1709
+ return Constructor;
1710
+ }
1711
+
1712
+ module.exports = _createClass;
1713
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1714
+
1715
+ /***/ }),
1716
+
1717
+ /***/ "./node_modules/@babel/runtime/helpers/defineProperty.js":
1718
+ /*!***************************************************************!*\
1719
+ !*** ./node_modules/@babel/runtime/helpers/defineProperty.js ***!
1720
+ \***************************************************************/
1721
+ /*! no static exports found */
1722
+ /***/ (function(module, exports) {
1723
+
1724
+ function _defineProperty(obj, key, value) {
1725
+ if (key in obj) {
1726
+ Object.defineProperty(obj, key, {
1727
+ value: value,
1728
+ enumerable: true,
1729
+ configurable: true,
1730
+ writable: true
1731
+ });
1732
+ } else {
1733
+ obj[key] = value;
1734
+ }
1735
+
1736
+ return obj;
1737
+ }
1738
+
1739
+ module.exports = _defineProperty;
1740
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1741
+
1742
+ /***/ }),
1743
+
1744
+ /***/ "./node_modules/@babel/runtime/helpers/getPrototypeOf.js":
1745
+ /*!***************************************************************!*\
1746
+ !*** ./node_modules/@babel/runtime/helpers/getPrototypeOf.js ***!
1747
+ \***************************************************************/
1748
+ /*! no static exports found */
1749
+ /***/ (function(module, exports) {
1750
+
1751
+ function _getPrototypeOf(o) {
1752
+ module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
1753
+ return o.__proto__ || Object.getPrototypeOf(o);
1754
+ };
1755
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1756
+ return _getPrototypeOf(o);
1757
+ }
1758
+
1759
+ module.exports = _getPrototypeOf;
1760
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1761
+
1762
+ /***/ }),
1763
+
1764
+ /***/ "./node_modules/@babel/runtime/helpers/inherits.js":
1765
+ /*!*********************************************************!*\
1766
+ !*** ./node_modules/@babel/runtime/helpers/inherits.js ***!
1767
+ \*********************************************************/
1768
+ /*! no static exports found */
1769
+ /***/ (function(module, exports, __webpack_require__) {
1770
+
1771
+ var setPrototypeOf = __webpack_require__(/*! ./setPrototypeOf.js */ "./node_modules/@babel/runtime/helpers/setPrototypeOf.js");
1772
+
1773
+ function _inherits(subClass, superClass) {
1774
+ if (typeof superClass !== "function" && superClass !== null) {
1775
+ throw new TypeError("Super expression must either be null or a function");
1776
+ }
1777
+
1778
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
1779
+ constructor: {
1780
+ value: subClass,
1781
+ writable: true,
1782
+ configurable: true
1783
+ }
1784
+ });
1785
+ if (superClass) setPrototypeOf(subClass, superClass);
1786
+ }
1787
+
1788
+ module.exports = _inherits;
1789
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1790
+
1791
+ /***/ }),
1792
+
1793
+ /***/ "./node_modules/@babel/runtime/helpers/possibleConstructorReturn.js":
1794
+ /*!**************************************************************************!*\
1795
+ !*** ./node_modules/@babel/runtime/helpers/possibleConstructorReturn.js ***!
1796
+ \**************************************************************************/
1797
+ /*! no static exports found */
1798
+ /***/ (function(module, exports, __webpack_require__) {
1799
+
1800
+ var _typeof = __webpack_require__(/*! @babel/runtime/helpers/typeof */ "./node_modules/@babel/runtime/helpers/typeof.js")["default"];
1801
+
1802
+ var assertThisInitialized = __webpack_require__(/*! ./assertThisInitialized.js */ "./node_modules/@babel/runtime/helpers/assertThisInitialized.js");
1803
+
1804
+ function _possibleConstructorReturn(self, call) {
1805
+ if (call && (_typeof(call) === "object" || typeof call === "function")) {
1806
+ return call;
1807
+ }
1808
+
1809
+ return assertThisInitialized(self);
1810
+ }
1811
+
1812
+ module.exports = _possibleConstructorReturn;
1813
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1814
+
1815
+ /***/ }),
1816
+
1817
+ /***/ "./node_modules/@babel/runtime/helpers/setPrototypeOf.js":
1818
+ /*!***************************************************************!*\
1819
+ !*** ./node_modules/@babel/runtime/helpers/setPrototypeOf.js ***!
1820
+ \***************************************************************/
1821
+ /*! no static exports found */
1822
+ /***/ (function(module, exports) {
1823
+
1824
+ function _setPrototypeOf(o, p) {
1825
+ module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
1826
+ o.__proto__ = p;
1827
+ return o;
1828
+ };
1829
+
1830
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1831
+ return _setPrototypeOf(o, p);
1832
+ }
1833
+
1834
+ module.exports = _setPrototypeOf;
1835
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1836
+
1837
+ /***/ }),
1838
+
1839
+ /***/ "./node_modules/@babel/runtime/helpers/typeof.js":
1840
+ /*!*******************************************************!*\
1841
+ !*** ./node_modules/@babel/runtime/helpers/typeof.js ***!
1842
+ \*******************************************************/
1843
+ /*! no static exports found */
1844
+ /***/ (function(module, exports) {
1845
+
1846
+ function _typeof(obj) {
1847
+ "@babel/helpers - typeof";
1848
+
1849
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
1850
+ module.exports = _typeof = function _typeof(obj) {
1851
+ return typeof obj;
1852
+ };
1853
+
1854
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1855
+ } else {
1856
+ module.exports = _typeof = function _typeof(obj) {
1857
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
1858
+ };
1859
+
1860
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1861
+ }
1862
+
1863
+ return _typeof(obj);
1864
+ }
1865
+
1866
+ module.exports = _typeof;
1867
+ module.exports["default"] = module.exports, module.exports.__esModule = true;
1868
+
1869
+ /***/ }),
1870
+
1871
+ /***/ "@wordpress/element":
1872
+ /*!*********************************!*\
1873
+ !*** external ["wp","element"] ***!
1874
+ \*********************************/
1875
+ /*! no static exports found */
1876
+ /***/ (function(module, exports) {
1877
+
1878
+ (function() { module.exports = window["wp"]["element"]; }());
1879
+
1880
+ /***/ })
1881
+
1882
+ /******/ });
1883
+ //# sourceMappingURL=blocks.build.js.map
js/pmpro-admin.js CHANGED
@@ -4,7 +4,7 @@
4
  * @param text The prompt, i.e. are you sure?
5
  * @param url The url to redirect to.
6
  */
7
- function pmpro_askfirst( text, url ) {
8
  var answer = window.confirm( text );
9
 
10
  if ( answer ) {
@@ -43,7 +43,7 @@ function pmpro_toggle_elements_by_selector( selector, checked ) {
43
  * @since v2.1
44
  */
45
  jQuery(document).ready(function() {
46
- jQuery( 'input[pmpro_toggle_trigger_for]' ).change( function() {
47
  pmpro_toggle_elements_by_selector( jQuery( this ).attr( 'pmpro_toggle_trigger_for' ), jQuery( this ).prop( 'checked' ) );
48
  });
49
  });
@@ -81,20 +81,19 @@ jQuery(document).ready(function() {
81
  }
82
 
83
  // Disable the webhook buttons if the API keys aren't complete yet.
84
- jQuery('#stripe_publishablekey,#stripe_secretkey').bind('change keyup', function() {
85
  pmpro_stripe_check_api_keys();
86
- });
87
- pmpro_stripe_check_api_keys();
88
 
89
  // AJAX call to create webhook.
90
- jQuery('#pmpro_stripe_create_webhook').click(function(event){
91
  event.preventDefault();
92
 
93
  var postData = {
94
  action: 'pmpro_stripe_create_webhook',
95
- secretkey: jQuery('#stripe_secretkey').val(),
96
  }
97
-
98
  jQuery.ajax({
99
  type: "POST",
100
  data: postData,
@@ -120,12 +119,12 @@ jQuery(document).ready(function() {
120
  });
121
 
122
  // AJAX call to delete webhook.
123
- jQuery('#pmpro_stripe_delete_webhook').click(function(event){
124
  event.preventDefault();
125
 
126
  var postData = {
127
  action: 'pmpro_stripe_delete_webhook',
128
- secretkey: jQuery('#stripe_secretkey').val(),
129
  }
130
 
131
  jQuery.ajax({
@@ -150,16 +149,294 @@ jQuery(document).ready(function() {
150
  }
151
  }
152
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  });
154
  });
155
 
156
  // Disable the webhook buttons if the API keys aren't complete yet.
157
- function pmpro_stripe_check_api_keys() {
158
- if( jQuery('#stripe_publishablekey').val().length > 0 && jQuery('#stripe_secretkey').val().length > 0 ) {
159
  jQuery('#pmpro_stripe_create_webhook').removeClass('disabled');
160
  jQuery('#pmpro_stripe_create_webhook').addClass('button-secondary');
161
  } else {
162
  jQuery('#pmpro_stripe_create_webhook').removeClass('button-secondary');
163
  jQuery('#pmpro_stripe_create_webhook').addClass('disabled');
164
  }
165
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  * @param text The prompt, i.e. are you sure?
5
  * @param url The url to redirect to.
6
  */
7
+ function pmpro_askfirst( text, url ) {
8
  var answer = window.confirm( text );
9
 
10
  if ( answer ) {
43
  * @since v2.1
44
  */
45
  jQuery(document).ready(function() {
46
+ jQuery( 'input[pmpro_toggle_trigger_for]' ).on( 'change', function() {
47
  pmpro_toggle_elements_by_selector( jQuery( this ).attr( 'pmpro_toggle_trigger_for' ), jQuery( this ).prop( 'checked' ) );
48
  });
49
  });
81
  }
82
 
83
  // Disable the webhook buttons if the API keys aren't complete yet.
84
+ jQuery('#stripe_publishablekey,#stripe_secretkey').on('change keyup', function() {
85
  pmpro_stripe_check_api_keys();
86
+ });
87
+ pmpro_stripe_check_api_keys();
88
 
89
  // AJAX call to create webhook.
90
+ jQuery('#pmpro_stripe_create_webhook').on( 'click', function(event){
91
  event.preventDefault();
92
 
93
  var postData = {
94
  action: 'pmpro_stripe_create_webhook',
95
+ secretkey: pmpro_stripe_get_secretkey(),
96
  }
 
97
  jQuery.ajax({
98
  type: "POST",
99
  data: postData,
119
  });
120
 
121
  // AJAX call to delete webhook.
122
+ jQuery('#pmpro_stripe_delete_webhook').on( 'click', function(event){
123
  event.preventDefault();
124
 
125
  var postData = {
126
  action: 'pmpro_stripe_delete_webhook',
127
+ secretkey: pmpro_stripe_get_secretkey(),
128
  }
129
 
130
  jQuery.ajax({
149
  }
150
  }
151
  })
152
+ });
153
+
154
+ // AJAX call to rebuild webhook.
155
+ jQuery('#pmpro_stripe_rebuild_webhook').on( 'click', function(event){
156
+ event.preventDefault();
157
+
158
+ var postData = {
159
+ action: 'pmpro_stripe_rebuild_webhook',
160
+ secretkey: pmpro_stripe_get_secretkey(),
161
+ }
162
+
163
+ jQuery.ajax({
164
+ type: "POST",
165
+ data: postData,
166
+ url: ajaxurl,
167
+ success: function( response ) {
168
+ response = jQuery.parseJSON( response );
169
+ ///console.log( response );
170
+
171
+ jQuery( '#pmpro_stripe_webhook_notice' ).parent('div').removeClass('error')
172
+ jQuery( '#pmpro_stripe_webhook_notice' ).parent('div').removeClass('notice-success')
173
+
174
+ if ( response.notice ) {
175
+ jQuery('#pmpro_stripe_webhook_notice').parent('div').addClass(response.notice);
176
+ }
177
+ if ( response.message ) {
178
+ jQuery('#pmpro_stripe_webhook_notice').html(response.message);
179
+ }
180
+ if ( response.success ) {
181
+ jQuery('#pmpro_stripe_create_webhook').hide();
182
+ }
183
+ }
184
+ })
185
  });
186
  });
187
 
188
  // Disable the webhook buttons if the API keys aren't complete yet.
189
+ function pmpro_stripe_check_api_keys() {
190
+ if( ( jQuery('#stripe_publishablekey').val().length > 0 && jQuery('#stripe_secretkey').val().length > 0 ) || jQuery('#live_stripe_connect_secretkey').val().length > 0 ) {
191
  jQuery('#pmpro_stripe_create_webhook').removeClass('disabled');
192
  jQuery('#pmpro_stripe_create_webhook').addClass('button-secondary');
193
  } else {
194
  jQuery('#pmpro_stripe_create_webhook').removeClass('button-secondary');
195
  jQuery('#pmpro_stripe_create_webhook').addClass('disabled');
196
  }
197
+ }
198
+
199
+ function pmpro_stripe_get_secretkey() {
200
+ // We can't do the webhook calls with the Connect keys anyway,
201
+ // so we just look for the legacy key here.
202
+ if ( jQuery('#stripe_secretkey').val().length > 0 ) {
203
+ return jQuery('#stripe_secretkey').val();
204
+ } else {
205
+ return '';
206
+ }
207
+ }
208
+
209
+ // EMAIL TEMPLATES.
210
+ jQuery(document).ready(function($) {
211
+
212
+ /* Variables */
213
+ var template, disabled, $subject, $editor, $testemail;
214
+ $subject = $("#pmpro_email_template_subject").closest("tr");
215
+ $editor = $("#wp-email_template_body-wrap");
216
+ $testemail = $("#test_email_address").closest("tr");
217
+
218
+ $(".hide-while-loading").hide();
219
+ $(".controls").hide();
220
+
221
+ /* PMPro Email Template Switcher */
222
+ $("#pmpro_email_template_switcher").change(function() {
223
+
224
+ $(".status_message").hide();
225
+ template = $(this).val();
226
+
227
+ //get template data
228
+ if (template)
229
+ pmpro_get_template(template);
230
+ else {
231
+ $(".hide-while-loading").hide();
232
+ $(".controls").hide();
233
+ }
234
+ });
235
+
236
+ $("#pmpro_submit_template_data").click(function() {
237
+ pmpro_save_template()
238
+ });
239
+
240
+ $("#pmpro_reset_template_data").click(function() {
241
+ pmpro_reset_template();
242
+ });
243
+
244
+ $("#pmpro_email_template_disable").click(function(e) {
245
+ pmpro_disable_template();
246
+ });
247
+
248
+ $("#send_test_email").click(function(e) {
249
+ pmpro_save_template().done(setTimeout(function(){pmpro_send_test_email();}, '1000'));
250
+ });
251
+
252
+ /* Functions */
253
+ function pmpro_get_template(template) {
254
+
255
+ //hide stuff and show ajax spinner
256
+ $(".hide-while-loading").hide();
257
+ $("#pmproet-spinner").show();
258
+
259
+ //get template data
260
+ $data = {
261
+ template: template,
262
+ action: 'pmpro_email_templates_get_template_data',
263
+ security: $('input[name=security]').val()
264
+ };
265
+
266
+ //console.log( $data );
267
+
268
+ $.post(ajaxurl, $data, function(response) {
269
+ var template_data = JSON.parse(response);
270
+
271
+ //show/hide stuff
272
+ $("#pmproet-spinner").hide();
273
+ $(".controls").show();
274
+ $(".hide-while-loading").show();
275
+ $(".status").hide();
276
+
277
+ //change disable text
278
+ if (template == 'header' || template === 'footer') {
279
+
280
+ $subject.hide();
281
+ $testemail.hide();
282
+
283
+ if(template == 'header')
284
+ $("#disable_label").text("Disable email header for all PMPro emails?");
285
+ else
286
+ $("#disable_label").text("Disable email footer for all PMPro emails?");
287
+
288
+ //hide description
289
+ $("#disable_description").hide();
290
+ }
291
+ else {
292
+ $testemail.show();
293
+ $("#disable_label").text("Disable this email?");
294
+ $("#disable_description").show().text("PMPro emails with this template will not be sent.");
295
+ }
296
+
297
+ // populate help text, subject, and body
298
+ $('#pmpro_email_template_help_text').text(template_data['help_text']);
299
+ $('#pmpro_email_template_subject').val(template_data['subject']);
300
+ $('#pmpro_email_template_body').val(template_data['body']);
301
+
302
+ // disable form
303
+ disabled = template_data['disabled'];
304
+ pmpro_toggle_form_disabled(disabled);
305
+ });
306
+ }
307
+
308
+ function pmpro_save_template() {
309
+
310
+ $("#submit_template_data").attr("disabled", true);
311
+ $(".status").hide();
312
+ // console.log(template);
313
+
314
+ $data = {
315
+ template: template,
316
+ subject: $("#pmpro_email_template_subject").val(),
317
+ body: $("#pmpro_email_template_body").val(),
318
+ action: 'pmpro_email_templates_save_template_data',
319
+ security: $('input[name=security]').val()
320
+ };
321
+ $.post(ajaxurl, $data, function(response) {
322
+ if(response != 0) {
323
+ $(".status_message_wrapper").addClass('updated');
324
+ }
325
+ else {
326
+ $(".status_message_wrapper").addClass("error");
327
+ }
328
+ $("#submit_template_data").attr("disabled", false);
329
+ $(".status_message").html(response);
330
+ $(".status").show();
331
+ $(".status_message").show();
332
+ });
333
+
334
+ return $.Deferred().resolve();
335
+ }
336
+
337
+ function pmpro_reset_template() {
338
+
339
+ var r = confirm('Are you sure? Your current template settings will be deleted permanently.');
340
+
341
+ if(!r) return false;
342
+
343
+ $data = {
344
+ template: template,
345
+ action: 'pmpro_email_templates_reset_template_data',
346
+ security: $('input[name=security]').val()
347
+ };
348
+ $.post(ajaxurl, $data, function(response) {
349
+ var template_data = $.parseJSON(response);
350
+ $('#pmpro_email_template_subject').val(template_data['subject']);
351
+ $('#pmpro_email_template_body').val(template_data['body']);
352
+ });
353
+
354
+ return true;
355
+ }
356
+
357
+ function pmpro_disable_template() {
358
+
359
+ //update wp_options
360
+ data = {
361
+ template: template,
362
+ action: 'pmpro_email_templates_disable_template',
363
+ disabled: $("#pmpro_email_template_disable").is(":checked"),
364
+ security: $('input[name=security]').val()
365
+ };
366
+
367
+ $.post(ajaxurl, data, function(response) {
368
+
369
+ response = JSON.parse(response);
370
+
371
+ //failure
372
+ if(response['result'] == false) {
373
+ $(".status_message_wrapper").addClass("error");
374
+ $(".status_message").show().text("There was an error updating your template settings.");
375
+ }
376
+ else {
377
+ if(response['status'] == 'true') {
378
+ $(".status_message_wrapper").addClass("updated");
379
+ $(".status_message").show().text("Template Disabled");
380
+ }
381
+ else {
382
+ $(".status_message_wrapper").addClass("updated");
383
+ $(".status_message").show().text("Template Enabled");
384
+ }
385
+ }
386
+
387
+ $(".hide-while-loading").show();
388
+
389
+ disabled = response['status'];
390
+
391
+ pmpro_toggle_form_disabled(disabled);
392
+ });
393
+ }
394
+
395
+ function pmpro_send_test_email() {
396
+
397
+ //hide stuff and show ajax spinner
398
+ $(".hide-while-loading").hide();
399
+ $("#pmproet-spinner").show();
400
+
401
+ data = {
402
+ template: template,
403
+ email: $("#test_email_address").val(),
404
+ action: 'pmpro_email_templates_send_test',
405
+ security: $('input[name=security]').val()
406
+ };
407
+
408
+ $.post(ajaxurl, data, function(success) {
409
+ //show/hide stuff
410
+ $("#pmproet-spinner").hide();
411
+ $(".controls").show();
412
+ $(".hide-while-loading").show();
413
+
414
+ if(success) {
415
+ $(".status_message_wrapper").addClass("updated").removeClass("error");
416
+ $(".status_message").show().text("Test email sent successfully.");
417
+ }
418
+ else {
419
+ $(".status_message_wrapper").addClass("error").removeClass("updated");
420
+ $(".status_message").show().text("Test email failed.");
421
+ }
422
+
423
+ })
424
+ }
425
+
426
+ function pmpro_toggle_form_disabled(disabled) {
427
+ if(disabled == 'true') {
428
+ $("#pmpro_email_template_disable").prop('checked', true);
429
+ $("#pmpro_email_template_body").attr('readonly', 'readonly').attr('disabled', 'disabled');
430
+ $("#pmpro_email_template_subject").attr('readonly', 'readonly').attr('disabled', 'disabled');
431
+ $(".controls").hide();
432
+ }
433
+ else {
434
+ $("#pmpro_email_template_disable").prop('checked', false);
435
+ $("#pmpro_email_template_body").removeAttr('readonly','readonly').removeAttr('disabled', 'disabled');
436
+ $("#pmpro_email_template_subject").removeAttr('readonly','readonly').removeAttr('disabled', 'disabled');
437
+ $(".controls").show();
438
+ }
439
+
440
+ }
441
+
442
+ });
js/pmpro-login.js CHANGED
@@ -12,7 +12,13 @@ jQuery(document).ready(function(){
12
 
13
  var strength;
14
  if ( pass1 != '' ) {
15
- strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputBlacklist(), pass1 );
 
 
 
 
 
 
16
  } else {
17
  strength = -1;
18
  }
@@ -66,4 +72,4 @@ jQuery(document).ready(function(){
66
  pmpro_check_password_strength( jQuery( '#pass1' ) );
67
  });
68
  }
69
- });
12
 
13
  var strength;
14
  if ( pass1 != '' ) {
15
+ // Call the disallowed list method corresponding to appropriate WP version.
16
+ const disallowedList = ( 'function' == typeof wp.passwordStrength.userInputDisallowedList )
17
+ ? wp.passwordStrength.userInputDisallowedList()
18
+ : wp.passwordStrength.userInputBlacklist();
19
+
20
+ strength = wp.passwordStrength.meter( pass1, disallowedList, pass1 );
21
+
22
  } else {
23
  strength = -1;
24
  }
72
  pmpro_check_password_strength( jQuery( '#pass1' ) );
73
  });
74
  }
75
+ });
js/pmpro-stripe.js CHANGED
@@ -5,10 +5,19 @@ jQuery( document ).ready( function( $ ) {
5
 
6
  var stripe, elements, cardNumber, cardExpiry, cardCvc;
7
 
8
- // Identify with Stripe.
9
- stripe = Stripe( pmproStripe.publishableKey );
 
 
 
 
 
 
10
  elements = stripe.elements();
11
 
 
 
 
12
  // Create Elements.
13
  cardNumber = elements.create('cardNumber');
14
  cardExpiry = elements.create('cardExpiry');
@@ -25,35 +34,39 @@ jQuery( document ).ready( function( $ ) {
25
  cardCvc.mount('#CVV');
26
  }
27
 
28
- // Handle authentication for charge if required.
 
 
 
 
 
 
 
 
 
29
  if ( 'undefined' !== typeof( pmproStripe.paymentIntent ) ) {
30
  if ( 'requires_action' === pmproStripe.paymentIntent.status ) {
31
- // On submit disable its submit button
32
- $('input[type=submit]', this).attr('disabled', 'disabled');
33
- $('input[type=image]', this).attr('disabled', 'disabled');
34
- $('#pmpro_processing_message').css('visibility', 'visible');
35
  stripe.handleCardAction( pmproStripe.paymentIntent.client_secret )
36
- .then( stripeResponseHandler );
37
  }
38
  }
39
-
40
- // Handle authentication for subscription if required.
41
  if ( 'undefined' !== typeof( pmproStripe.setupIntent ) ) {
42
  if ( 'requires_action' === pmproStripe.setupIntent.status ) {
43
- // On submit disable its submit button
44
- $('input[type=submit]', this).attr('disabled', 'disabled');
45
- $('input[type=image]', this).attr('disabled', 'disabled');
46
- $('#pmpro_processing_message').css('visibility', 'visible');
47
  stripe.handleCardSetup( pmproStripe.setupIntent.client_secret )
48
- .then( stripeResponseHandler );
49
  }
50
  }
51
 
 
 
 
52
  // Set require billing var if not set yet.
53
  if ( typeof pmpro_require_billing === 'undefined' ) {
54
  pmpro_require_billing = pmproStripe.pmpro_require_billing;
55
  }
56
-
57
  $( '.pmpro_form' ).submit( function( event ) {
58
  var name, address;
59
 
@@ -62,7 +75,7 @@ jQuery( document ).ready( function( $ ) {
62
 
63
  // Double check in case a discount code made the level free.
64
  if ( typeof pmpro_require_billing === 'undefined' || pmpro_require_billing ) {
65
-
66
  if ( pmproStripe.verifyAddress ) {
67
  address = {
68
  line1: $( '#baddress1' ).val(),
@@ -78,13 +91,14 @@ jQuery( document ).ready( function( $ ) {
78
  if ( $( '#bfirstname' ).length && $( '#blastname' ).length ) {
79
  name = $.trim( $( '#bfirstname' ).val() + ' ' + $( '#blastname' ).val() );
80
  }
81
-
 
82
  stripe.createPaymentMethod( 'card', cardNumber, {
83
  billing_details: {
84
  address: address,
85
  name: name,
86
  }
87
- }).then( stripeResponseHandler );
88
 
89
  // Prevent the form from submitting with the default action.
90
  return false;
@@ -94,15 +108,102 @@ jQuery( document ).ready( function( $ ) {
94
  }
95
  });
96
 
97
- // Handle the response from Stripe.
98
- function stripeResponseHandler( response ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
- var form, data, card, paymentMethodId, customerId;
101
 
102
  form = $('#pmpro_form, .pmpro_form');
103
 
104
  if (response.error) {
105
-
106
  // Re-enable the submit button.
107
  $('.pmpro_btn-submit-checkout,.pmpro_btn-submit').removeAttr('disabled');
108
 
@@ -113,7 +214,7 @@ jQuery( document ).ready( function( $ ) {
113
  $( '#pmpro_message' ).text( response.error.message ).addClass( 'pmpro_error' ).removeClass( 'pmpro_alert' ).removeClass( 'pmpro_success' ).show();
114
 
115
  } else if ( response.paymentMethod ) {
116
-
117
  paymentMethodId = response.paymentMethod.id;
118
  card = response.paymentMethod.card;
119
 
@@ -136,14 +237,10 @@ jQuery( document ).ready( function( $ ) {
136
  form.get(0).submit();
137
 
138
  } else if ( response.paymentIntent || response.setupIntent ) {
139
-
140
  // success message
141
  $( '#pmpro_message' ).text( pmproStripe.msgAuthenticationValidated ).addClass( 'pmpro_success' ).removeClass( 'pmpro_alert' ).removeClass( 'pmpro_error' ).show();
142
-
143
- customerId = pmproStripe.paymentIntent
144
- ? pmproStripe.paymentIntent.customer
145
- : pmproStripe.setupIntent.customer;
146
-
147
  paymentMethodId = pmproStripe.paymentIntent
148
  ? pmproStripe.paymentIntent.payment_method.id
149
  : pmproStripe.setupIntent.payment_method.id;
@@ -152,17 +249,13 @@ jQuery( document ).ready( function( $ ) {
152
  ? pmproStripe.paymentIntent.payment_method.card
153
  : pmproStripe.setupIntent.payment_method.card;
154
 
155
- if ( pmproStripe.paymentIntent ) {
156
  form.append( '<input type="hidden" name="payment_intent_id" value="' + pmproStripe.paymentIntent.id + '" />' );
157
  }
158
  if ( pmproStripe.setupIntent ) {
159
  form.append( '<input type="hidden" name="setup_intent_id" value="' + pmproStripe.setupIntent.id + '" />' );
160
- form.append( '<input type="hidden" name="subscription_id" value="' + pmproStripe.subscription.id + '" />' );
161
  }
162
 
163
- // Insert the Customer ID into the form so it gets submitted to the server.
164
- form.append( '<input type="hidden" name="customer_id" value="' + customerId + '" />' );
165
-
166
  // Insert the PaymentMethod ID into the form so it gets submitted to the server.
167
  form.append( '<input type="hidden" name="payment_method_id" value="' + paymentMethodId + '" />' );
168
 
5
 
6
  var stripe, elements, cardNumber, cardExpiry, cardCvc;
7
 
8
+ /**
9
+ * Identify with Stripe.
10
+ */
11
+ if ( pmproStripe.user_id ) {
12
+ stripe = Stripe( pmproStripe.publishableKey, { stripeAccount: pmproStripe.user_id, locale: 'auto' } );
13
+ } else {
14
+ stripe = Stripe( pmproStripe.publishableKey, { locale: 'auto' } );
15
+ }
16
  elements = stripe.elements();
17
 
18
+ /**
19
+ * Set up default credit card fields.
20
+ */
21
  // Create Elements.
22
  cardNumber = elements.create('cardNumber');
23
  cardExpiry = elements.create('cardExpiry');
34
  cardCvc.mount('#CVV');
35
  }
36
 
37
+ /**
38
+ * Handle request for card authentication. Only used after initial
39
+ * checkout form has been submitted with a valid payment method.
40
+ */
41
+ function pmpro_set_checkout_for_stripe_card_authentication() {
42
+ $('input[type=submit]', this).attr('disabled', 'disabled');
43
+ $('input[type=image]', this).attr('disabled', 'disabled');
44
+ $('#pmpro_processing_message').css('visibility', 'visible');
45
+ }
46
+ // Check if payment intent (charge) requires authentication.
47
  if ( 'undefined' !== typeof( pmproStripe.paymentIntent ) ) {
48
  if ( 'requires_action' === pmproStripe.paymentIntent.status ) {
49
+ pmpro_set_checkout_for_stripe_card_authentication();
 
 
 
50
  stripe.handleCardAction( pmproStripe.paymentIntent.client_secret )
51
+ .then( pmpro_stripeResponseHandler );
52
  }
53
  }
54
+ // Check if payment intent (subscription) requires authentication.
 
55
  if ( 'undefined' !== typeof( pmproStripe.setupIntent ) ) {
56
  if ( 'requires_action' === pmproStripe.setupIntent.status ) {
57
+ pmpro_set_checkout_for_stripe_card_authentication();
 
 
 
58
  stripe.handleCardSetup( pmproStripe.setupIntent.client_secret )
59
+ .then( pmpro_stripeResponseHandler );
60
  }
61
  }
62
 
63
+ /**
64
+ * Set up submit behavior for checkout form.
65
+ */
66
  // Set require billing var if not set yet.
67
  if ( typeof pmpro_require_billing === 'undefined' ) {
68
  pmpro_require_billing = pmproStripe.pmpro_require_billing;
69
  }
 
70
  $( '.pmpro_form' ).submit( function( event ) {
71
  var name, address;
72
 
75
 
76
  // Double check in case a discount code made the level free.
77
  if ( typeof pmpro_require_billing === 'undefined' || pmpro_require_billing ) {
78
+ // Get the data needed to create a payment method for this checkout.
79
  if ( pmproStripe.verifyAddress ) {
80
  address = {
81
  line1: $( '#baddress1' ).val(),
91
  if ( $( '#bfirstname' ).length && $( '#blastname' ).length ) {
92
  name = $.trim( $( '#bfirstname' ).val() + ' ' + $( '#blastname' ).val() );
93
  }
94
+
95
+ // Create the payment method.
96
  stripe.createPaymentMethod( 'card', cardNumber, {
97
  billing_details: {
98
  address: address,
99
  name: name,
100
  }
101
+ }).then( pmpro_stripeResponseHandler );
102
 
103
  // Prevent the form from submitting with the default action.
104
  return false;
108
  }
109
  });
110
 
111
+ /**
112
+ * Set up payment request button.
113
+ */
114
+ // Check if Payment Request Button is enabled.
115
+ if ( $('#payment-request-button').length ) {
116
+ var paymentRequest = null;
117
+
118
+ // Get the level price so that information can be shown in payment request popup
119
+ jQuery.noConflict().ajax({
120
+ url: pmproStripe.restUrl + 'pmpro/v1/checkout_levels',
121
+ dataType: 'json',
122
+ data: jQuery( "#pmpro_form" ).serialize(),
123
+ success: function(data) {
124
+ if ( data.hasOwnProperty('initial_payment') ) {
125
+ // Build payment request button.
126
+ paymentRequest = stripe.paymentRequest({
127
+ country: pmproStripe.accountCountry,
128
+ currency: pmproStripe.currency,
129
+ total: {
130
+ label: pmproStripe.siteName,
131
+ amount: Math.round( data.initial_payment * 100 ),
132
+ },
133
+ requestPayerName: true,
134
+ requestPayerEmail: true,
135
+ });
136
+ var prButton = elements.create('paymentRequestButton', {
137
+ paymentRequest: paymentRequest,
138
+ });
139
+ // Mount payment request button.
140
+ paymentRequest.canMakePayment().then(function(result) {
141
+ if (result) {
142
+ prButton.mount('#payment-request-button');
143
+ } else {
144
+ $('#payment-request-button').hide();
145
+ }
146
+ });
147
+ // Handle payment request button confirmation.
148
+ paymentRequest.on('paymentmethod', function( event ) {
149
+ // Do not let customer submit the form again.
150
+ $('#pmpro_btn-submit').attr('disabled', 'disabled');
151
+ $('#pmpro_processing_message').css('visibility', 'visible');
152
+ $('#payment-request-button').hide();
153
+ /*
154
+ Close the payment request interface immeditately. This is not the intended
155
+ implementation from Stripe, but we are submitting the payment method
156
+ through our default checkout process instead of letting Stripe
157
+ process it through the payment request button. Closing immediately also
158
+ prevents timeouts during the payment request workflow that have caused
159
+ issues on slower sites in the past.
160
+ */
161
+ event.complete('success');
162
+ pmpro_stripeResponseHandler( event );
163
+ });
164
+ }
165
+ }
166
+ });
167
+ // Hide payment request button on form submit to prevent double charges.
168
+ jQuery('form').submit(function(){
169
+ jQuery('#payment-request-button').hide();
170
+ });
171
+ // Update price shown in payment request button if price changes.
172
+ function stripeUpdatePaymentRequestButton() {
173
+ jQuery.noConflict().ajax({
174
+ url: pmproStripe.restUrl + 'pmpro/v1/checkout_levels',
175
+ dataType: 'json',
176
+ data: jQuery( "#pmpro_form" ).serialize(),
177
+ success: function(data) {
178
+ if ( data.hasOwnProperty('initial_payment') ) {
179
+ paymentRequest.update({
180
+ total: {
181
+ label: pmproStripe.siteName,
182
+ amount: Math.round( data.initial_payment * 100 ),
183
+ },
184
+ });
185
+ }
186
+ }
187
+ });
188
+ }
189
+ if ( pmproStripe.updatePaymentRequestButton ) {
190
+ $(".pmpro_alter_price").change(function(){
191
+ stripeUpdatePaymentRequestButton();
192
+ });
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Handle the response from Stripe.
198
+ */
199
+ function pmpro_stripeResponseHandler( response ) {
200
 
201
+ var form, data, card, paymentMethodId;
202
 
203
  form = $('#pmpro_form, .pmpro_form');
204
 
205
  if (response.error) {
206
+ // There was an issue with the payment method supplied or card authentication failed.
207
  // Re-enable the submit button.
208
  $('.pmpro_btn-submit-checkout,.pmpro_btn-submit').removeAttr('disabled');
209
 
214
  $( '#pmpro_message' ).text( response.error.message ).addClass( 'pmpro_error' ).removeClass( 'pmpro_alert' ).removeClass( 'pmpro_success' ).show();
215
 
216
  } else if ( response.paymentMethod ) {
217
+ // A payment method was created successfully. Submit the checkout form and finish the checkout in PHP.
218
  paymentMethodId = response.paymentMethod.id;
219
  card = response.paymentMethod.card;
220
 
237
  form.get(0).submit();
238
 
239
  } else if ( response.paymentIntent || response.setupIntent ) {
240
+ // Card authentication was successful. Finish the checkout in PHP.
241
  // success message
242
  $( '#pmpro_message' ).text( pmproStripe.msgAuthenticationValidated ).addClass( 'pmpro_success' ).removeClass( 'pmpro_alert' ).removeClass( 'pmpro_error' ).show();
243
+
 
 
 
 
244
  paymentMethodId = pmproStripe.paymentIntent
245
  ? pmproStripe.paymentIntent.payment_method.id
246
  : pmproStripe.setupIntent.payment_method.id;
249
  ? pmproStripe.paymentIntent.payment_method.card
250
  : pmproStripe.setupIntent.payment_method.card;
251
 
252
+ if ( pmproStripe.paymentIntent ) {
253
  form.append( '<input type="hidden" name="payment_intent_id" value="' + pmproStripe.paymentIntent.id + '" />' );
254
  }
255
  if ( pmproStripe.setupIntent ) {
256
  form.append( '<input type="hidden" name="setup_intent_id" value="' + pmproStripe.setupIntent.id + '" />' );
 
257
  }
258
 
 
 
 
259
  // Insert the PaymentMethod ID into the form so it gets submitted to the server.
260
  form.append( '<input type="hidden" name="payment_method_id" value="' + paymentMethodId + '" />' );
261
 
languages/email/de_DE/admin_change.html CHANGED
@@ -1,7 +1,7 @@
1
- <p>Ein Administrator von !!sitename!! hat Ihr Paket ge�ndert.</p>
2
 
3
  <p>!!membership_change!!.</p>
4
 
5
- <p>Wenn Sie diese �nderung nicht veranlasst haben und mehr Informationen ben�tigen, kotaktieren Sie uns bitte unter !!siteemail!!</p>
6
 
7
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Ein Administrator von !!sitename!! hat Ihr Paket geändert.</p>
2
 
3
  <p>!!membership_change!!.</p>
4
 
5
+ <p>Wenn Sie diese Änderung nicht veranlasst haben und mehr Informationen benötigen, kotaktieren Sie uns bitte unter !!siteemail!!</p>
6
 
7
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/admin_change_admin.html CHANGED
@@ -1,4 +1,4 @@
1
- <p>Ein Administrator von !!sitename!! hat das Paket ge�ndert.</p>
2
 
3
  <p>!!membership_change!!.</p>
4
 
1
+ <p>Ein Administrator von !!sitename!! hat das Paket geändert.</p>
2
 
3
  <p>!!membership_change!!.</p>
4
 
languages/email/de_DE/billing.html CHANGED
@@ -1,4 +1,4 @@
1
- <p>Ihre Zahlungsinformationen bei !!sitename!! wurden ge�ndert.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>
@@ -8,9 +8,9 @@
8
 
9
  <p>
10
  !!cardtype!!: !!accountnumber!!<br />
11
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
12
  </p>
13
 
14
- <p>Wenn Sie diese �nderung nicht veranlasst haben, kontaktieren Sie uns bitte unter !!siteemail!!</p>
15
 
16
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Ihre Zahlungsinformationen bei !!sitename!! wurden geändert.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>
8
 
9
  <p>
10
  !!cardtype!!: !!accountnumber!!<br />
11
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
12
  </p>
13
 
14
+ <p>Wenn Sie diese Änderung nicht veranlasst haben, kontaktieren Sie uns bitte unter !!siteemail!!</p>
15
 
16
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/billing_admin.html CHANGED
@@ -1,4 +1,4 @@
1
- <p>Die Zahlungsinformationen f�r !!display_name!! bei !!sitename!! wurden ge�ndert.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>
@@ -11,7 +11,7 @@
11
 
12
  <p>
13
  !!cardtype!!: !!accountnumber!!<br />
14
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
15
  </p>
16
 
17
  <p>Wordpress Login: !!login_link!!</p>
1
+ <p>Die Zahlungsinformationen für !!display_name!! bei !!sitename!! wurden geändert.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>
11
 
12
  <p>
13
  !!cardtype!!: !!accountnumber!!<br />
14
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
15
  </p>
16
 
17
  <p>Wordpress Login: !!login_link!!</p>
languages/email/de_DE/billing_failure.html CHANGED
@@ -1,4 +1,4 @@
1
- <p>Die aktuelle Abo-Zahlung f�r ihr Paket auf !!sitename!! ist fehlgeschlagen. <strong>Klicken Sie bitte auf den folgenden Link, um sich einzuloggen und Ihre Zahlungsinformationen zu aktualisieren, da Ihr Account sonst gesperrt wird: !!login_link!!</strong></p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Die aktuell bei uns hinterlegten Zahlungsinformationen lauten:</p>
@@ -7,5 +7,5 @@
7
 
8
  <p>
9
  !!cardtype!!: !!accountnumber!!<br />
10
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
11
  </p>
1
+ <p>Die aktuelle Abo-Zahlung für ihr Paket auf !!sitename!! ist fehlgeschlagen. <strong>Klicken Sie bitte auf den folgenden Link, um sich einzuloggen und Ihre Zahlungsinformationen zu aktualisieren, da Ihr Account sonst gesperrt wird: !!login_link!!</strong></p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Die aktuell bei uns hinterlegten Zahlungsinformationen lauten:</p>
7
 
8
  <p>
9
  !!cardtype!!: !!accountnumber!!<br />
10
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
11
  </p>
languages/email/de_DE/billing_failure_admin.html CHANGED
@@ -7,5 +7,5 @@
7
 
8
  <p>
9
  !!cardtype!!: !!accountnumber!!<br />
10
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
11
  </p>
7
 
8
  <p>
9
  !!cardtype!!: !!accountnumber!!<br />
10
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
11
  </p>
languages/email/de_DE/cancel.html CHANGED
@@ -1,3 +1,3 @@
1
- <p>Ihr Account bei !!sitename!! wurde gel�scht.</p>
2
 
3
- <p>Wenn Sie diese L�schung nicht veranlasst haben und Sie mehr Informationen ben�tigen, kontaktieren Sie uns bitte unter !!siteemail!!</p>
1
+ <p>Ihr Account bei !!sitename!! wurde gelöscht.</p>
2
 
3
+ <p>Wenn Sie diese Löschung nicht veranlasst haben und Sie mehr Informationen benötigen, kontaktieren Sie uns bitte unter !!siteemail!!</p>
languages/email/de_DE/cancel_admin.html CHANGED
@@ -1,8 +1,8 @@
1
- <p>Der Zugang f�r !!user_login!! bei !!sitename!! wurde gel�scht.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Paket: !!membership_level_name!!</p>
5
  <p>Datum der Anmeldung: !!startdate!!</p>
6
- <p>Datum der L�schung: !!enddate!!</p>
7
 
8
  <p>Wordpress Login: !!login_link!!</p>
1
+ <p>Der Zugang für !!user_login!! bei !!sitename!! wurde gelöscht.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Paket: !!membership_level_name!!</p>
5
  <p>Datum der Anmeldung: !!startdate!!</p>
6
+ <p>Datum der Löschung: !!enddate!!</p>
7
 
8
  <p>Wordpress Login: !!login_link!!</p>
languages/email/de_DE/checkout_check.html CHANGED
@@ -1,8 +1,8 @@
1
- <p>Danke f�r Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
 
3
  !!instructions!!
4
 
5
- <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung f�r Ihre Zahlung.</p>
6
 
7
  <p>Account: !!display_name!! (!!user_email!!)</p>
8
  <p>Paket: !!membership_level_name!!</p>
@@ -14,4 +14,4 @@
14
  Gesamt: !!invoice_total!!
15
  </p>
16
 
17
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Danke für Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
 
3
  !!instructions!!
4
 
5
+ <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung für Ihre Zahlung.</p>
6
 
7
  <p>Account: !!display_name!! (!!user_email!!)</p>
8
  <p>Paket: !!membership_level_name!!</p>
14
  Gesamt: !!invoice_total!!
15
  </p>
16
 
17
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/checkout_check_admin.html CHANGED
@@ -1,8 +1,8 @@
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
 
3
- <p><strong>Als Zahlungsart wurde die Zahlung per Scheck ausgew�hlt.</strong></p>
4
 
5
- <p>Untenstehend die Details �ber den neuen Account und die Rechnung f�r die aktuelle Zahlung.</p>
6
 
7
  <p>Account: !!display_name!! (!!user_email!!)</p>
8
  <p>Paket: !!membership_level_name!!</p>
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
 
3
+ <p><strong>Als Zahlungsart wurde die Zahlung per Scheck ausgewählt.</strong></p>
4
 
5
+ <p>Untenstehend die Details über den neuen Account und die Rechnung für die aktuelle Zahlung.</p>
6
 
7
  <p>Account: !!display_name!! (!!user_email!!)</p>
8
  <p>Paket: !!membership_level_name!!</p>
languages/email/de_DE/checkout_express.html CHANGED
@@ -1,5 +1,5 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
- <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung f�r Ihre Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
@@ -11,4 +11,4 @@
11
  Gesamt: !!invoice_total!!
12
  </p>
13
 
14
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
+ <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung für Ihre Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
11
  Gesamt: !!invoice_total!!
12
  </p>
13
 
14
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/checkout_express_admin.html CHANGED
@@ -1,5 +1,5 @@
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
- <p>Untenstehend die Details �ber den neuen Account und die Rechnung f�r die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
@@ -7,7 +7,7 @@
7
  !!membership_expiration!! !!discount_code!!
8
 
9
  <p>
10
- ZahRechnung lung #!!invoice_id!! am !!invoice_date!!<br />
11
  Gesamt: !!invoice_total!!
12
  </p>
13
 
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
+ <p>Untenstehend die Details über den neuen Account und die Rechnung für die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
7
  !!membership_expiration!! !!discount_code!!
8
 
9
  <p>
10
+ Rechnung #!!invoice_id!! am !!invoice_date!!<br />
11
  Gesamt: !!invoice_total!!
12
  </p>
13
 
languages/email/de_DE/checkout_free.html CHANGED
@@ -1,8 +1,8 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
  <p>Untenstehend die Details zu Ihrem Account.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
6
  !!membership_expiration!! !!discount_code!!
7
 
8
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
  <p>Untenstehend die Details zu Ihrem Account.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
6
  !!membership_expiration!! !!discount_code!!
7
 
8
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/checkout_free_admin.html CHANGED
@@ -1,5 +1,5 @@
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
- <p>Untenstehend die Details �ber den neuen Account.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
+ <p>Untenstehend die Details über den neuen Account.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
languages/email/de_DE/checkout_freetrial.html CHANGED
@@ -1,4 +1,4 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
  <p>Untenstehend finden Sie die Details zu Ihrem Account.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
@@ -13,7 +13,7 @@
13
 
14
  <p>
15
  !!cardtype!!: !!accountnumber!!<br />
16
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
17
  </p>
18
 
19
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
  <p>Untenstehend finden Sie die Details zu Ihrem Account.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
13
 
14
  <p>
15
  !!cardtype!!: !!accountnumber!!<br />
16
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
17
  </p>
18
 
19
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/checkout_freetrial_admin.html CHANGED
@@ -1,5 +1,5 @@
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
- <p>Untenstehend die Details �ber den neuen Account und die Rechnung f�r die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
@@ -13,7 +13,7 @@
13
 
14
  <p>
15
  !!cardtype!!: !!accountnumber!!<br />
16
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
17
  </p>
18
 
19
  <p>Wordpress Login: !!login_link!!</p>
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
+ <p>Untenstehend die Details über den neuen Account und die Rechnung für die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
13
 
14
  <p>
15
  !!cardtype!!: !!accountnumber!!<br />
16
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
17
  </p>
18
 
19
  <p>Wordpress Login: !!login_link!!</p>
languages/email/de_DE/checkout_paid.html CHANGED
@@ -1,5 +1,5 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
- <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung f�r Ihre Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
@@ -17,7 +17,7 @@
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
+ <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung für Ihre Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/checkout_paid_admin.html CHANGED
@@ -1,5 +1,5 @@
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
- <p>Untenstehend die Details �ber den neuen Account und die Rechnung f�r die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
@@ -17,7 +17,7 @@
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
  <p>Wordpress Login: !!login_link!!</p>
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
+ <p>Untenstehend die Details über den neuen Account und die Rechnung für die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
  <p>Wordpress Login: !!login_link!!</p>
languages/email/de_DE/checkout_trial.html CHANGED
@@ -1,5 +1,5 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
- <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung f�r Ihre Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
@@ -17,7 +17,7 @@
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Ihr Zugang ist nun aktiv.</p>
2
+ <p>Untenstehend finden Sie die Details zu Ihrem Account und eine Rechnung für Ihre Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/checkout_trial_admin.html CHANGED
@@ -1,5 +1,5 @@
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
- <p>Untenstehend die Details �ber den neuen Account und die Rechnung f�r die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
@@ -17,7 +17,7 @@
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
  <p>Wordpress Login: !!login_link!!</p>
1
  <p>Es gibt eine neue Registrierung bei !!sitename!!.</p>
2
+ <p>Untenstehend die Details über den neuen Account und die Rechnung für die aktuelle Zahlung.</p>
3
 
4
  <p>Account: !!display_name!! (!!user_email!!)</p>
5
  <p>Paket: !!membership_level_name!!</p>
17
 
18
  <p>
19
  !!cardtype!!: !!accountnumber!!<br />
20
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
21
  </p>
22
 
23
  <p>Wordpress Login: !!login_link!!</p>
languages/email/de_DE/credit_card_expiring.html CHANGED
@@ -1,4 +1,4 @@
1
- <p>Die G�ltigkeit der Kreditkarte, die Sie f�r Ihren Zugang bei !!sitename!! angegeben haben, wird in K�rze ablaufen. <strong>Klicken Sie bitte auf den folgenden Link, um Ihre Zahlungsinformationen zu aktualisieren, damit Ihr Account nicht deaktiviert wird: !!login_link!!</strong></p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Ihre aktuellen Zahlungsinformationen:</p>
@@ -9,5 +9,5 @@
9
 
10
  <p>
11
  !!cardtype!!: !!accountnumber!!<br />
12
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
13
  </p>
1
+ <p>Die Gültigkeit der Kreditkarte, die Sie für Ihren Zugang bei !!sitename!! angegeben haben, wird in Kürze ablaufen. <strong>Klicken Sie bitte auf den folgenden Link, um Ihre Zahlungsinformationen zu aktualisieren, damit Ihr Account nicht deaktiviert wird: !!login_link!!</strong></p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Ihre aktuellen Zahlungsinformationen:</p>
9
 
10
  <p>
11
  !!cardtype!!: !!accountnumber!!<br />
12
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
13
  </p>
languages/email/de_DE/footer.html CHANGED
@@ -1,4 +1,4 @@
1
  <p>
2
- mit freundlichen Gr��en,<br />
3
  Ihr !!sitename!! Team
4
  </p>
1
  <p>
2
+ Mit freundlichen Grüßen,<br />
3
  Ihr !!sitename!! Team
4
  </p>
languages/email/de_DE/invoice.html CHANGED
@@ -1,8 +1,8 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Untenstehend finden Sie die Rechnung zur Ihrer aktuellen Zahlung.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>
5
- Rechnung #!!invoice_id!! on !!invoice_date!!<br />
6
  Gesamt: !!invoice_total!!
7
  </p>
8
  <p>
@@ -12,8 +12,8 @@
12
 
13
  <p>
14
  !!cardtype!!: !!accountnumber!!<br />
15
- G�ltig bis: !!expirationmonth!!/!!expirationyear!!
16
  </p>
17
 
18
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
19
  <p>Um diese Rechnung online abzurufen, klicken Sie bitte hier: !!invoice_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Untenstehend finden Sie die Rechnung zur Ihrer aktuellen Zahlung.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>
5
+ Rechnung #!!invoice_id!! vom !!invoice_date!!<br />
6
  Gesamt: !!invoice_total!!
7
  </p>
8
  <p>
12
 
13
  <p>
14
  !!cardtype!!: !!accountnumber!!<br />
15
+ Gültig bis: !!expirationmonth!!/!!expirationyear!!
16
  </p>
17
 
18
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
19
  <p>Um diese Rechnung online abzurufen, klicken Sie bitte hier: !!invoice_link!!</p>
languages/email/de_DE/membership_expired.html CHANGED
@@ -1,7 +1,7 @@
1
  <p>Ihr Zugang auf !!sitename!! ist abgelaufen.</p>
2
 
3
- <p>Vielen Dank f�r Ihre Unterst�tzung.</p>
4
 
5
  <p>Unsere aktuellen Konditionen finden Sie hier: !!levels_link!!</p>
6
 
7
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
  <p>Ihr Zugang auf !!sitename!! ist abgelaufen.</p>
2
 
3
+ <p>Vielen Dank für Ihre Unterstützung.</p>
4
 
5
  <p>Unsere aktuellen Konditionen finden Sie hier: !!levels_link!!</p>
6
 
7
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/membership_expiring.html CHANGED
@@ -1,6 +1,6 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Dies ist eine Erinnerung, dass Ihr Zugang am !!enddate!! abl�uft.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Paket: !!membership_level_name!!</p>
5
 
6
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Dies ist eine Erinnerung, dass Ihr Zugang am !!enddate!! abläuft.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Paket: !!membership_level_name!!</p>
5
 
6
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/email/de_DE/trial_ending.html CHANGED
@@ -1,8 +1,8 @@
1
- <p>Vielen Dank f�r Ihre Anmeldung bei !!sitename!!. Ihre Testperiode endet am !!trial_end!!.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Paket: !!membership_level_name!!</p>
5
 
6
- <p>Die Kosten f�r den Zugang �ndern sich dann von !!trial_amount!! auf !!billing_amount!! jede(n) !!cycle_number!! !!cycle_period!!(s).</p>
7
 
8
- <p>Hier k�nnen Sie sich in Ihren Account einloggen: !!login_link!!</p>
1
+ <p>Vielen Dank für Ihre Anmeldung bei !!sitename!!. Ihre Testperiode endet am !!trial_end!!.</p>
2
 
3
  <p>Account: !!display_name!! (!!user_email!!)</p>
4
  <p>Paket: !!membership_level_name!!</p>
5
 
6
+ <p>Die Kosten für den Zugang ändern sich dann von !!trial_amount!! auf !!billing_amount!! jede(n) !!cycle_number!! !!cycle_period!!(s).</p>
7
 
8
+ <p>Hier können Sie sich in Ihren Account einloggen: !!login_link!!</p>
languages/gettext.sh CHANGED
@@ -16,6 +16,12 @@ xgettext -j -o languages/paid-memberships-pro.pot \
16
  --keyword=_ex \
17
  --keyword=_n \
18
  --keyword=_x \
 
 
 
 
 
 
19
  --sort-by-file \
20
  --package-version=1.0 \
21
  --msgid-bugs-address="jason@strangerstudios.com" \
16
  --keyword=_ex \
17
  --keyword=_n \
18
  --keyword=_x \
19
+ --keyword=esc_html__ \
20
+ --keyword=esc_html_e \
21
+ --keyword=esc_html_x \
22
+ --keyword=esc_attr__ \
23
+ --keyword=esc_attr_e \
24
+ --keyword=esc_attr_x \
25
  --sort-by-file \
26
  --package-version=1.0 \
27
  --msgid-bugs-address="jason@strangerstudios.com" \
languages/paid-memberships-pro-ca.mo CHANGED
Binary file
languages/paid-memberships-pro-ca.po CHANGED
@@ -1,175 +1,309 @@
1
- #
2
- # Hi there! Details on how to help out translating Paid Memberships Pro can be found at:
3
- # http://www.paidmembershipspro.com/documentation/languages/
4
- #
5
  msgid ""
6
  msgstr ""
7
- "Project-Id-Version: pmpro\n"
8
- "POT-Creation-Date: 2016-01-31 12:04+0100\n"
9
- "PO-Revision-Date: 2016-02-06 17:11+0100\n"
10
- "Language-Team: Stranger Studios <jason@strangerstudios.com>\n"
11
  "MIME-Version: 1.0\n"
12
  "Content-Type: text/plain; charset=UTF-8\n"
13
  "Content-Transfer-Encoding: 8bit\n"
14
- "X-Generator: Poedit 1.8.6\n"
15
- "Last-Translator: \n"
16
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
17
- "Language: ca\n"
18
-
19
- #: adminpages/addons.php:5 adminpages/advancedsettings.php:5
20
- #: adminpages/discountcodes.php:5 adminpages/emailsettings.php:5
21
- #: adminpages/membershiplevels.php:5 adminpages/memberslist-csv.php:5
22
- #: adminpages/memberslist.php:5 adminpages/orders-csv.php:5
23
- #: adminpages/orders-print.php:12 adminpages/orders.php:5
24
- #: adminpages/pagesettings.php:4 adminpages/paymentsettings.php:5
25
- #: adminpages/updates.php:5 includes/license.php:36 adminpages/addons.php:21
 
 
 
 
 
 
 
 
26
  #: adminpages/pagesettings.php:5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  msgid "You do not have permissions to perform this action."
28
  msgstr "Vostè no té permisos per a realitzar aquesta acció."
29
 
30
- #: adminpages/addons.php:64 adminpages/admin_header.php:170
31
- #: includes/adminpages.php:53 includes/adminpages.php:151
32
- #: adminpages/addons.php:80 adminpages/admin_header.php:133
33
- #: adminpages/admin_header.php:154 includes/adminpages.php:14
34
- #: includes/adminpages.php:52 includes/adminpages.php:69
35
- #: includes/adminpages.php:135 includes/adminpages.php:142
 
 
 
 
 
 
36
  #: includes/adminpages.php:146
 
 
 
 
37
  msgid "Add Ons"
38
  msgstr "Complement"
39
 
40
- #: adminpages/addons.php:71 adminpages/addons.php:87
 
 
41
  #, php-format
42
  msgid "Last checked on %s at %s."
43
  msgstr "Darrera comprovació: %s a %s."
44
 
45
- #: adminpages/addons.php:72 adminpages/addons.php:88
 
 
46
  msgid "Check Again"
47
  msgstr "Comproveu una vegada més"
48
 
49
- #: adminpages/addons.php:76 adminpages/orders.php:712 adminpages/addons.php:92
50
- #: adminpages/orders.php:605 adminpages/orders.php:741
 
 
 
 
 
 
 
 
51
  msgid "All"
52
  msgstr "Tot"
53
 
54
- #: adminpages/addons.php:77 adminpages/addons.php:93
 
 
55
  msgid "Active"
56
  msgstr "Actiu"
57
 
58
- #: adminpages/addons.php:78 adminpages/addons.php:94
 
 
59
  msgid "Inactive"
60
  msgstr "Inactiu"
61
 
62
- #: adminpages/addons.php:79 adminpages/addons.php:95
 
 
63
  msgid "Update Available"
64
  msgstr "Actualització disponible"
65
 
66
- #: adminpages/addons.php:80 adminpages/addons.php:96
 
 
67
  msgid "Not Installed"
68
  msgstr "No instal·lat"
69
 
70
- #: adminpages/addons.php:93 adminpages/addons.php:109
 
71
  msgid "Add On Name"
72
  msgstr "Add On Nom"
73
 
74
- #: adminpages/addons.php:94 adminpages/addons.php:110
 
75
  msgid "Type"
76
  msgstr "Tipus"
77
 
78
- #: adminpages/addons.php:95 adminpages/membershiplevels.php:300
79
- #: adminpages/addons.php:111 adminpages/membershiplevels.php:296
 
 
80
  #: adminpages/membershiplevels.php:298
 
81
  msgid "Description"
82
  msgstr "Descripció"
83
 
84
- #: adminpages/addons.php:118 adminpages/addons.php:134
 
 
85
  msgid "No Add Ons found."
86
  msgstr "No s’ha trobat Add Ons"
87
 
88
- #: adminpages/addons.php:179 adminpages/addons.php:184
89
- #: adminpages/addons.php:196 adminpages/addons.php:195
90
- #: adminpages/addons.php:200 adminpages/addons.php:212
 
 
 
 
 
91
  msgid "Install Now"
92
  msgstr "Instal · la ara"
93
 
94
- #: adminpages/addons.php:185 adminpages/addons.php:191
95
- #: adminpages/addons.php:197 adminpages/addons.php:203
96
- #: adminpages/addons.php:201 adminpages/addons.php:207
97
- #: adminpages/addons.php:213 adminpages/addons.php:219
 
 
 
 
 
 
 
 
98
  msgid "Download"
99
  msgstr "Descarrega"
100
 
101
- #: adminpages/addons.php:190 adminpages/addons.php:202
102
- #: adminpages/addons.php:206 adminpages/addons.php:218
 
 
 
 
103
  msgid "Update License"
104
  msgstr "Actualitza Llicència"
105
 
106
- #: adminpages/addons.php:208 adminpages/addons.php:224
 
107
  msgid "Deactivate"
108
  msgstr "Desactiva"
109
 
110
- #: adminpages/addons.php:208 adminpages/addons.php:224
 
111
  #, php-format
112
  msgid "Deactivate %s"
113
  msgstr "Desactiva %s"
114
 
115
- #: adminpages/addons.php:212 adminpages/addons.php:228
 
116
  msgid "Activate"
117
  msgstr "Activa"
118
 
119
- #: adminpages/addons.php:212 adminpages/addons.php:228
 
120
  #, php-format
121
  msgid "Activate %s"
122
  msgstr "Activa %s"
123
 
124
- #: adminpages/addons.php:213 adminpages/addons.php:229
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  msgid "Delete"
126
  msgstr "Esborra"
127
 
128
- #: adminpages/addons.php:213 adminpages/addons.php:229
 
129
  #, php-format
130
  msgid "Delete %s"
131
  msgstr "Esborra %s"
132
 
133
- #: adminpages/addons.php:223 adminpages/addons.php:239
 
 
134
  msgid "PMPro Free"
135
  msgstr "PMPro Free"
136
 
137
- #: adminpages/addons.php:225 adminpages/addons.php:241
 
 
138
  msgid "PMPro Core"
139
  msgstr "PMPro Core"
140
 
141
- #: adminpages/addons.php:227 adminpages/addons.php:243
 
 
142
  msgid "PMPro Plus"
143
  msgstr "PMPro Plus"
144
 
145
- #: adminpages/addons.php:229 adminpages/addons.php:245
 
 
146
  msgid "WordPress.org"
147
  msgstr "WordPress.org"
148
 
149
- #: adminpages/addons.php:231 adminpages/addons.php:247
 
 
 
 
 
 
1
+ # Copyright (C) 2022 Stranger Studios
2
+ # This file is distributed under the same license as the Paid Memberships Pro plugin.
 
 
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Paid Memberships Pro 2.7\n"
6
+ "Report-Msgid-Bugs-To: info@paidmembershipspro.com\n"
7
+ "Last-Translator: Paid Memberships Pro <info@paidmembershipspro.com>\n"
8
+ "Language-Team: Paid Memberships Pro <info@paidmembershipspro.com>\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
+ "POT-Creation-Date: 2022-01-13T17:22:32+00:00\n"
13
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
14
+ "X-Generator: WP-CLI 2.5.0\n"
15
+ "X-Domain: paid-memberships-pro\n"
16
+
17
+ #: adminpages/addons.php:5
18
+ #: adminpages/advancedsettings.php:5
19
+ #: adminpages/discountcodes.php:5
20
+ #: adminpages/emailsettings.php:5
21
+ #: adminpages/membershiplevels.php:5
22
+ #: adminpages/memberslist-csv.php:5
23
+ #: adminpages/memberslist.php:5
24
+ #: adminpages/orders-csv.php:5
25
+ #: adminpages/orders-print.php:12
26
+ #: adminpages/orders.php:5
27
+ #: adminpages/pagesettings.php:4
28
+ #: adminpages/paymentsettings.php:5
29
+ #: adminpages/updates.php:5
30
+ #: includes/license.php:36
31
+ #: adminpages/addons.php:21
32
  #: adminpages/pagesettings.php:5
33
+ #: adminpages/emailtemplates.php:5
34
+ #: adminpages/license.php:4
35
+ #: adminpages/orders-csv.php:4
36
+ #: adminpages/orders.php:4
37
+ #: includes/services.php:91
38
+ #: includes/services.php:104
39
+ #: includes/email.php:175
40
+ #: includes/email.php:211
41
+ #: includes/email.php:230
42
+ #: includes/email.php:255
43
+ #: includes/email.php:273
44
+ #: includes/email.php:178
45
+ #: includes/email.php:214
46
+ #: includes/email.php:237
47
+ #: includes/email.php:262
48
+ #: includes/email.php:280
49
  msgid "You do not have permissions to perform this action."
50
  msgstr "Vostè no té permisos per a realitzar aquesta acció."
51
 
52
+ #: adminpages/addons.php:64
53
+ #: adminpages/admin_header.php:170
54
+ #: includes/adminpages.php:53
55
+ #: includes/adminpages.php:151
56
+ #: adminpages/addons.php:80
57
+ #: adminpages/admin_header.php:133
58
+ #: adminpages/admin_header.php:154
59
+ #: includes/adminpages.php:14
60
+ #: includes/adminpages.php:52
61
+ #: includes/adminpages.php:69
62
+ #: includes/adminpages.php:135
63
+ #: includes/adminpages.php:142
64
  #: includes/adminpages.php:146
65
+ #: adminpages/addons.php:81
66
+ #: adminpages/admin_header.php:242
67
+ #: includes/adminpages.php:56
68
+ #: includes/adminpages.php:206
69
  msgid "Add Ons"
70
  msgstr "Complement"
71
 
72
+ #: adminpages/addons.php:71
73
+ #: adminpages/addons.php:87
74
+ #: adminpages/addons.php:89
75
  #, php-format
76
  msgid "Last checked on %s at %s."
77
  msgstr "Darrera comprovació: %s a %s."
78
 
79
+ #: adminpages/addons.php:72
80
+ #: adminpages/addons.php:88
81
+ #: adminpages/addons.php:90
82
  msgid "Check Again"
83
  msgstr "Comproveu una vegada més"
84
 
85
+ #: adminpages/addons.php:76
86
+ #: adminpages/orders.php:712
87
+ #: adminpages/addons.php:92
88
+ #: adminpages/orders.php:605
89
+ #: adminpages/orders.php:741
90
+ #: adminpages/addons.php:94
91
+ #: adminpages/orders.php:1016
92
+ #: adminpages/orders.php:1022
93
+ #: adminpages/orders.php:1014
94
+ #: includes/metaboxes.php:19
95
  msgid "All"
96
  msgstr "Tot"
97
 
98
+ #: adminpages/addons.php:77
99
+ #: adminpages/addons.php:93
100
+ #: adminpages/addons.php:95
101
  msgid "Active"
102
  msgstr "Actiu"
103
 
104
+ #: adminpages/addons.php:78
105
+ #: adminpages/addons.php:94
106
+ #: adminpages/addons.php:96
107
  msgid "Inactive"
108
  msgstr "Inactiu"
109
 
110
+ #: adminpages/addons.php:79
111
+ #: adminpages/addons.php:95
112
+ #: adminpages/addons.php:97
113
  msgid "Update Available"
114
  msgstr "Actualització disponible"
115
 
116
+ #: adminpages/addons.php:80
117
+ #: adminpages/addons.php:96
118
+ #: adminpages/addons.php:98
119
  msgid "Not Installed"
120
  msgstr "No instal·lat"
121
 
122
+ #: adminpages/addons.php:93
123
+ #: adminpages/addons.php:109
124
  msgid "Add On Name"
125
  msgstr "Add On Nom"
126
 
127
+ #: adminpages/addons.php:94
128
+ #: adminpages/addons.php:110
129
  msgid "Type"
130
  msgstr "Tipus"
131
 
132
+ #: adminpages/addons.php:95
133
+ #: adminpages/membershiplevels.php:300
134
+ #: adminpages/addons.php:111
135
+ #: adminpages/membershiplevels.php:296
136
  #: adminpages/membershiplevels.php:298
137
+ #: adminpages/membershiplevels.php:359
138
  msgid "Description"
139
  msgstr "Descripció"
140
 
141
+ #: adminpages/addons.php:118
142
+ #: adminpages/addons.php:134
143
+ #: adminpages/addons.php:135
144
  msgid "No Add Ons found."
145
  msgstr "No s’ha trobat Add Ons"
146
 
147
+ #: adminpages/addons.php:179
148
+ #: adminpages/addons.php:184
149
+ #: adminpages/addons.php:196
150
+ #: adminpages/addons.php:195
151
+ #: adminpages/addons.php:200
152
+ #: adminpages/addons.php:212
153
+ #: adminpages/addons.php:201
154
+ #: adminpages/addons.php:213
155
  msgid "Install Now"
156
  msgstr "Instal · la ara"
157
 
158
+ #: adminpages/addons.php:185
159
+ #: adminpages/addons.php:191
160
+ #: adminpages/addons.php:197
161
+ #: adminpages/addons.php:203
162
+ #: adminpages/addons.php:201
163
+ #: adminpages/addons.php:207
164
+ #: adminpages/addons.php:213
165
+ #: adminpages/addons.php:219
166
+ #: adminpages/addons.php:202
167
+ #: adminpages/addons.php:208
168
+ #: adminpages/addons.php:214
169
+ #: adminpages/addons.php:220
170
  msgid "Download"
171
  msgstr "Descarrega"
172
 
173
+ #: adminpages/addons.php:190
174
+ #: adminpages/addons.php:202
175
+ #: adminpages/addons.php:206
176
+ #: adminpages/addons.php:218
177
+ #: adminpages/addons.php:207
178
+ #: adminpages/addons.php:219
179
  msgid "Update License"
180
  msgstr "Actualitza Llicència"
181
 
182
+ #: adminpages/addons.php:208
183
+ #: adminpages/addons.php:224
184
  msgid "Deactivate"
185
  msgstr "Desactiva"
186
 
187
+ #: adminpages/addons.php:208
188
+ #: adminpages/addons.php:224
189
  #, php-format
190
  msgid "Deactivate %s"
191
  msgstr "Desactiva %s"
192
 
193
+ #: adminpages/addons.php:212
194
+ #: adminpages/addons.php:228
195
  msgid "Activate"
196
  msgstr "Activa"
197
 
198
+ #: adminpages/addons.php:212
199
+ #: adminpages/addons.php:228
200
  #, php-format
201
  msgid "Activate %s"
202
  msgstr "Activa %s"
203
 
204
+ #: adminpages/addons.php:213
205
+ #: adminpages/addons.php:229
206
+ #: adminpages/discountcodes.php:816
207
+ #: adminpages/membershiplevels.php:835
208
+ #: adminpages/orders.php:1374
209
+ #: adminpages/discountcodes.php:862
210
+ #: adminpages/discountcodes.php:864
211
+ #: adminpages/membershiplevels.php:887
212
+ #: adminpages/membershiplevels.php:889
213
+ #: adminpages/orders.php:1425
214
+ #: adminpages/orders.php:1427
215
+ #: adminpages/discountcodes.php:866
216
+ #: adminpages/orders.php:1417
217
+ #: adminpages/orders.php:1419
218
  msgid "Delete"
219
  msgstr "Esborra"
220
 
221
+ #: adminpages/addons.php:213
222
+ #: adminpages/addons.php:229
223
  #, php-format
224
  msgid "Delete %s"
225
  msgstr "Esborra %s"
226
 
227
+ #: adminpages/addons.php:223
228
+ #: adminpages/addons.php:239
229
+ #: adminpages/addons.php:240
230
  msgid "PMPro Free"
231
  msgstr "PMPro Free"
232
 
233
+ #: adminpages/addons.php:225
234
+ #: adminpages/addons.php:241
235
+ #: adminpages/addons.php:242
236
  msgid "PMPro Core"
237
  msgstr "PMPro Core"
238
 
239
+ #: adminpages/addons.php:227
240
+ #: adminpages/addons.php:243
241
+ #: adminpages/addons.php:244
242
  msgid "PMPro Plus"
243
  msgstr "PMPro Plus"
244
 
245
+ #: adminpages/addons.php:229
246
+ #: adminpages/addons.php:245
247
+ #: adminpages/addons.php:246
248
  msgid "WordPress.org"
249
  msgstr "WordPress.org"
250
 
251
+ #: adminpages/addons.php:231
252
+ #: adminpages/addons.php:247
253
+ #: adminpages/addons.php:248
254
+ #: adminpages/orders.php:581
255
+ #: adminpages/orders.php:1467
256
+ #: adminpages/orders.php:1477
257
+ #: inclu