Paid Memberships Pro - Version 2.1

Version Description

  • 2019-09-13 =
  • FEATURE: Updated Stripe integration to support Stripe v3, Stripe Elements, and their Secure Customer Authorization process.
  • FEATURE: Updated how we store prices to support up to 8 decimals (e.g. for Bitcoin gateway implementations).
  • ENHANCEMENT: Improved error messaging on the update billing page when a gateway doesn't support it or the user's current membership doesn't have a subscription.
  • ENHANCEMENT: Added a pmpro_is_checkout() function that will return true if on the PMPro checkout page or a page with the PMPro checkout shortcode or block.
  • ENHANCEMENT: Showing a warning message when a user about to be deleted has a membership so admins know that existing subscriptions will be deleted at the gateway.
  • ENHANCEMENT: Added a pmpro_braintree_plan_id filter in case you need to adjust plan IDs. This is useful if you have several sites running on the same Braintree account.
  • ENHANCEMENT: Added a pmpro_num_expiration_years filter to adjust the number of years to include in the dropdown to set the year membership will expire.
  • ENHANCEMENT: Tweaked the UI of the orders list and members list in the dashboard.
  • ENHANCEMENT: Added pmpro_membership_levels_table_extra_cols_header and pmpro_membership_levels_table_extra_cols_body hooks to add columns to the members list.
  • ENHANCEMENT: Showing notices to admins when categories are hidden from them on the frontend of the site.
  • ENHANCEMENT: Added a pmpro_url filter to filter URLs returned from that function.
  • ENHANCEMENT: Adding a pmpro_checkout_gateway-stripe or pmpro_checkout_gateway-paypal/etc CSS class to the wrapping div for payment fields to aid in styling.
  • ENHANCEMENT: Using the site's date format option when printing orders.
  • BUG FIX/ENHANCEMENT: If a site has no paying levels, the test gateway will show as the "Default" gateway and we will no longer show a message about requiring gateway setup on the checkout page.
  • BUG FIX/ENHANCEMENT: Updated Russian Ruble definition to have 0 decimals and use   as the thousands separator. (Thanks, Airat Halitov)
  • BUG FIX/ENHANCEMENT: Using add_query_arg when generating IPN URLs to avoid issues on sites that aren't using pretty permalinks or have moved their admin directory.
  • BUG FIX/ENHANCEMENT: Fixed issue on advanced settings page where clicking on labels didn't check the corresponding check boxes.
  • BUG FIX/ENHANCEMENT: Updated our pmpro_generateUsername() function to be a bit smarter.
  • BUG FIX/ENHANCEMENT: Now using wp_generate_password() when choosing a random password for a user (e.g. when using the Sign Up Shortcode add on or the $skip_account_fields global).
  • BUG FIX/ENHANCEMENT: Setting autocomplete to false on the "fullname" honeypot field. This will prevent user's with certain autocomplete tools from accidentally filling it out.
  • BUG FIX/EHNANCEMENT: Now sending name and email fields to PayPay (using Website Payments Pro) even if no address was captured.
  • BUG FIX/ENHANCEMENT: More specific CSS selectors for checkout form elements to make sure errors are highlighted/etc with different themes.
  • BUG FIX: Fixed issue where the first 2000 or so orders might be skipped when exporting orders on large sites.
  • BUG FIX: Fixed issue with setting custom trials on discount codes.
  • BUG FIX: Fixed issue in the SQL query in the pmpro_calculateInitialPaymentRevenue() function. This function is deprecated, but still used by some custom code.
  • BUG FIX: Fixed issue where default templates would fail to load if a custom template was specified.
  • BUG FIX: Fixed fatal errors that could happen when using the PMPro REST API endpoints.
  • BUG FIX: Fixed bug where the invoices page would sometimes show data for the current (admin) users instead of the user the invoice was for.
  • BUG FIX: Fixed bug where the membership stats graphs would sometimes show up blank.
  • BUG FIX: Now falling back to using readfile() if fpassthru() doesn't existing.
  • BUG FIX: Fixed issue where the from name and email were not set properly if the Only Filter PMPro Emails setting was checked. (Thanks, mjulian87 on GitHub)
  • BUG FIX: Fixed several error handling issues with the Cybersource gateway integration.
  • REFACTOR: Moved JavaScript out of pages/checkout.php and other places into files in the /js/ folder. This will avoid issues where other JS at checkout breaks PMPro checkout and will improve compatibility with tools that optimize JS.
  • REFACTOR: Added unit testing and a started on coverage of some functions in includes/functions.php. (Thanks, Mike Auteri)
  • REFACTOR: The JS function askfirst is now prefixed as pmpro_askfirst.
Download this release

Release Info

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

Code changes from version 2.0.7 to 2.1

Files changed (163) hide show
  1. CHANGELOG.txt +65 -2
  2. adminpages/admin_header.php +1 -1
  3. adminpages/discountcodes.php +28 -21
  4. adminpages/functions.php +66 -0
  5. adminpages/membershiplevels.php +38 -6
  6. adminpages/memberslist-csv.php +13 -8
  7. adminpages/orders-csv.php +14 -9
  8. adminpages/orders.php +43 -93
  9. adminpages/paymentsettings.php +17 -0
  10. adminpages/reports/memberships.php +2 -2
  11. adminpages/templates/orders-print.php +1 -1
  12. bin/install-wp-tests.sh +152 -0
  13. classes/class.pmproemail.php +104 -0
  14. classes/gateways/class.pmprogateway_authorizenet.php +1 -1
  15. classes/gateways/class.pmprogateway_braintree.php +50 -37
  16. classes/gateways/class.pmprogateway_cybersource.php +215 -165
  17. classes/gateways/class.pmprogateway_payflowpro.php +5 -5
  18. classes/gateways/class.pmprogateway_paypal.php +105 -55
  19. classes/gateways/class.pmprogateway_paypalexpress.php +28 -45
  20. classes/gateways/class.pmprogateway_paypalstandard.php +2 -2
  21. classes/gateways/class.pmprogateway_stripe.php +1422 -890
  22. css/admin.css +1 -1
  23. css/frontend.css +82 -8
  24. email/payment_action.html +4 -0
  25. email/payment_action_admin.html +7 -0
  26. includes/cleanup.php +31 -1
  27. includes/currencies.php +3 -0
  28. includes/filters.php +1 -1
  29. includes/functions.php +299 -36
  30. includes/init.php +0 -71
  31. includes/lib/Stripe/LICENSE +1 -1
  32. includes/lib/Stripe/README.md +103 -10
  33. includes/lib/Stripe/VERSION +1 -1
  34. includes/lib/Stripe/data/ca-certificates.crt +421 -694
  35. includes/lib/Stripe/init.php +54 -3
  36. includes/lib/Stripe/lib/Account.php +297 -46
  37. includes/lib/Stripe/lib/AccountLink.php +21 -0
  38. includes/lib/Stripe/lib/AlipayAccount.php +60 -1
  39. includes/lib/Stripe/lib/ApiOperations/All.php +34 -0
  40. includes/lib/Stripe/lib/ApiOperations/Create.php +28 -0
  41. includes/lib/Stripe/lib/ApiOperations/Delete.php +27 -0
  42. includes/lib/Stripe/lib/ApiOperations/NestedResource.php +118 -0
  43. includes/lib/Stripe/lib/ApiOperations/Request.php +61 -0
  44. includes/lib/Stripe/lib/ApiOperations/Retrieve.php +27 -0
  45. includes/lib/Stripe/lib/ApiOperations/Update.php +46 -0
  46. includes/lib/Stripe/lib/ApiRequestor.php +198 -16
  47. includes/lib/Stripe/lib/ApiResource.php +40 -130
  48. includes/lib/Stripe/lib/ApplePayDomain.php +8 -46
  49. includes/lib/Stripe/lib/ApplicationFee.php +51 -23
  50. includes/lib/Stripe/lib/ApplicationFeeRefund.php +16 -0
  51. includes/lib/Stripe/lib/Balance.php +7 -3
  52. includes/lib/Stripe/lib/BalanceTransaction.php +41 -24
  53. includes/lib/Stripe/lib/BankAccount.php +85 -1
  54. includes/lib/Stripe/lib/BitcoinReceiver.php +20 -69
  55. includes/lib/Stripe/lib/BitcoinTransaction.php +1 -0
  56. includes/lib/Stripe/lib/Capability.php +83 -0
  57. includes/lib/Stripe/lib/Card.php +118 -1
  58. includes/lib/Stripe/lib/Charge.php +86 -64
  59. includes/lib/Stripe/lib/Checkout/Session.php +40 -0
  60. includes/lib/Stripe/lib/Collection.php +27 -6
  61. includes/lib/Stripe/lib/CountrySpec.php +12 -32
  62. includes/lib/Stripe/lib/Coupon.php +22 -64
  63. includes/lib/Stripe/lib/CreditNote.php +74 -0
  64. includes/lib/Stripe/lib/Customer.php +204 -75
  65. includes/lib/Stripe/lib/CustomerBalanceTransaction.php +88 -0
  66. includes/lib/Stripe/lib/Discount.php +21 -0
  67. includes/lib/Stripe/lib/Dispute.php +33 -39
  68. includes/lib/Stripe/lib/EphemeralKey.php +6 -20
  69. includes/lib/Stripe/lib/Error/Base.php +9 -0
  70. includes/lib/Stripe/lib/Error/Card.php +6 -7
  71. includes/lib/Stripe/lib/Error/Idempotency.php +7 -0
  72. includes/lib/Stripe/lib/Error/OAuth/OAuthBase.php +2 -2
  73. includes/lib/Stripe/lib/Event.php +148 -24
  74. includes/lib/Stripe/lib/ExchangeRate.php +17 -0
  75. includes/lib/Stripe/lib/File.php +62 -0
  76. includes/lib/Stripe/lib/FileLink.php +29 -0
  77. includes/lib/Stripe/lib/FileUpload.php +2 -58
  78. includes/lib/Stripe/lib/HttpClient/ClientInterface.php +5 -2
  79. includes/lib/Stripe/lib/HttpClient/CurlClient.php +241 -39
  80. includes/lib/Stripe/lib/Invoice.php +137 -27
  81. includes/lib/Stripe/lib/InvoiceItem.php +26 -64
  82. includes/lib/Stripe/lib/InvoiceLineItem.php +32 -0
  83. includes/lib/Stripe/lib/IssuerFraudRecord.php +25 -0
  84. includes/lib/Stripe/lib/Issuing/Authorization.php +68 -0
  85. includes/lib/Stripe/lib/Issuing/Card.php +51 -0
  86. includes/lib/Stripe/lib/Issuing/CardDetails.php +21 -0
  87. includes/lib/Stripe/lib/Issuing/Cardholder.php +30 -0
  88. includes/lib/Stripe/lib/Issuing/Dispute.php +30 -0
  89. includes/lib/Stripe/lib/Issuing/Transaction.php +35 -0
  90. includes/lib/Stripe/lib/LoginLink.php +5 -0
  91. includes/lib/Stripe/lib/OAuth.php +3 -8
  92. includes/lib/Stripe/lib/Order.php +29 -53
  93. includes/lib/Stripe/lib/OrderItem.php +22 -0
  94. includes/lib/Stripe/lib/OrderReturn.php +13 -31
  95. includes/lib/Stripe/lib/PaymentIntent.php +107 -0
  96. includes/lib/Stripe/lib/PaymentMethod.php +60 -0
  97. includes/lib/Stripe/lib/Payout.php +43 -48
  98. includes/lib/Stripe/lib/Person.php +108 -0
  99. includes/lib/Stripe/lib/Plan.php +25 -76
  100. includes/lib/Stripe/lib/Product.php +30 -61
  101. includes/lib/Stripe/lib/Radar/EarlyFraudWarning.php +36 -0
  102. includes/lib/Stripe/lib/Radar/ValueList.php +32 -0
  103. includes/lib/Stripe/lib/Radar/ValueListItem.php +26 -0
  104. includes/lib/Stripe/lib/Recipient.php +22 -67
  105. includes/lib/Stripe/lib/RecipientTransfer.php +2 -1
  106. includes/lib/Stripe/lib/Refund.php +30 -49
  107. includes/lib/Stripe/lib/Reporting/ReportRun.php +28 -0
  108. includes/lib/Stripe/lib/Reporting/ReportType.php +24 -0
  109. includes/lib/Stripe/lib/RequestTelemetry.php +27 -0
  110. includes/lib/Stripe/lib/Review.php +57 -0
  111. includes/lib/Stripe/lib/SKU.php +21 -64
  112. includes/lib/Stripe/lib/SetupIntent.php +75 -0
  113. includes/lib/Stripe/lib/Sigma/ScheduledQueryRun.php +33 -0
  114. includes/lib/Stripe/lib/SingletonApiResource.php +3 -1
  115. includes/lib/Stripe/lib/Source.php +90 -52
  116. includes/lib/Stripe/lib/SourceTransaction.php +23 -0
  117. includes/lib/Stripe/lib/Stripe.php +97 -7
  118. includes/lib/Stripe/lib/StripeObject.php +344 -115
  119. includes/lib/Stripe/lib/Subscription.php +58 -57
  120. includes/lib/Stripe/lib/SubscriptionItem.php +23 -67
  121. includes/lib/Stripe/lib/SubscriptionSchedule.php +111 -0
  122. includes/lib/Stripe/lib/SubscriptionScheduleRevision.php +77 -0
  123. includes/lib/Stripe/lib/TaxId.php +81 -0
  124. includes/lib/Stripe/lib/TaxRate.php +31 -0
  125. includes/lib/Stripe/lib/Terminal/ConnectionToken.php +17 -0
  126. includes/lib/Stripe/lib/Terminal/Location.php +25 -0
  127. includes/lib/Stripe/lib/Terminal/Reader.php +30 -0
  128. includes/lib/Stripe/lib/ThreeDSecure.php +6 -23
  129. includes/lib/Stripe/lib/Token.php +14 -22
  130. includes/lib/Stripe/lib/Topup.php +60 -0
  131. includes/lib/Stripe/lib/Transfer.php +63 -47
  132. includes/lib/Stripe/lib/TransferReversal.php +10 -1
  133. includes/lib/Stripe/lib/UsageRecord.php +44 -0
  134. includes/lib/Stripe/lib/UsageRecordSummary.php +22 -0
  135. includes/lib/Stripe/lib/Util/AutoPagingIterator.php +3 -3
  136. includes/lib/Stripe/lib/Util/CaseInsensitiveArray.php +62 -0
  137. includes/lib/Stripe/lib/Util/DefaultLogger.php +2 -2
  138. includes/lib/Stripe/lib/Util/LoggerInterface.php +1 -1
  139. includes/lib/Stripe/lib/Util/RandomGenerator.php +34 -0
  140. includes/lib/Stripe/lib/Util/RequestOptions.php +34 -5
  141. includes/lib/Stripe/lib/Util/Set.php +2 -2
  142. includes/lib/Stripe/lib/Util/Util.php +216 -72
  143. includes/lib/Stripe/lib/Webhook.php +4 -4
  144. includes/lib/Stripe/lib/WebhookEndpoint.php +29 -0
  145. includes/lib/Stripe/lib/WebhookSignature.php +4 -3
  146. includes/lib/php-jwt/BeforeValidException.php +7 -0
  147. includes/lib/php-jwt/ExpiredException.php +7 -0
  148. includes/lib/php-jwt/JWT.php +379 -0
  149. includes/lib/php-jwt/SignatureInvalidException.php +7 -0
  150. includes/profile.php +39 -39
  151. includes/recaptcha.php +14 -2
  152. includes/rest-api.php +49 -44
  153. includes/scripts.php +99 -0
  154. includes/sessions.php +81 -36
  155. includes/upgradecheck.php +16 -9
  156. js/pmpro-admin.js +49 -0
  157. js/pmpro-braintree.js +25 -0
  158. js/pmpro-checkout.js +203 -0
  159. js/pmpro-paypal.js +24 -0
  160. js/pmpro-stripe.js +159 -0
  161. languages/paid-memberships-pro.mo +0 -0
  162. languages/paid-memberships-pro.po +1468 -918
  163. languages/paid-memberships-pro.pot +183 -138
CHANGELOG.txt CHANGED
@@ -1,6 +1,70 @@
1
  == Changelog ==
2
 
3
- = 2.0.4 - 2019-01-11 =
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  * BUG FIX: Fixed warning in code added in 2.0.3 that could cause issues at checkout.
5
  * BUG FIX: Setting priority of pmpro_check_admin_capabilities to 5 to ensure it runs before dashboard redirect.
6
  * BUG FIX: Removed duplicate id attribute on the Membership Account page "cancel" action link.
@@ -103,7 +167,6 @@
103
  * ENHANCEMENT: Showing the Stripe version we use on the Payment Settings page.
104
  * ENHANCEMENT: Updated Copyright date and GPLv2 link in license.txt.
105
 
106
-
107
  = 1.9.5.3 - 2018-06-26 =
108
  * BUG FIX: The pmpro_ipnhandler_extend_memberships function actually needed use $user_id instead of $current_user.
109
 
1
  == Changelog ==
2
 
3
+ = 2.1 - 2019-09-13 =
4
+ * FEATURE: Updated Stripe integration to support Stripe v3, Stripe Elements, and their Secure Customer Authorization process.
5
+ * FEATURE: Updated how we store prices to support up to 8 decimals (e.g. for Bitcoin gateway implementations).
6
+ * ENHANCEMENT: Improved error messaging on the update billing page when a gateway doesn't support it or the user's current membership doesn't have a subscription.
7
+ * ENHANCEMENT: Added a pmpro_is_checkout() function that will return true if on the PMPro checkout page or a page with the PMPro checkout shortcode or block.
8
+ * ENHANCEMENT: Showing a warning message when a user about to be deleted has a membership so admins know that existing subscriptions will be deleted at the gateway.
9
+ * ENHANCEMENT: Added a pmpro_braintree_plan_id filter in case you need to adjust plan IDs. This is useful if you have several sites running on the same Braintree account.
10
+ * ENHANCEMENT: Added a pmpro_num_expiration_years filter to adjust the number of years to include in the dropdown to set the year membership will expire.
11
+ * ENHANCEMENT: Tweaked the UI of the orders list and members list in the dashboard.
12
+ * ENHANCEMENT: Added pmpro_membership_levels_table_extra_cols_header and pmpro_membership_levels_table_extra_cols_body hooks to add columns to the members list.
13
+ * ENHANCEMENT: Showing notices to admins when categories are hidden from them on the frontend of the site.
14
+ * ENHANCEMENT: Added a pmpro_url filter to filter URLs returned from that function.
15
+ * ENHANCEMENT: Adding a pmpro_checkout_gateway-stripe or pmpro_checkout_gateway-paypal/etc CSS class to the wrapping div for payment fields to aid in styling.
16
+ * ENHANCEMENT: Using the site's date format option when printing orders.
17
+ * BUG FIX/ENHANCEMENT: If a site has no paying levels, the test gateway will show as the "Default" gateway and we will no longer show a message about requiring gateway setup on the checkout page.
18
+ * BUG FIX/ENHANCEMENT: Updated Russian Ruble definition to have 0 decimals and use   as the thousands separator. (Thanks, Airat Halitov)
19
+ * BUG FIX/ENHANCEMENT: Using add_query_arg when generating IPN URLs to avoid issues on sites that aren't using pretty permalinks or have moved their admin directory.
20
+ * BUG FIX/ENHANCEMENT: Fixed issue on advanced settings page where clicking on labels didn't check the corresponding check boxes.
21
+ * BUG FIX/ENHANCEMENT: Updated our pmpro_generateUsername() function to be a bit smarter.
22
+ * BUG FIX/ENHANCEMENT: Now using wp_generate_password() when choosing a random password for a user (e.g. when using the Sign Up Shortcode add on or the $skip_account_fields global).
23
+ * BUG FIX/ENHANCEMENT: Setting autocomplete to false on the "fullname" honeypot field. This will prevent user's with certain autocomplete tools from accidentally filling it out.
24
+ * BUG FIX/EHNANCEMENT: Now sending name and email fields to PayPay (using Website Payments Pro) even if no address was captured.
25
+ * BUG FIX/ENHANCEMENT: More specific CSS selectors for checkout form elements to make sure errors are highlighted/etc with different themes.
26
+ * BUG FIX: Fixed issue where the first 2000 or so orders might be skipped when exporting orders on large sites.
27
+ * BUG FIX: Fixed issue with setting custom trials on discount codes.
28
+ * BUG FIX: Fixed issue in the SQL query in the pmpro_calculateInitialPaymentRevenue() function. This function is deprecated, but still used by some custom code.
29
+ * BUG FIX: Fixed issue where default templates would fail to load if a custom template was specified.
30
+ * BUG FIX: Fixed fatal errors that could happen when using the PMPro REST API endpoints.
31
+ * BUG FIX: Fixed bug where the invoices page would sometimes show data for the current (admin) users instead of the user the invoice was for.
32
+ * BUG FIX: Fixed bug where the membership stats graphs would sometimes show up blank.
33
+ * BUG FIX: Now falling back to using readfile() if fpassthru() doesn't existing.
34
+ * BUG FIX: Fixed issue where the from name and email were not set properly if the Only Filter PMPro Emails setting was checked. (Thanks, mjulian87 on GitHub)
35
+ * BUG FIX: Fixed several error handling issues with the Cybersource gateway integration.
36
+ * REFACTOR: Moved JavaScript out of pages/checkout.php and other places into files in the /js/ folder. This will avoid issues where other JS at checkout breaks PMPro checkout and will improve compatibility with tools that optimize JS.
37
+ * REFACTOR: Added unit testing and a started on coverage of some functions in includes/functions.php. (Thanks, Mike Auteri)
38
+ * REFACTOR: The JS function askfirst is now prefixed as pmpro_askfirst.
39
+
40
+ = 2.0.7 - 2019-05-30 =
41
+ * BUG FIX: Fixed issue where the profile start date would sometimes be set incorrectly on the Stripe subscription.
42
+ * BUG FIX: Fixed issue where the membership shortcode would not work properly if more than one level name was given.
43
+ * BUG FIX: Fixed issue where an incorrect email address was sometimes set in the confirm email field on the update billing page. (Thanks, Jessica Thomas)
44
+ * BUG FIX/ENHANCEMENT: Fixed placement of the hr tag above the user fields at checkout for consistency.
45
+ * ENHANCEMENT: Set the priority on the Require Membership meta box to "high" so it appears higher in the right sidebar.
46
+
47
+ = 2.0.6 - 2019-05-30 =
48
+ * SECURITY: Now using wp_safe_redirect when possible, especially in includes/login.php where the user-provided redirect_to URL parameter is used. (Thanks PluginVulnerabilities.com)
49
+
50
+ = 2.0.5 - 2019-04-25 =
51
+ * BUG FIX: Fixed fatal error on return from 2Checkout.
52
+ * BUG FIX: Removed error when installing PMPro via WP-CLI.
53
+ * BUG FIX: Fix database upgrade error on localhost environment. (Thanks, codezz on GitHub)
54
+ * BUG FIX: Fixed issue where the credit card expiring email didn't include user info because the user ID wasn't passed in properly. (Thanks, David Cervantes Caballero)
55
+ * BUG FIX: Fixed typo on edit level page. (Thanks, Theuns Coetzee)
56
+ * BUG FIX: Fixed bug with daily revenue reports not showing up in some cases.
57
+ * BUG FIX: Now checking before cancelling a Stripe subscription at the gateway to see if it has already been cancelled.
58
+ * BUG FIX/ENHANCEMENT: Now caching the query results in pmpro_getMembershipLevelsForUser(). This improves performance, especially when there are many posts on one page to check membership for. (Thanks, Seagyn Davis)
59
+ * BUG FIX/ENHANCEMENT: Now sending display_name to the $data array passed to PMPro email filters. (Thanks, David Cervantes Caballero)
60
+ * BUG FIX/ENHANCEMENT: Now searching for the last order with "success" or "pending" status on the Billing page.
61
+ * BUG FIX/ENHANCEMENT: Added pmpro_checkout_preheader_before_get_level_at_checkout and pmpro_checkout_preheader_after_get_level_at_checkout action hooks. Using pmpro_checkout_preheader_before_get_level_at_checkout to start the session earlier now.
62
+ * BUG FIX/ENHANCEMENT: Removed the "membership_code_id" and "membership_code" as field options for the member shortcode. These weren't working and it's unclear what would be meant to ask for a user's discount code since a user could have several orders with or without discount codes. Added "membership_description" and "membership_confirmation" instead.
63
+ * BUG FIX/ENHANCEMENT: Filtering the password reset message to make sure the link still works in all cases when we convert emails to HTML.
64
+ * BUG FIX/ENHANCEMENT: Added reCAPTCHA v3 and invisible reCAPTCHA support. It is recommended sites using Stripe or Braintree update to the reCAPTCHA v3 option. Read more here: https://www.paidmembershipspro.com/pmpro-update-2-0-5/
65
+ * REFACTOR: Now running the pmpro_billing_preheader hook after the jquery.creditCardValidator script is enqueued in preheader/billing.php to match how we do it in preheader/checkout.php. (Thanks, Rafe Colton)
66
+
67
+ = 2.0.4 - 2019-01-14 =
68
  * BUG FIX: Fixed warning in code added in 2.0.3 that could cause issues at checkout.
69
  * BUG FIX: Setting priority of pmpro_check_admin_capabilities to 5 to ensure it runs before dashboard redirect.
70
  * BUG FIX: Removed duplicate id attribute on the Membership Account page "cancel" action link.
167
  * ENHANCEMENT: Showing the Stripe version we use on the Payment Settings page.
168
  * ENHANCEMENT: Updated Copyright date and GPLv2 link in license.txt.
169
 
 
170
  = 1.9.5.3 - 2018-06-26 =
171
  * BUG FIX: The pmpro_ipnhandler_extend_memberships function actually needed use $user_id instead of $current_user.
172
 
adminpages/admin_header.php CHANGED
@@ -25,7 +25,7 @@
25
  $msgt .= " <a href=\"" . admin_url('admin.php?page=pmpro-membershiplevels&edit=-1') . "\">" . __("Add a membership level to get started.", 'paid-memberships-pro' ) . "</a>";
26
  elseif($pmpro_level_ready && !$pmpro_pages_ready && $view != "pmpro-pagesettings")
27
  $msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-pagesettings') . "\">" . __("Set up the membership pages", 'paid-memberships-pro' ) . "</a>.";
28
- elseif($pmpro_level_ready && $pmpro_pages_ready && !$pmpro_gateway_ready && $view != "pmpro-paymentsettings")
29
  $msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-paymentsettings') . "\">" . __("Set up your SSL certificate and payment gateway", 'paid-memberships-pro' ) . "</a>.";
30
 
31
  if(empty($msgt))
25
  $msgt .= " <a href=\"" . admin_url('admin.php?page=pmpro-membershiplevels&edit=-1') . "\">" . __("Add a membership level to get started.", 'paid-memberships-pro' ) . "</a>";
26
  elseif($pmpro_level_ready && !$pmpro_pages_ready && $view != "pmpro-pagesettings")
27
  $msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-pagesettings') . "\">" . __("Set up the membership pages", 'paid-memberships-pro' ) . "</a>.";
28
+ elseif($pmpro_level_ready && $pmpro_pages_ready && !$pmpro_gateway_ready && $view != "pmpro-paymentsettings" && ! pmpro_onlyFreeLevels())
29
  $msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-paymentsettings') . "\">" . __("Set up your SSL certificate and payment gateway", 'paid-memberships-pro' ) . "</a>.";
30
 
31
  if(empty($msgt))
adminpages/discountcodes.php CHANGED
@@ -585,7 +585,7 @@
585
 
586
  <tr>
587
  <th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'paid-memberships-pro' );?>:</label></th>
588
- <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).attr('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>
589
  </tr>
590
 
591
  <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
@@ -626,7 +626,7 @@
626
 
627
  <tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
628
  <th scope="row" valign="top"><label><?php _e('Custom Trial', 'paid-memberships-pro' );?>:</label></th>
629
- <td><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).attr('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></td>
630
  </tr>
631
 
632
  <tr class="trial_info recurring_info" <?php if (!pmpro_isLevelTrial($level)) echo "style='display:none;'";?>>
@@ -750,7 +750,6 @@
750
  <th><?php _e('Uses', 'paid-memberships-pro' );?></th>
751
  <th><?php _e('Levels', 'paid-memberships-pro' );?></th>
752
  <?php do_action("pmpro_discountcodes_extra_cols_header", $codes);?>
753
- <th></th>
754
  </tr>
755
  </thead>
756
  <tbody>
@@ -759,8 +758,24 @@
759
  foreach($codes as $code) { ?>
760
  <tr<?php if($count++ % 2 == 1) { ?> class="alternate"<?php } ?>>
761
  <td><?php echo $code->id?></td>
762
- <td>
763
- <a href="?page=pmpro-discountcodes&edit=<?php echo $code->id?>"><?php echo $code->code?></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
  </td>
765
  <td>
766
  <?php echo date_i18n(get_option('date_format'), $code->starts)?>
@@ -790,25 +805,17 @@
790
  $levels = $wpdb->get_results($sqlQuery);
791
 
792
  $level_names = array();
793
- foreach($levels as $level)
794
- $level_names[] = "<a target=\"_blank\" href=\"" . pmpro_url("checkout", "?level=" . $level->id . "&discount_code=" . $code->code) . "\">" . $level->name . "</a>";
795
- if($level_names)
796
- echo implode(", ", $level_names);
797
- else
798
- echo "None";
 
 
799
  ?>
800
  </td>
801
  <?php do_action("pmpro_discountcodes_extra_cols_body", $code);?>
802
- <td>
803
- <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' ) ); ?>" class="button-primary"><?php _e( 'edit', 'paid-memberships-pro' ); ?></a>
804
- <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' ) ); ?>" class="button-secondary"><?php _e( 'copy', 'paid-memberships-pro' ); ?></a>
805
- <a title="<?php _e('delete', 'paid-memberships-pro' ); ?>" href="javascript: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);" class="button-secondary"><?php _e('delete', 'paid-memberships-pro' ); ?></a>
806
- <?php if ( (int)$uses > 0 ) { ?>
807
- <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' ) ); ?>" class="button-secondary"><?php _e( 'orders', 'paid-memberships-pro' ); ?></a>
808
- <?php } else { ?>
809
- <a title="<?php _e('no orders', 'paid-memberships-pro' ); ?>" href="#" class="button-secondary button-disabled"><?php _e( 'orders', 'paid-memberships-pro' ); ?></a>
810
- <?php } ?>
811
- </td>
812
  </tr>
813
  <?php
814
  }
585
 
586
  <tr>
587
  <th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'paid-memberships-pro' );?>:</label></th>
588
+ <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>
589
  </tr>
590
 
591
  <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
626
 
627
  <tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
628
  <th scope="row" valign="top"><label><?php _e('Custom Trial', 'paid-memberships-pro' );?>:</label></th>
629
+ <td><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></td>
630
  </tr>
631
 
632
  <tr class="trial_info recurring_info" <?php if (!pmpro_isLevelTrial($level)) echo "style='display:none;'";?>>
750
  <th><?php _e('Uses', 'paid-memberships-pro' );?></th>
751
  <th><?php _e('Levels', 'paid-memberships-pro' );?></th>
752
  <?php do_action("pmpro_discountcodes_extra_cols_header", $codes);?>
 
753
  </tr>
754
  </thead>
755
  <tbody>
758
  foreach($codes as $code) { ?>
759
  <tr<?php if($count++ % 2 == 1) { ?> class="alternate"<?php } ?>>
760
  <td><?php echo $code->id?></td>
761
+ <td class="has-row-actions">
762
+ <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>
763
+ <div class="row-actions">
764
+ <span class="edit">
765
+ <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>
766
+ </span> |
767
+ <span class="copy">
768
+ <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>
769
+ </span> |
770
+ <span class="delete">
771
+ <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>
772
+ </span>
773
+ <?php if ( (int)$uses > 0 ) { ?>
774
+ | <span class="orders">
775
+ <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>
776
+ </span>
777
+ <?php } ?>
778
+ </div>
779
  </td>
780
  <td>
781
  <?php echo date_i18n(get_option('date_format'), $code->starts)?>
805
  $levels = $wpdb->get_results($sqlQuery);
806
 
807
  $level_names = array();
808
+ foreach( $levels as $level ) {
809
+ $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>';
810
+ }
811
+ if( $level_names ) {
812
+ echo implode( ', ', $level_names );
813
+ } else {
814
+ echo 'None';
815
+ }
816
  ?>
817
  </td>
818
  <?php do_action("pmpro_discountcodes_extra_cols_body", $code);?>
 
 
 
 
 
 
 
 
 
 
819
  </tr>
820
  <?php
821
  }
adminpages/functions.php CHANGED
@@ -242,3 +242,69 @@ function pmpro_getClassesForPaymentSettingsField($field, $force = false)
242
  //return space separated string
243
  return implode(" ", $rgateways);
244
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  //return space separated string
243
  return implode(" ", $rgateways);
244
  }
245
+
246
+
247
+ /**
248
+ * Code to handle emailing billable invoices.
249
+ *
250
+ * @since 1.8.6
251
+ */
252
+
253
+ /**
254
+ * Get the gateway-related classes for fields on the payment settings page.
255
+ *
256
+ * @param string $field The name of the field to check.
257
+ * @param bool $force If true, it will rebuild the cached results.
258
+ *
259
+ * @since 1.8
260
+ */
261
+ function pmpro_add_email_order_modal() {
262
+
263
+ // emailing?
264
+ if ( ! empty( $_REQUEST['email'] ) && ! empty( $_REQUEST['order'] ) ) {
265
+ $email = new PMProEmail();
266
+ $user = get_user_by( 'email', sanitize_email( $_REQUEST['email'] ) );
267
+ $order = new MemberOrder( $_REQUEST['order'] );
268
+ if ( $email->sendBillableInvoiceEmail( $user, $order ) ) { ?>
269
+ <div class="notice notice-success is-dismissible">
270
+ <p><?php _e( 'Invoice emailed successfully.', 'paid-memberships-pro' ); ?></p>
271
+ </div>
272
+ <?php } else { ?>
273
+ <div class="notice notice-error is-dismissible">
274
+ <p><?php _e( 'Error emailing invoice.', 'paid-memberships-pro' ); ?></p>
275
+ </div>
276
+ <?php }
277
+ }
278
+
279
+ ?>
280
+ <script>
281
+ // Update fields in email modal.
282
+ jQuery(document).ready(function ($) {
283
+ var order, order_id;
284
+ $('.email_link').click(function () {
285
+ order_id = $(this).data('order');
286
+ $('input[name=order]').val(order_id);
287
+ // Get email address from order ID
288
+ data = {
289
+ action: 'pmpro_get_order_json',
290
+ order_id: order_id
291
+ };
292
+ $.post(ajaxurl, data, function (response) {
293
+ order = JSON.parse(response);
294
+ $('input[name=email]').val(order.Email);
295
+ });
296
+ });
297
+ });
298
+ </script>
299
+ <?php add_thickbox(); ?>
300
+ <div id="email_invoice" style="display:none;">
301
+ <h3><?php _e( 'Email Invoice', 'paid-memberships-pro' ); ?></h3>
302
+ <form method="post" action="">
303
+ <input type="hidden" name="order" value=""/>
304
+ <?php _e( 'Send an invoice for this order to: ', 'paid-memberships-pro' ); ?>
305
+ <input type="text" value="" name="email"/>
306
+ <button class="button button-primary alignright"><?php _e( 'Send Email', 'paid-memberships-pro' ); ?></button>
307
+ </form>
308
+ </div>
309
+ <?php
310
+ }
adminpages/membershiplevels.php CHANGED
@@ -452,7 +452,7 @@
452
  $has_bt_plan = PMProGateway_braintree::checkLevelForPlan( $level->id );
453
  ?>
454
  <p class="pmpro_message <?php if ( ! $has_bt_plan ) {?>pmpro_error<?php } ?>">
455
- <strong><?php _e('Note', 'paid-memberships-pro' );?>:</strong> <?php printf( __('You will need to create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to %s.', 'paid-memberships-pro' ), 'pmpro_' . $level->id ); ?></p>
456
  <?php } ?>
457
  </td>
458
  </tr>
@@ -570,6 +570,32 @@
570
  <?php do_action("pmpro_membership_level_after_other_settings"); ?>
571
 
572
  <h3 class="topborder"><?php _e('Content Settings', 'paid-memberships-pro' );?></h3>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  <table class="form-table">
574
  <tbody>
575
  <tr class="membership_categories">
@@ -717,7 +743,7 @@
717
  <th><?php _e('Billing Details', 'paid-memberships-pro' );?></th>
718
  <th><?php _e('Expiration', 'paid-memberships-pro' );?></th>
719
  <th><?php _e('Allow Signups', 'paid-memberships-pro' );?></th>
720
- <th></th>
721
  </tr>
722
  </thead>
723
  <tbody>
@@ -728,7 +754,14 @@
728
  ?>
729
  <tr class="<?php if($count++ % 2 == 1) { ?>alternate<?php } ?> <?php if(!$level->allow_signups) { ?>pmpro_gray<?php } ?> <?php if(!pmpro_checkLevelForStripeCompatibility($level) || !pmpro_checkLevelForBraintreeCompatibility($level) || !pmpro_checkLevelForPayflowCompatibility($level) || !pmpro_checkLevelForTwoCheckoutCompatibility($level)) { ?>pmpro_error<?php } ?>">
730
  <td><?php echo $level->id?></td>
731
- <td 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></td>
 
 
 
 
 
 
 
732
  <td>
733
  <?php if(pmpro_isLevelFree($level)) { ?>
734
  <?php _e('FREE', 'paid-memberships-pro' );?>
@@ -743,9 +776,8 @@
743
  <?php _e('After', 'paid-memberships-pro' );?> <?php echo $level->expiration_number?> <?php echo sornot($level->expiration_period,$level->expiration_number)?>
744
  <?php } ?>
745
  </td>
746
- <td><?php if($level->allow_signups) { ?><a 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>
747
-
748
- <td><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' ) ); ?>" class="button-primary"><?php _e('edit', 'paid-memberships-pro' ); ?></a>&nbsp;<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' ) ); ?>" class="button-secondary"><?php _e('copy', 'paid-memberships-pro' ); ?></a>&nbsp;<a title="<?php _e('delete', 'paid-memberships-pro' ); ?>" href="javascript: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);" class="button-secondary"><?php _e('delete', 'paid-memberships-pro' ); ?></a></td>
749
  </tr>
750
  <?php
751
  }
452
  $has_bt_plan = PMProGateway_braintree::checkLevelForPlan( $level->id );
453
  ?>
454
  <p class="pmpro_message <?php if ( ! $has_bt_plan ) {?>pmpro_error<?php } ?>">
455
+ <strong><?php _e('Note', 'paid-memberships-pro' );?>:</strong> <?php printf( __('You will need to create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to %s.', 'paid-memberships-pro' ), PMProGateway_braintree::get_plan_id( $level->id ) ); ?></p>
456
  <?php } ?>
457
  </td>
458
  </tr>
570
  <?php do_action("pmpro_membership_level_after_other_settings"); ?>
571
 
572
  <h3 class="topborder"><?php _e('Content Settings', 'paid-memberships-pro' );?></h3>
573
+ <?php
574
+ // Get the Advanced Settings for filtering queries and showing excerpts.
575
+ $filterqueries = pmpro_getOption('filterqueries');
576
+ $showexcerpts = pmpro_getOption("showexcerpts");
577
+
578
+ $allowed_html = array (
579
+ 'a' => array (
580
+ 'href' => array(),
581
+ 'target' => array(),
582
+ 'title' => array(),
583
+ ),
584
+ );
585
+
586
+ if ( $filterqueries == 1 ) {
587
+ // Show a message that posts in these categories are hidden.
588
+ echo '<p>' . sprintf( wp_kses( __( 'Non-members will not see posts in these categories. You can <a href="%s" title="Advanced Settings" target="_blank">update this setting here</a>.', 'paid-memberships-pro' ), $allowed_html ), admin_url( 'admin.php?page=pmpro-advancedsettings' ) ) . '</p>';
589
+ } else {
590
+ if ( $showexcerpts == 1 ) {
591
+ // Show a message that posts in these categories will show title and excerpt.
592
+ echo '<p>' . sprintf( wp_kses( __( 'Non-members will see the title and excerpt for posts in these categories. You can <a href="%s" title="Advanced Settings" target="_blank">update this setting here</a>.', 'paid-memberships-pro' ), $allowed_html ), admin_url( 'admin.php?page=pmpro-advancedsettings' ) ) . '</p>';
593
+ } else {
594
+ // Show a message that posts in these categories will show only the title.
595
+ echo '<p>' . sprintf( wp_kses( __( 'Non-members will see the title only for posts in these categories. You can <a href="%s" title="Advanced Settings" target="_blank">update this setting here</a>.', 'paid-memberships-pro' ), $allowed_html ), admin_url( 'admin.php?page=pmpro-advancedsettings' ) ) . '</p>';
596
+ }
597
+ }
598
+ ?>
599
  <table class="form-table">
600
  <tbody>
601
  <tr class="membership_categories">
743
  <th><?php _e('Billing Details', 'paid-memberships-pro' );?></th>
744
  <th><?php _e('Expiration', 'paid-memberships-pro' );?></th>
745
  <th><?php _e('Allow Signups', 'paid-memberships-pro' );?></th>
746
+ <?php do_action( 'pmpro_membership_levels_table_extra_cols_header', $reordered_levels ); ?>
747
  </tr>
748
  </thead>
749
  <tbody>
754
  ?>
755
  <tr class="<?php if($count++ % 2 == 1) { ?>alternate<?php } ?> <?php if(!$level->allow_signups) { ?>pmpro_gray<?php } ?> <?php if(!pmpro_checkLevelForStripeCompatibility($level) || !pmpro_checkLevelForBraintreeCompatibility($level) || !pmpro_checkLevelForPayflowCompatibility($level) || !pmpro_checkLevelForTwoCheckoutCompatibility($level)) { ?>pmpro_error<?php } ?>">
756
  <td><?php echo $level->id?></td>
757
+ <td class="level_name has-row-actions">
758
+ <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>
759
+ <div class="row-actions">
760
+ <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> |
761
+ <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> |
762
+ <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>
763
+ </div>
764
+ </td>
765
  <td>
766
  <?php if(pmpro_isLevelFree($level)) { ?>
767
  <?php _e('FREE', 'paid-memberships-pro' );?>
776
  <?php _e('After', 'paid-memberships-pro' );?> <?php echo $level->expiration_number?> <?php echo sornot($level->expiration_period,$level->expiration_number)?>
777
  <?php } ?>
778
  </td>
779
+ <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>
780
+ <?php do_action( 'pmpro_membership_levels_table_extra_cols_body', $level ); ?>
 
781
  </tr>
782
  <?php
783
  }
adminpages/memberslist-csv.php CHANGED
@@ -296,10 +296,7 @@
296
  $last_uid = $theusers[($users_found - 1)];
297
 
298
  //increment starting position
299
- if(0 < $iterations)
300
- {
301
- $i_start += $max_users_per_loop;
302
- }
303
 
304
  //escape the % for LIKE comparison with $wpdb
305
  if(!empty($search))
@@ -553,10 +550,18 @@
553
  ini_set('zlib.output_compression', 'Off');
554
  }
555
 
556
- // open and send the file contents to the remote location
557
- $fh = fopen( $filename, 'rb' );
558
- fpassthru($fh);
559
- fclose($fh);
 
 
 
 
 
 
 
 
560
 
561
  // remove the temp file
562
  unlink($filename);
296
  $last_uid = $theusers[($users_found - 1)];
297
 
298
  //increment starting position
299
+ $i_start += $max_users_per_loop;
 
 
 
300
 
301
  //escape the % for LIKE comparison with $wpdb
302
  if(!empty($search))
550
  ini_set('zlib.output_compression', 'Off');
551
  }
552
 
553
+ if( function_exists('fpassthru') )
554
+ {
555
+ // open and send the file contents to the remote location
556
+ $fh = fopen( $filename, 'rb' );
557
+ fpassthru($fh);
558
+ fclose($fh);
559
+ }
560
+ else
561
+ {
562
+ // use readfile() if fpassthru() is disabled (like on Flywheel Hosted)
563
+ readfile($fh);
564
+ }
565
 
566
  // remove the temp file
567
  unlink($filename);
adminpages/orders-csv.php CHANGED
@@ -428,11 +428,11 @@ for ( $ic = 1; $ic <= $iterations; $ic ++ ) {
428
  }
429
 
430
  //increment starting position
431
- if ( $iterations > 1 ) {
432
  $i_start += $max_orders_per_loop;
433
  }
434
  // get the order list we should process
435
- $order_list = array_slice( $order_ids, $i_start, ( $i_start + ( $max_orders_per_loop - 1 ) ) );
436
 
437
  if (PMPRO_BENCHMARK)
438
  {
@@ -441,7 +441,6 @@ for ( $ic = 1; $ic <= $iterations; $ic ++ ) {
441
  }
442
 
443
  foreach ( $order_list as $order_id ) {
444
-
445
  $csvoutput = array();
446
 
447
  $order = new MemberOrder();
@@ -537,11 +536,9 @@ for ( $ic = 1; $ic <= $iterations; $ic ++ ) {
537
  error_log("PMPRO_BENCHMARK - Time processing data: {$sec}.{$usec} seconds");
538
  error_log("PMPRO_BENCHMARK - Peak memory usage: " . number_format($memory_processing_data, false, '.', ',') . " bytes");
539
  }
540
-
541
  $order_list = null;
542
  wp_cache_flush();
543
  }
544
-
545
  pmpro_transmit_order_content( $csv_fh, $filename, $headers );
546
 
547
  function pmpro_enclose( $s ) {
@@ -588,10 +585,18 @@ function pmpro_transmit_order_content( $csv_fh, $filename, $headers = array() )
588
  ini_set( 'zlib.output_compression', 'Off' );
589
  }
590
 
591
- // open and send the file contents to the remote location
592
- $fh = fopen( $filename, 'rb' );
593
- fpassthru( $fh );
594
- fclose( $fh );
 
 
 
 
 
 
 
 
595
 
596
  // remove the temp file
597
  unlink( $filename );
428
  }
429
 
430
  //increment starting position
431
+ if ( $ic > 1 ) {
432
  $i_start += $max_orders_per_loop;
433
  }
434
  // get the order list we should process
435
+ $order_list = array_slice( $order_ids, $i_start, $max_orders_per_loop );
436
 
437
  if (PMPRO_BENCHMARK)
438
  {
441
  }
442
 
443
  foreach ( $order_list as $order_id ) {
 
444
  $csvoutput = array();
445
 
446
  $order = new MemberOrder();
536
  error_log("PMPRO_BENCHMARK - Time processing data: {$sec}.{$usec} seconds");
537
  error_log("PMPRO_BENCHMARK - Peak memory usage: " . number_format($memory_processing_data, false, '.', ',') . " bytes");
538
  }
 
539
  $order_list = null;
540
  wp_cache_flush();
541
  }
 
542
  pmpro_transmit_order_content( $csv_fh, $filename, $headers );
543
 
544
  function pmpro_enclose( $s ) {
585
  ini_set( 'zlib.output_compression', 'Off' );
586
  }
587
 
588
+ if( function_exists('fpassthru') )
589
+ {
590
+ // open and send the file contents to the remote location
591
+ $fh = fopen( $filename, 'rb' );
592
+ fpassthru($fh);
593
+ fclose($fh);
594
+ }
595
+ else
596
+ {
597
+ // use readfile() if fpassthru() is disabled (like on Flywheel Hosted)
598
+ readfile($fh);
599
+ }
600
 
601
  // remove the temp file
602
  unlink( $filename );
adminpages/orders.php CHANGED
@@ -149,24 +149,6 @@ if ( empty( $filter ) || $filter === 'all' ) {
149
  $condition = "o.total = 0";
150
  }
151
 
152
- // emailing?
153
- if ( ! empty( $_REQUEST['email'] ) && ! empty( $_REQUEST['order'] ) ) {
154
- $email = new PMProEmail();
155
- $user = get_user_by( 'email', sanitize_email( $_REQUEST['email'] ) );
156
- $order = new MemberOrder( $_REQUEST['order'] );
157
- if ( $email->sendBillableInvoiceEmail( $user, $order ) ) {
158
- $pmpro_msg = __( 'Invoice emailed successfully.', 'paid-memberships-pro' );
159
- $pmpro_msgt = 'success';
160
- } else {
161
- $pmpro_msg = __( 'Error emailing invoice.', 'paid-memberships-pro' );
162
- $pmpro_msgt = 'error';
163
- }
164
-
165
- // clean up so we stay on the orders list view
166
- unset( $_REQUEST['order'] );
167
- $order = null;
168
- }
169
-
170
  // deleting?
171
  if ( ! empty( $_REQUEST['delete'] ) ) {
172
  $dorder = new MemberOrder( intval( $_REQUEST['delete'] ) );
@@ -373,6 +355,12 @@ if ( ! empty( $_REQUEST['save'] ) ) {
373
  }
374
 
375
  require_once( dirname( __FILE__ ) . '/admin_header.php' );
 
 
 
 
 
 
376
  ?>
377
 
378
  <?php if ( ! empty( $order ) ) { ?>
@@ -380,6 +368,8 @@ require_once( dirname( __FILE__ ) . '/admin_header.php' );
380
  <h2>
381
  <?php if ( ! empty( $order->id ) ) { ?>
382
  <?php _e( 'Order', 'paid-memberships-pro' ); ?> #<?php echo $order->id; ?>: <?php echo $order->code; ?>
 
 
383
  <?php } else { ?>
384
  <?php _e( 'New Order', 'paid-memberships-pro' ); ?>
385
  <?php } ?>
@@ -953,42 +943,7 @@ selected="selected"<?php } ?>><?php echo date_i18n( 'M', strtotime( $i . '/1/' .
953
  </form>
954
 
955
  <?php } else { ?>
956
- <?php
957
- /**
958
- * Code to handle emailing billable invoices.
959
- *
960
- * @since 1.8.6
961
- */
962
- ?>
963
- <script>
964
- // Update fields in email modal.
965
- jQuery(document).ready(function ($) {
966
- var order, order_id;
967
- $('.email_link').click(function () {
968
- order_id = $(this).data('order');
969
- $('input[name=order]').val(order_id);
970
- // Get email address from order ID
971
- data = {
972
- action: 'pmpro_get_order_json',
973
- order_id: order_id
974
- };
975
- $.post(ajaxurl, data, function (response) {
976
- order = JSON.parse(response);
977
- $('input[name=email]').val(order.Email);
978
- });
979
- });
980
- });
981
- </script>
982
- <?php add_thickbox(); ?>
983
- <div id="email_invoice" style="display:none;">
984
- <h3><?php _e( 'Email Invoice', 'paid-memberships-pro' ); ?></h3>
985
- <form method="post" action="">
986
- <input type="hidden" name="order" value=""/>
987
- <?php _e( 'Send an invoice for this order to: ', 'paid-memberships-pro' ); ?>
988
- <input type="text" value="" name="email"/>
989
- <button class="button button-primary alignright"><?php _e( 'Send Email', 'paid-memberships-pro' ); ?></button>
990
- </form>
991
- </div>
992
  <form id="posts-filter" method="get" action="">
993
  <h2>
994
  <?php _e( 'Orders', 'paid-memberships-pro' ); ?>
@@ -1361,11 +1316,6 @@ selected="selected"<?php } ?>><?php echo date_i18n( 'M', strtotime( $i . '/1/' .
1361
  <th><?php _e( 'Status', 'paid-memberships-pro' ); ?></th>
1362
  <th><?php _e( 'Date', 'paid-memberships-pro' ); ?></th>
1363
  <th><?php _e( 'Discount Code', 'paid-memberships-pro' );?></th>
1364
- <th></th>
1365
- <th></th>
1366
- <th></th>
1367
- <th></th>
1368
- <th></th>
1369
  </tr>
1370
  </thead>
1371
  <tbody id="orders" class="list:order orders-list">
@@ -1387,7 +1337,7 @@ class="alternate"<?php } ?>>
1387
  <td>
1388
  <a href="admin.php?page=pmpro-orders&order=<?php echo $order->id; ?>"><?php echo $order->code; ?></a>
1389
  </td>
1390
- <td class="username column-username">
1391
  <?php $order->getUser(); ?>
1392
  <?php if ( ! empty( $order->user ) ) { ?>
1393
  <a href="user-edit.php?user_id=<?php echo $order->user->ID; ?>"><?php echo $order->user->user_login; ?></a>
@@ -1396,23 +1346,40 @@ class="alternate"<?php } ?>>
1396
  <?php } else { ?>
1397
  [<?php _e( 'none', 'paid-memberships-pro' ); ?>]
1398
  <?php } ?>
1399
- <br/>
1400
- <?php
1401
- // Set up the hover actions for this user
1402
- $actions = apply_filters( 'pmpro_orders_user_row_actions', array(), $order->user, $order );
1403
- $action_count = count( $actions );
1404
- $i = 0;
1405
- if ( $action_count ) {
1406
- $out = '<div class="row-actions">';
1407
- foreach ( $actions as $action => $link ) {
1408
- ++ $i;
1409
- ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
1410
- $out .= "<span class='$action'>$link$sep</span>";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1411
  }
1412
- $out .= '</div>';
1413
- echo $out;
1414
- }
1415
- ?>
1416
  </td>
1417
  <?php do_action( 'pmpro_orders_extra_cols_body', $order ); ?>
1418
  <td><?php echo $order->membership_id; ?></td>
@@ -1485,23 +1452,6 @@ class="alternate"<?php } ?>>
1485
  </a>
1486
  <?php } ?>
1487
  </td>
1488
- <td align="center">
1489
- <a href="admin.php?page=pmpro-orders&order=<?php echo $order->id; ?>"><?php _e( 'edit', 'paid-memberships-pro' ); ?></a>
1490
- </td>
1491
- <td align="center">
1492
- <a href="admin.php?page=pmpro-orders&order=-1&copy=<?php echo $order->id; ?>"><?php _e( 'copy', 'paid-memberships-pro' ); ?></a>
1493
- </td>
1494
- <td align="center">
1495
- <a href="javascript:askfirst('<?php echo str_replace( "'", "\'", 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 _e( 'delete', 'paid-memberships-pro' ); ?></a>
1496
- </td>
1497
- <td align="center">
1498
- <a href="admin-ajax.php?action=pmpro_orders_print_view&order=<?php echo $order->id; ?>"
1499
- target="_blank"><?php _e( 'print', 'paid-memberships-pro' ); ?></a>
1500
- </td>
1501
- <td align="center">
1502
- <a href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link"
1503
- data-order="<?php echo $order->id; ?>"><?php _e( 'email', 'paid-memberships-pro' ); ?></a>
1504
- </td>
1505
  </tr>
1506
  <?php
1507
  }
149
  $condition = "o.total = 0";
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  // deleting?
153
  if ( ! empty( $_REQUEST['delete'] ) ) {
154
  $dorder = new MemberOrder( intval( $_REQUEST['delete'] ) );
355
  }
356
 
357
  require_once( dirname( __FILE__ ) . '/admin_header.php' );
358
+
359
+ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
360
+ // Load the email order modal.
361
+ pmpro_add_email_order_modal();
362
+ }
363
+
364
  ?>
365
 
366
  <?php if ( ! empty( $order ) ) { ?>
368
  <h2>
369
  <?php if ( ! empty( $order->id ) ) { ?>
370
  <?php _e( 'Order', 'paid-memberships-pro' ); ?> #<?php echo $order->id; ?>: <?php echo $order->code; ?>
371
+ <a title="<?php _e( 'Print', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'action' => 'pmpro_orders_print_view', 'order' => $order->id ), admin_url('admin-ajax.php' ) ); ?>" class="add-new-h2" target="_blank" ><?php _e( 'Print', 'paid-memberships-pro' ); ?></a>
372
+ <a title="<?php _e( 'Email', 'paid-memberships-pro' ); ?>" href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link add-new-h2" data-order="<?php echo $order->id; ?>"><?php _e( 'Email', 'paid-memberships-pro' ); ?></a>
373
  <?php } else { ?>
374
  <?php _e( 'New Order', 'paid-memberships-pro' ); ?>
375
  <?php } ?>
943
  </form>
944
 
945
  <?php } else { ?>
946
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
947
  <form id="posts-filter" method="get" action="">
948
  <h2>
949
  <?php _e( 'Orders', 'paid-memberships-pro' ); ?>
1316
  <th><?php _e( 'Status', 'paid-memberships-pro' ); ?></th>
1317
  <th><?php _e( 'Date', 'paid-memberships-pro' ); ?></th>
1318
  <th><?php _e( 'Discount Code', 'paid-memberships-pro' );?></th>
 
 
 
 
 
1319
  </tr>
1320
  </thead>
1321
  <tbody id="orders" class="list:order orders-list">
1337
  <td>
1338
  <a href="admin.php?page=pmpro-orders&order=<?php echo $order->id; ?>"><?php echo $order->code; ?></a>
1339
  </td>
1340
+ <td class="username column-username has-row-actions">
1341
  <?php $order->getUser(); ?>
1342
  <?php if ( ! empty( $order->user ) ) { ?>
1343
  <a href="user-edit.php?user_id=<?php echo $order->user->ID; ?>"><?php echo $order->user->user_login; ?></a>
1346
  <?php } else { ?>
1347
  [<?php _e( 'none', 'paid-memberships-pro' ); ?>]
1348
  <?php } ?>
1349
+ <br />
1350
+ <div class="row-actions">
1351
+ <span class="edit">
1352
+ <a title="<?php _e( 'Edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'order' => $order->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Edit', 'paid-memberships-pro' ); ?></a>
1353
+ </span> |
1354
+ <span class="copy">
1355
+ <a title="<?php _e( 'Copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'order' => '-1', 'copy' => $order->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Copy', 'paid-memberships-pro' ); ?></a>
1356
+ </span> |
1357
+ <span class="delete">
1358
+ <a href="javascript:pmpro_askfirst('<?php echo str_replace( "'", "\'", 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 _e( 'Delete', 'paid-memberships-pro' ); ?></a>
1359
+ </span> |
1360
+ <span class="print">
1361
+ <a target="_blank" title="<?php _e( 'Print', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'action' => 'pmpro_orders_print_view', 'order' => $order->id ), admin_url('admin-ajax.php' ) ); ?>"><?php _e( 'Print', 'paid-memberships-pro' ); ?></a>
1362
+ </span> |
1363
+ <span class="email">
1364
+ <a href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link"
1365
+ data-order="<?php echo $order->id; ?>"><?php _e( 'Email', 'paid-memberships-pro' ); ?></a>
1366
+ </span>
1367
+ <?php
1368
+ // Set up the hover actions for this user
1369
+ $actions = apply_filters( 'pmpro_orders_user_row_actions', array(), $order->user, $order );
1370
+ $action_count = count( $actions );
1371
+ $i = 0;
1372
+ if ( $action_count ) {
1373
+ $out = ' | ';
1374
+ foreach ( $actions as $action => $link ) {
1375
+ ++ $i;
1376
+ ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
1377
+ $out .= "<span class='$action'>$link$sep</span>";
1378
+ }
1379
+ echo $out;
1380
  }
1381
+ ?>
1382
+ </div>
 
 
1383
  </td>
1384
  <?php do_action( 'pmpro_orders_extra_cols_body', $order ); ?>
1385
  <td><?php echo $order->membership_id; ?></td>
1452
  </a>
1453
  <?php } ?>
1454
  </td>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1455
  </tr>
1456
  <?php
1457
  }
adminpages/paymentsettings.php CHANGED
@@ -134,6 +134,9 @@
134
  }
135
  ?>
136
  </select>
 
 
 
137
  </td>
138
  </tr>
139
  <tr>
@@ -151,6 +154,20 @@
151
  //hide all gateway options
152
  jQuery('tr.gateway').hide();
153
  jQuery('tr.gateway_'+gateway).show();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  }
155
  pmpro_changeGateway(jQuery('#gateway').val());
156
  </script>
134
  }
135
  ?>
136
  </select>
137
+ <?php if( pmpro_onlyFreeLevels() ) { ?>
138
+ <div id='pmpro-default-gateway-message' style="display:none;"><small><?php echo __( 'This gateway is for membership sites with Free levels or for sites that accept payment offline.', 'paid-memberships-pro' ) . '<br/>' . __( 'It is not connected to a live gateway environment and cannot accept payments.', 'paid-memberships-pro' ); ?></small></div>
139
+ <?php } ?>
140
  </td>
141
  </tr>
142
  <tr>
154
  //hide all gateway options
155
  jQuery('tr.gateway').hide();
156
  jQuery('tr.gateway_'+gateway).show();
157
+
158
+ //hide sub settings and toggle them on based on triggers
159
+ jQuery('tr.pmpro_toggle_target').hide();
160
+ jQuery( 'input[pmpro_toggle_trigger_for]' ).each( function() {
161
+ if ( jQuery( this ).is( ':visible' ) ) {
162
+ pmpro_toggle_elements_by_selector( jQuery( this ).attr( 'pmpro_toggle_trigger_for' ), jQuery( this ).prop( 'checked' ) );
163
+ }
164
+ });
165
+
166
+ if ( jQuery('#gateway').val() === '' ) {
167
+ jQuery('#pmpro-default-gateway-message').show();
168
+ } else {
169
+ jQuery('#pmpro-default-gateway-message').hide();
170
+ }
171
  }
172
  pmpro_changeGateway(jQuery('#gateway').val());
173
  </script>
adminpages/reports/memberships.php CHANGED
@@ -186,7 +186,7 @@ function pmpro_report_memberships_page()
186
  if($period == "daily")
187
  {
188
  $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
189
- $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-32';
190
  $date_function = 'DAY';
191
  }
192
  elseif($period == "monthly")
@@ -221,7 +221,7 @@ function pmpro_report_memberships_page()
221
  $sqlQuery .= "WHERE mu.startdate >= '" . esc_sql( $startdate ) . "' ";
222
 
223
  if ( ! empty( $enddate ) ) {
224
- $sqlQuery .= "AND mu.startdate < '" . esc_sql( $enddate ) . "' ";
225
  }
226
  }
227
 
186
  if($period == "daily")
187
  {
188
  $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
189
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-31';
190
  $date_function = 'DAY';
191
  }
192
  elseif($period == "monthly")
221
  $sqlQuery .= "WHERE mu.startdate >= '" . esc_sql( $startdate ) . "' ";
222
 
223
  if ( ! empty( $enddate ) ) {
224
+ $sqlQuery .= "AND mu.startdate <= '" . esc_sql( $enddate ) . "' ";
225
  }
226
  }
227
 
adminpages/templates/orders-print.php CHANGED
@@ -51,7 +51,7 @@
51
  </tr>
52
  <tr>
53
  <td>
54
- <?php echo __( 'Date:', 'paid-memberships-pro' ) . '&nbsp;' . date_i18n( 'Y-m-d', $order->timestamp ) ?>
55
  </td>
56
  </tr>
57
  </table>
51
  </tr>
52
  <tr>
53
  <td>
54
+ <?php echo __( 'Date:', 'paid-memberships-pro' ) . '&nbsp;' . date_i18n( get_option( 'date_format' ), $order->timestamp ); ?>
55
  </td>
56
  </tr>
57
  </table>
bin/install-wp-tests.sh ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ if [ $# -lt 3 ]; then
4
+ echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
5
+ exit 1
6
+ fi
7
+
8
+ DB_NAME=$1
9
+ DB_USER=$2
10
+ DB_PASS=$3
11
+ DB_HOST=${4-localhost}
12
+ WP_VERSION=${5-latest}
13
+ SKIP_DB_CREATE=${6-false}
14
+
15
+ TMPDIR=${TMPDIR-/tmp}
16
+ TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
17
+ WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
18
+ WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
19
+
20
+ download() {
21
+ if [ `which curl` ]; then
22
+ curl -s "$1" > "$2";
23
+ elif [ `which wget` ]; then
24
+ wget -nv -O "$2" "$1"
25
+ fi
26
+ }
27
+
28
+ if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
29
+ WP_TESTS_TAG="branches/$WP_VERSION"
30
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
31
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
32
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
33
+ WP_TESTS_TAG="tags/${WP_VERSION%??}"
34
+ else
35
+ WP_TESTS_TAG="tags/$WP_VERSION"
36
+ fi
37
+ elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
38
+ WP_TESTS_TAG="trunk"
39
+ else
40
+ # http serves a single offer, whereas https serves multiple. we only want one
41
+ download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
42
+ grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
43
+ LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
44
+ if [[ -z "$LATEST_VERSION" ]]; then
45
+ echo "Latest WordPress version could not be found"
46
+ exit 1
47
+ fi
48
+ WP_TESTS_TAG="tags/$LATEST_VERSION"
49
+ fi
50
+
51
+ set -ex
52
+
53
+ install_wp() {
54
+
55
+ if [ -d $WP_CORE_DIR ]; then
56
+ return;
57
+ fi
58
+
59
+ mkdir -p $WP_CORE_DIR
60
+
61
+ if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
62
+ mkdir -p $TMPDIR/wordpress-nightly
63
+ download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
64
+ unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
65
+ mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
66
+ else
67
+ if [ $WP_VERSION == 'latest' ]; then
68
+ local ARCHIVE_NAME='latest'
69
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
70
+ # https serves multiple offers, whereas http serves single.
71
+ download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
72
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
73
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
74
+ LATEST_VERSION=${WP_VERSION%??}
75
+ else
76
+ # otherwise, scan the releases and get the most up to date minor version of the major release
77
+ local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
78
+ LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
79
+ fi
80
+ if [[ -z "$LATEST_VERSION" ]]; then
81
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
82
+ else
83
+ local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
84
+ fi
85
+ else
86
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
87
+ fi
88
+ download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
89
+ tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
90
+ fi
91
+
92
+ download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
93
+ }
94
+
95
+ install_test_suite() {
96
+ # portable in-place argument for both GNU sed and Mac OSX sed
97
+ if [[ $(uname -s) == 'Darwin' ]]; then
98
+ local ioption='-i .bak'
99
+ else
100
+ local ioption='-i'
101
+ fi
102
+
103
+ # set up testing suite if it doesn't yet exist
104
+ if [ ! -d $WP_TESTS_DIR ]; then
105
+ # set up testing suite
106
+ mkdir -p $WP_TESTS_DIR
107
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
108
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
109
+ fi
110
+
111
+ if [ ! -f wp-tests-config.php ]; then
112
+ download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
113
+ # remove all forward slashes in the end
114
+ WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
115
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
116
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
117
+ sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
118
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
119
+ sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
120
+ fi
121
+
122
+ }
123
+
124
+ install_db() {
125
+
126
+ if [ ${SKIP_DB_CREATE} = "true" ]; then
127
+ return 0
128
+ fi
129
+
130
+ # parse DB_HOST for port or socket references
131
+ local PARTS=(${DB_HOST//\:/ })
132
+ local DB_HOSTNAME=${PARTS[0]};
133
+ local DB_SOCK_OR_PORT=${PARTS[1]};
134
+ local EXTRA=""
135
+
136
+ if ! [ -z $DB_HOSTNAME ] ; then
137
+ if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
138
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
139
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
140
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
141
+ elif ! [ -z $DB_HOSTNAME ] ; then
142
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
143
+ fi
144
+ fi
145
+
146
+ # create database
147
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
148
+ }
149
+
150
+ install_wp
151
+ install_test_suite
152
+ install_db
classes/class.pmproemail.php CHANGED
@@ -114,6 +114,7 @@
114
  $this->email = apply_filters("pmpro_email_recipient", $temail->email, $this);
115
  $this->from = apply_filters("pmpro_email_sender", $temail->from, $this);
116
  $this->fromname = apply_filters("pmpro_email_sender_name", $temail->fromname, $this);
 
117
  $this->subject = apply_filters("pmpro_email_subject", $temail->subject, $this);
118
  $this->template = apply_filters("pmpro_email_template", $temail->template, $this);
119
  $this->body = apply_filters("pmpro_email_body", $temail->body, $this);
@@ -130,6 +131,33 @@
130
  }
131
  }
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  function sendCancelEmail($user = NULL, $old_level_id = NULL)
134
  {
135
  global $wpdb, $current_user;
@@ -953,6 +981,82 @@
953
 
954
  return $this->sendEmail();
955
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
956
 
957
  /**
958
  * Load the text for each default email template.
114
  $this->email = apply_filters("pmpro_email_recipient", $temail->email, $this);
115
  $this->from = apply_filters("pmpro_email_sender", $temail->from, $this);
116
  $this->fromname = apply_filters("pmpro_email_sender_name", $temail->fromname, $this);
117
+ $this->add_from_to_headers();
118
  $this->subject = apply_filters("pmpro_email_subject", $temail->subject, $this);
119
  $this->template = apply_filters("pmpro_email_template", $temail->template, $this);
120
  $this->body = apply_filters("pmpro_email_body", $temail->body, $this);
131
  }
132
  }
133
 
134
+ /**
135
+ * Add the From Name and Email to the headers.
136
+ * @since 2.1
137
+ */
138
+ function add_from_to_headers() {
139
+ // Make sure we have a headers array
140
+ if ( empty( $this->headers ) ) {
141
+ $this->headers = array();
142
+ } elseif ( ! is_array( $this->headers ) ) {
143
+ $this->headers = array( $this->headers );
144
+ }
145
+
146
+ // Remove any previous from header
147
+ foreach( $this->headers as $key => $header ) {
148
+ if( strtolower( substr( $header, 0, 5 ) ) == 'from:' ) {
149
+ unset( $this->headers[$key] );
150
+ }
151
+ }
152
+
153
+ // Add From Email and Name or Just Email
154
+ if( !empty( $this->from ) && !empty( $this->fromname ) ) {
155
+ $this->headers[] = 'From:' . $this->fromname . ' <' . $this->from . '>';
156
+ } elseif( !empty( $this->from ) ) {
157
+ $this->headers[] = 'From:' . $this->from;
158
+ }
159
+ }
160
+
161
  function sendCancelEmail($user = NULL, $old_level_id = NULL)
162
  {
163
  global $wpdb, $current_user;
981
 
982
  return $this->sendEmail();
983
  }
984
+
985
+ function sendPaymentActionRequiredEmail($user = NULL, $order = NULL, $invoice_url = NULL)
986
+ {
987
+ global $wpdb, $current_user;
988
+ if(!$user)
989
+ $user = $current_user;
990
+
991
+ if(!$user || !$order)
992
+ return false;
993
+
994
+ // if an invoice URL wasn't passed in, grab it from the order
995
+ if(empty($invoice_url) && isset($order->invoice_url))
996
+ $invoice_url = $order->invoice_url;
997
+
998
+ // still no invoice URL? bail
999
+ if(empty($invoice_url))
1000
+ return false;
1001
+
1002
+ $this->email = $user->user_email;
1003
+ $this->subject = sprintf(__("Payment action required for your %s membership", 'paid-memberships-pro' ), get_option("blogname"));
1004
+
1005
+ $this->template = "payment_action";
1006
+
1007
+ $this->template = apply_filters("pmpro_email_template", $this->template, $this);
1008
+
1009
+ $this->data = array(
1010
+ "subject" => $this->subject,
1011
+ "name" => $user->display_name,
1012
+ "display_name" => $user->display_name,
1013
+ "user_login" => $user->user_login,
1014
+ "sitename" => get_option("blogname"),
1015
+ "siteemail" => pmpro_getOption("from_email"),
1016
+ "invoice_url" => $invoice_url,
1017
+ );
1018
+
1019
+ return $this->sendEmail();
1020
+ }
1021
+
1022
+ function sendPaymentActionRequiredAdminEmail($user = NULL, $order = NULL, $invoice_url = NULL)
1023
+ {
1024
+ global $wpdb, $current_user;
1025
+ if(!$user)
1026
+ $user = $current_user;
1027
+
1028
+ if(!$user || !$order)
1029
+ return false;
1030
+
1031
+ // if an invoice URL wasn't passed in, grab it from the order
1032
+ if(empty($invoice_url) && isset($order->invoice_url))
1033
+ $invoice_url = $order->invoice_url;
1034
+
1035
+ // still no invoice URL? bail
1036
+ if(empty($invoice_url))
1037
+ return false;
1038
+
1039
+ $this->email = get_bloginfo("admin_email");
1040
+ $this->subject = sprintf(__("Payment action required: membership for %s at %s", 'paid-memberships-pro' ), $user->user_login, get_option("blogname"));
1041
+
1042
+ $this->template = "payment_action_admin";
1043
+
1044
+ $this->template = apply_filters("pmpro_email_template", $this->template, $this);
1045
+
1046
+ $this->data = array(
1047
+ "subject" => $this->subject,
1048
+ "name" => $user->display_name,
1049
+ "display_name" => $user->display_name,
1050
+ "user_login" => $user->user_login,
1051
+ "sitename" => get_option("blogname"),
1052
+ "siteemail" => pmpro_getOption("from_email"),
1053
+ "user_email" => $user->user_email,
1054
+ "invoice_url" => $invoice_url,
1055
+ );
1056
+
1057
+ return $this->sendEmail();
1058
+ }
1059
+
1060
 
1061
  /**
1062
  * Load the text for each default email template.
classes/gateways/class.pmprogateway_authorizenet.php CHANGED
@@ -58,7 +58,7 @@
58
  'use_ssl',
59
  'tax_state',
60
  'tax_rate',
61
- 'accepted_credit_cards'
62
  );
63
 
64
  return $options;
58
  'use_ssl',
59
  'tax_state',
60
  'tax_rate',
61
+ 'accepted_credit_cards',
62
  );
63
 
64
  return $options;
classes/gateways/class.pmprogateway_braintree.php CHANGED
@@ -200,7 +200,9 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
200
  */
201
  static function checkLevelForPlan($level_id) {
202
  $Gateway = new PMProGateway_braintree();
203
- $plan = $Gateway->getPlanByID('pmpro_' . $level_id);
 
 
204
  if(!empty($plan))
205
  return true;
206
  else
@@ -226,7 +228,8 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
226
  $current_gateway = pmpro_getGateway();
227
  if( ( $default_gateway == "braintree" || $current_gateway == "braintree" && empty($_REQUEST['review']))) //$_REQUEST['review'] means the PayPal Express review page
228
  {
229
- add_action( 'pmpro_save_membership_level', array( 'PMProGateway_braintree', 'pmpro_save_level_action') );
 
230
  add_action('pmpro_checkout_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
231
  add_action('pmpro_billing_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
232
  add_filter('pmpro_checkout_order', array('PMProGateway_braintree', 'pmpro_checkout_order'));
@@ -268,7 +271,7 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
268
  'use_ssl',
269
  'tax_state',
270
  'tax_rate',
271
- 'accepted_credit_cards'
272
  );
273
 
274
  return $options;
@@ -351,6 +354,29 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
351
  </tr>
352
  <?php
353
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
355
  /**
356
  * Filtering orders at checkout.
@@ -403,39 +429,6 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
403
  ?>
404
  <input type='hidden' data-encrypted-name='expiration_date' id='credit_card_exp' />
405
  <input type='hidden' name='AccountNumber' id='BraintreeAccountNumber' />
406
- <script type="text/javascript" src="https://js.braintreegateway.com/v1/braintree.js"></script>
407
- <script type="text/javascript">
408
- <!--
409
- /**
410
- * @since 1.9.5 - BUG FIX: substr() on undefined error
411
- */
412
- jQuery(document).ready(function() {
413
- //set up braintree encryption
414
- var braintree = Braintree.create('<?php echo pmpro_getOption("braintree_encryptionkey"); ?>');
415
- braintree.onSubmitEncryptForm('pmpro_form');
416
-
417
- //pass expiration dates in original format
418
- function pmpro_updateBraintreeCardExp()
419
- {
420
- jQuery('#credit_card_exp').val(jQuery('#ExpirationMonth').val() + "/" + jQuery('#ExpirationYear').val());
421
- }
422
- jQuery('#ExpirationMonth, #ExpirationYear').change(function() {
423
- pmpro_updateBraintreeCardExp();
424
- });
425
- pmpro_updateBraintreeCardExp();
426
-
427
- //pass last 4 of credit card
428
- function pmpro_updateBraintreeAccountNumber()
429
- {
430
- jQuery('#BraintreeAccountNumber').val('XXXXXXXXXXXXX' + jQuery('#AccountNumber').val().substr(jQuery('#AccountNumber').val().length - 4));
431
- }
432
- jQuery('#AccountNumber').change(function() {
433
- pmpro_updateBraintreeAccountNumber();
434
- });
435
- pmpro_updateBraintreeAccountNumber();
436
- });
437
- -->
438
- </script>
439
  <?php
440
  }
441
 
@@ -917,9 +910,10 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
917
  //subscribe to the plan
918
  try
919
  {
 
920
  $details = array(
921
  'paymentMethodToken' => $this->customer->creditCards[0]->token,
922
- 'planId' => 'pmpro_' . $order->membership_id,
923
  'price' => $amount
924
  );
925
 
@@ -1058,4 +1052,23 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
1058
  update_user_meta($user_id, 'pmpro_braintree_customerid', $pmpro_braintree_customerid);
1059
  }
1060
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1061
  }
 
200
  */
201
  static function checkLevelForPlan($level_id) {
202
  $Gateway = new PMProGateway_braintree();
203
+
204
+ $plan = $Gateway->getPlanByID( $Gateway->get_plan_id( $level_id ) );
205
+
206
  if(!empty($plan))
207
  return true;
208
  else
228
  $current_gateway = pmpro_getGateway();
229
  if( ( $default_gateway == "braintree" || $current_gateway == "braintree" && empty($_REQUEST['review']))) //$_REQUEST['review'] means the PayPal Express review page
230
  {
231
+ add_action('pmpro_checkout_preheader', array('PMProGateway_braintree', 'pmpro_checkout_preheader'));
232
+ add_action( 'pmpro_save_membership_level', array( 'PMProGateway_braintree', 'pmpro_save_level_action') );
233
  add_action('pmpro_checkout_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
234
  add_action('pmpro_billing_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
235
  add_filter('pmpro_checkout_order', array('PMProGateway_braintree', 'pmpro_checkout_order'));
271
  'use_ssl',
272
  'tax_state',
273
  'tax_rate',
274
+ 'accepted_credit_cards',
275
  );
276
 
277
  return $options;
354
  </tr>
355
  <?php
356
  }
357
+
358
+ /**
359
+ * Code added to checkout preheader.
360
+ *
361
+ * @since 2.1
362
+ */
363
+ static function pmpro_checkout_preheader() {
364
+ global $gateway, $pmpro_level;
365
+
366
+ $default_gateway = pmpro_getOption("gateway");
367
+
368
+ if(($gateway == "braintree" || $default_gateway == "braintree") && !pmpro_isLevelFree($pmpro_level)) {
369
+ wp_enqueue_script("stripe", "https://js.braintreegateway.com/v1/braintree.js", array(), NULL);
370
+ wp_register_script( 'pmpro_braintree',
371
+ plugins_url( 'js/pmpro-braintree.js', PMPRO_BASE_FILE ),
372
+ array( 'jquery' ),
373
+ PMPRO_VERSION );
374
+ wp_localize_script( 'pmpro_braintree', 'pmpro_braintree', array(
375
+ 'encryptionkey' => pmpro_getOption( 'braintree_encryptionkey' )
376
+ ));
377
+ wp_enqueue_script( 'pmpro_braintree' );
378
+ }
379
+ }
380
 
381
  /**
382
  * Filtering orders at checkout.
429
  ?>
430
  <input type='hidden' data-encrypted-name='expiration_date' id='credit_card_exp' />
431
  <input type='hidden' name='AccountNumber' id='BraintreeAccountNumber' />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  <?php
433
  }
434
 
910
  //subscribe to the plan
911
  try
912
  {
913
+
914
  $details = array(
915
  'paymentMethodToken' => $this->customer->creditCards[0]->token,
916
+ 'planId' => $this->get_plan_id( $order->membership_id ),
917
  'price' => $amount
918
  );
919
 
1052
  update_user_meta($user_id, 'pmpro_braintree_customerid', $pmpro_braintree_customerid);
1053
  }
1054
  }
1055
+
1056
+ /**
1057
+ * Gets the Braintree plan ID for a given level ID
1058
+ * @param int $level_id level to get plan ID for
1059
+ * @return string Braintree plan ID
1060
+ */
1061
+ static function get_plan_id( $level_id ) {
1062
+ /**
1063
+ * Filter pmpro_braintree_plan_id
1064
+ *
1065
+ * Used to change the Braintree plan ID for a given level
1066
+ *
1067
+ * @since 2.1.0
1068
+ *
1069
+ * @param string $plan_id for the given level
1070
+ * @param int $level_id the level id to make a plan id for
1071
+ */
1072
+ return apply_filters( 'pmpro_braintree_plan_id', 'pmpro_' . $level_id, $level_id );
1073
  }
1074
+ }
classes/gateways/class.pmprogateway_cybersource.php CHANGED
@@ -1,21 +1,17 @@
1
  <?php
2
  //include pmprogateway
3
  require_once(dirname(__FILE__) . "/class.pmprogateway.php");
4
-
5
  //load classes init method
6
  add_action('init', array('PMProGateway_cybersource', 'init'));
7
-
8
  class PMProGateway_cybersource extends PMProGateway
9
  {
10
  function __construct($gateway = NULL)
11
  {
12
  if(!class_exists("CyberSourceSoapClient"))
13
  require_once(dirname(__FILE__) . "/../../includes/lib/CyberSource/cyber_source_soap_client.php");
14
-
15
  $this->gateway = $gateway;
16
  return $this->gateway;
17
  }
18
-
19
  /**
20
  * Run on WP init
21
  *
@@ -25,12 +21,10 @@
25
  {
26
  //make sure CyberSource is a gateway option
27
  add_filter('pmpro_gateways', array('PMProGateway_cybersource', 'pmpro_gateways'));
28
-
29
  //add fields to payment settings
30
  add_filter('pmpro_payment_options', array('PMProGateway_cybersource', 'pmpro_payment_options'));
31
  add_filter('pmpro_payment_option_fields', array('PMProGateway_cybersource', 'pmpro_payment_option_fields'), 10, 2);
32
  }
33
-
34
  /**
35
  * Make sure this gateway is in the gateways list
36
  *
@@ -40,10 +34,8 @@
40
  {
41
  if(empty($gateways['cybersource']))
42
  $gateways['cybersource'] = __('CyberSource', 'paid-memberships-pro' );
43
-
44
  return $gateways;
45
  }
46
-
47
  /**
48
  * Get a list of payment options that the this gateway needs/supports.
49
  *
@@ -61,12 +53,10 @@
61
  'use_ssl',
62
  'tax_state',
63
  'tax_rate',
64
- 'accepted_credit_cards'
65
  );
66
-
67
  return $options;
68
  }
69
-
70
  /**
71
  * Set payment options for payment settings page.
72
  *
@@ -76,13 +66,10 @@
76
  {
77
  //get stripe options
78
  $cybersource_options = PMProGateway_cybersource::getGatewayOptions();
79
-
80
  //merge with others.
81
  $options = array_merge($cybersource_options, $options);
82
-
83
  return $options;
84
  }
85
-
86
  /**
87
  * Display fields for this gateway's options.
88
  *
@@ -119,7 +106,6 @@
119
  </tr>
120
  <?php
121
  }
122
-
123
  /**
124
  * Process checkout.
125
  *
@@ -141,7 +127,6 @@
141
  $order->TrialBillingFrequency = $order->BillingFrequency;
142
  $order->TrialBillingCycles = 1;
143
  $order->TrialAmount = 0;
144
-
145
  //add a billing cycle to make up for the trial, if applicable
146
  if(!empty($order->TotalBillingCycles))
147
  $order->TotalBillingCycles++;
@@ -151,7 +136,6 @@
151
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
152
  $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
153
  $order->TrialBillingCycles++;
154
-
155
  //add a billing cycle to make up for the trial, if applicable
156
  if($order->TotalBillingCycles)
157
  $order->TotalBillingCycles++;
@@ -161,7 +145,6 @@
161
  //add a period to the start date to account for the initial payment
162
  $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
163
  }
164
-
165
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
166
  return $this->subscribe($order);
167
  }
@@ -188,7 +171,6 @@
188
  $order->TrialBillingFrequency = $order->BillingFrequency;
189
  $order->TrialBillingCycles = 1;
190
  $order->TrialAmount = 0;
191
-
192
  //add a billing cycle to make up for the trial, if applicable
193
  if(!empty($order->TotalBillingCycles))
194
  $order->TotalBillingCycles++;
@@ -198,7 +180,6 @@
198
  //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
199
  $order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
200
  $order->TrialBillingCycles++;
201
-
202
  //add a billing cycle to make up for the trial, if applicable
203
  if(!empty($order->TotalBillingCycles))
204
  $order->TotalBillingCycles++;
@@ -208,7 +189,6 @@
208
  //add a period to the start date to account for the initial payment
209
  $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
210
  }
211
-
212
  $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
213
  if($this->subscribe($order))
214
  {
@@ -225,10 +205,8 @@
225
  {
226
  if(!$order->error)
227
  $order->error = __("Unknown error: Payment failed.", 'paid-memberships-pro' );
228
-
229
  $order->error .= " " . __("A partial payment was made that we could not void. Please contact the site owner immediately to correct this.", 'paid-memberships-pro' );
230
  }
231
-
232
  return false;
233
  }
234
  }
@@ -243,12 +221,10 @@
243
  {
244
  if(empty($order->error))
245
  $order->error = __("Unknown error: Payment failed.", 'paid-memberships-pro' );
246
-
247
  return false;
248
  }
249
  }
250
  }
251
-
252
  function getCardType($name)
253
  {
254
  $card_types = array(
@@ -263,13 +239,11 @@
263
  'Carte Blanche' => '006',
264
  'JCB' => '007'
265
  );
266
-
267
  if(isset($card_types[$name]))
268
  return $card_types[$name];
269
  else
270
  return false;
271
  }
272
-
273
  function getWSDL($order)
274
  {
275
  //which gateway environment?
@@ -277,61 +251,45 @@
277
  $gateway_environment = pmpro_getOption("gateway_environment");
278
  else
279
  $gateway_environment = $order->gateway_environment;
280
-
281
  //which host?
282
  if($gateway_environment == "live")
283
  $host = "ics2ws.ic3.com";
284
  else
285
  $host = "ics2wstest.ic3.com";
286
-
287
  //path
288
- $path = "/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.90.wsdl";
289
-
290
  //build url
291
  $wsdl_url = "https://" . $host . $path;
292
-
293
  //filter
294
  $wsdl_url = apply_filters("pmpro_cybersource_wsdl_url", $wsdl_url, $gateway_environment);
295
-
296
  return $wsdl_url;
297
  }
298
-
299
  function authorize(&$order)
300
  {
301
  global $pmpro_currency;
302
-
303
  if(empty($order->code))
304
  $order->code = $order->getRandomCode();
305
-
306
  $wsdl_url = $this->getWSDL($order);
307
-
308
  //what amount to authorize? just $1 to test
309
  $amount = "1.00";
310
-
311
  //combine address
312
  $address = $order->Address1;
313
  if(!empty($order->Address2))
314
  $address .= "\n" . $order->Address2;
315
-
316
  //customer stuff
317
  $customer_email = $order->Email;
318
  $customer_phone = $order->billing->phone;
319
-
320
  if(!isset($order->membership_level->name))
321
  $order->membership_level->name = "";
322
-
323
  //to store our request
324
  $request = new stdClass();
325
-
326
  //which service?
327
  $ccAuthService = new stdClass();
328
  $ccAuthService->run = "true";
329
  $request->ccAuthService = $ccAuthService;
330
-
331
  //merchant id and order code
332
  $request->merchantID = pmpro_getOption("cybersource_merchantid");
333
  $request->merchantReferenceCode = $order->code;
334
-
335
  //bill to
336
  $billTo = new stdClass();
337
  $billTo->firstName = $order->FirstName;
@@ -344,7 +302,6 @@
344
  $billTo->email = $order->Email;
345
  $billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
346
  $request->billTo = $billTo;
347
-
348
  //card
349
  $card = new stdClass();
350
  $card->cardType = $this->getCardType($order->cardtype);
@@ -354,6 +311,13 @@
354
  $card->cvNumber = $order->CVV2;
355
  $request->card = $card;
356
 
 
 
 
 
 
 
 
357
  //currency
358
  $purchaseTotals = new stdClass();
359
  $purchaseTotals->currency = $pmpro_currency;
@@ -368,8 +332,24 @@
368
  $item0->id = $order->membership_id;
369
  $request->item = array($item0);
370
 
371
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
372
- $reply = $soapClient->runTransaction($request);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
  if($reply->reasonCode == "100")
375
  {
@@ -382,36 +362,46 @@
382
  {
383
  //error
384
  $order->errorcode = $reply->reasonCode;
385
- $order->error = $this->getErrorFromCode($reply->reasonCode);
386
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
387
  return false;
388
  }
389
  }
390
-
391
  function void(&$order)
392
  {
393
  //need a transaction id
394
  if(empty($order->payment_transaction_id))
395
  return false;
396
-
397
  //get wsdl
398
  $wsdl_url = $this->getWSDL($order);
399
-
400
  //to store our request
401
  $request = new stdClass();
402
-
403
  //which service?
404
  $voidService = new stdClass();
405
  $voidService->run = "true";
406
  $voidService->voidRequestID = $order->payment_transaction_id;
407
  $request->voidService = $voidService;
408
-
409
  //merchant id and order code
410
  $request->merchantID = pmpro_getOption("cybersource_merchantid");
411
  $request->merchantReferenceCode = $order->code;
412
 
413
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
414
- $reply = $soapClient->runTransaction($request);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
 
416
  if($reply->reasonCode == "100")
417
  {
@@ -424,59 +414,46 @@
424
  {
425
  //error
426
  $order->errorcode = $reply->reasonCode;
427
- $order->error = $this->getErrorFromCode($reply->reasonCode);
428
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
429
  return false;
430
  }
431
  }
432
-
433
  function charge(&$order)
434
  {
435
  global $pmpro_currency;
436
-
437
  //get a code
438
  if(empty($order->code))
439
  $order->code = $order->getRandomCode();
440
-
441
  //get wsdl
442
  $wsdl_url = $this->getWSDL($order);
443
-
444
  //what amount to charge?
445
  $amount = $order->InitialPayment;
446
-
447
  //tax
448
  $order->subtotal = $amount;
449
  $tax = $order->getTax(true);
450
  $amount = pmpro_round_price((float)$order->subtotal + (float)$tax);
451
-
452
  //combine address
453
  $address = $order->Address1;
454
  if(!empty($order->Address2))
455
  $address .= "\n" . $order->Address2;
456
-
457
  //customer stuff
458
  $customer_email = $order->Email;
459
  $customer_phone = $order->billing->phone;
460
-
461
  if(!isset($order->membership_level->name))
462
  $order->membership_level->name = "";
463
-
464
  //to store our request
465
  $request = new stdClass();
466
-
467
  //authorize and capture
468
  $ccAuthService = new stdClass();
469
  $ccAuthService->run = "true";
470
  $request->ccAuthService = $ccAuthService;
471
-
472
  $ccCaptureService = new stdClass();
473
  $ccCaptureService->run = "true";
474
  $request->ccCaptureService = $ccCaptureService;
475
-
476
  //merchant id and order code
477
  $request->merchantID = pmpro_getOption("cybersource_merchantid");
478
  $request->merchantReferenceCode = $order->code;
479
-
480